Skip to content

Commit b28308d

Browse files
authored
Merge pull request #74428 from gottesmm/release/6.0-rdar127675288
[6.0] Fix function subtyping rules for sending
2 parents b26b45b + aae5b6f commit b28308d

18 files changed

+546
-14
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7952,6 +7952,19 @@ ERROR(sending_only_on_parameters_and_results, none,
79527952
"'sending' may only be used on parameters and results", ())
79537953
ERROR(sending_cannot_be_applied_to_tuple_elt, none,
79547954
"'sending' cannot be applied to tuple elements", ())
7955+
ERROR(sending_function_wrong_sending,none,
7956+
"converting a value of type %0 to type %1 risks causing data races",
7957+
(Type, Type))
7958+
NOTE(sending_function_param_with_sending_param_note, none,
7959+
"converting a function typed value with a sending parameter to one "
7960+
"without risks allowing actor-isolated values to escape their isolation "
7961+
"domain as an argument to an invocation of value",
7962+
())
7963+
NOTE(sending_function_result_with_sending_param_note, none,
7964+
"converting a function typed value without a sending result as one with "
7965+
"risks allowing actor-isolated values to escape their "
7966+
"isolation domain through a result of an invocation of value",
7967+
())
79557968

79567969
#define UNDEFINE_DIAGNOSTIC_MACROS
79577970
#include "DefineDiagnosticMacros.h"

include/swift/AST/Types.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3237,6 +3237,9 @@ class AnyFunctionType : public TypeBase {
32373237
/// Whether the parameter is 'isolated'.
32383238
bool isIsolated() const { return Flags.isIsolated(); }
32393239

3240+
/// Whether or not the parameter is 'sending'.
3241+
bool isSending() const { return Flags.isSending(); }
3242+
32403243
/// Whether the parameter is 'isCompileTimeConst'.
32413244
bool isCompileTimeConst() const { return Flags.isCompileTimeConst(); }
32423245

include/swift/Sema/CSFix.h

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,17 @@ enum class FixKind : uint8_t {
471471
/// Ignore situations when key path subscript index gets passed an invalid
472472
/// type as an argument (something that is not a key path).
473473
IgnoreKeyPathSubscriptIndexMismatch,
474+
475+
/// Ignore the following situations:
476+
///
477+
/// 1. Where we have a function that expects a function typed parameter
478+
/// without a sendable parameter but is passed a function type with a sending
479+
/// parameter.
480+
///
481+
/// 2. Where we have a function that expects a function typed parameter with a
482+
/// sending result, but is passed a function typeed parameter without a
483+
/// sending result.
484+
AllowSendingMismatch,
474485
};
475486

476487
class ConstraintFix {
@@ -2619,6 +2630,49 @@ class TreatEphemeralAsNonEphemeral final : public AllowArgumentMismatch {
26192630
}
26202631
};
26212632

2633+
/// Error if a user passes let f: (sending T) -> () as a (T) -> ().
2634+
///
2635+
/// This prevents data races since f assumes its parameter if the parameter is
2636+
/// non-Sendable is safe to transfer onto other situations. The caller though
2637+
/// that this is being sent to does not enforce that invariants within its body.
2638+
class AllowSendingMismatch final : public ContextualMismatch {
2639+
public:
2640+
enum class Kind {
2641+
Parameter,
2642+
Result,
2643+
};
2644+
2645+
private:
2646+
Kind kind;
2647+
2648+
AllowSendingMismatch(ConstraintSystem &cs, Type argType, Type paramType,
2649+
ConstraintLocator *locator, Kind kind,
2650+
FixBehavior fixBehavior)
2651+
: ContextualMismatch(cs, FixKind::AllowSendingMismatch, argType,
2652+
paramType, locator, fixBehavior),
2653+
kind(kind) {}
2654+
2655+
public:
2656+
std::string getName() const override {
2657+
return "treat a function argument with sending parameter as a function "
2658+
"argument without sending parameters";
2659+
}
2660+
2661+
bool diagnose(const Solution &solution, bool asNote = false) const override;
2662+
2663+
bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override {
2664+
return diagnose(*commonFixes.front().first);
2665+
}
2666+
2667+
static AllowSendingMismatch *create(ConstraintSystem &cs,
2668+
ConstraintLocator *locator, Type srcType,
2669+
Type dstType, Kind kind);
2670+
2671+
static bool classof(const ConstraintFix *fix) {
2672+
return fix->getKind() == FixKind::AllowSendingMismatch;
2673+
}
2674+
};
2675+
26222676
class SpecifyBaseTypeForContextualMember final : public ConstraintFix {
26232677
DeclNameRef MemberName;
26242678

lib/Parse/ParseType.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1555,6 +1555,8 @@ bool Parser::canParseType() {
15551555
consumeToken();
15561556
} else if (Tok.isContextualKeyword("each")) {
15571557
consumeToken();
1558+
} else if (Tok.isContextualKeyword("sending")) {
1559+
consumeToken();
15581560
}
15591561

15601562
switch (Tok.getKind()) {

lib/Sema/AssociatedTypeInference.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2425,7 +2425,9 @@ AssociatedTypeInference::computeFailureTypeWitness(
24252425
// it.
24262426
for (const auto &witness : valueWitnesses) {
24272427
if (isAsyncIteratorProtocolNext(witness.first)) {
2428-
if (auto witnessFunc = dyn_cast<AbstractFunctionDecl>(witness.second)) {
2428+
// We use a dyn_cast_or_null since we can get a nullptr here if we fail to
2429+
// match a witness. In such a case, we should just fail here.
2430+
if (auto witnessFunc = dyn_cast_or_null<AbstractFunctionDecl>(witness.second)) {
24292431
auto thrownError = witnessFunc->getEffectiveThrownErrorType();
24302432

24312433
// If it doesn't throw, Failure == Never.

lib/Sema/CSDiagnostics.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7918,6 +7918,24 @@ bool NonEphemeralConversionFailure::diagnoseAsError() {
79187918
return true;
79197919
}
79207920

7921+
bool SendingOnFunctionParameterMismatchFail::diagnoseAsError() {
7922+
emitDiagnosticAt(getLoc(), diag::sending_function_wrong_sending,
7923+
getFromType(), getToType())
7924+
.warnUntilSwiftVersion(6);
7925+
emitDiagnosticAt(getLoc(),
7926+
diag::sending_function_param_with_sending_param_note);
7927+
return true;
7928+
}
7929+
7930+
bool SendingOnFunctionResultMismatchFailure::diagnoseAsError() {
7931+
emitDiagnosticAt(getLoc(), diag::sending_function_wrong_sending,
7932+
getFromType(), getToType())
7933+
.warnUntilSwiftVersion(6);
7934+
emitDiagnosticAt(getLoc(),
7935+
diag::sending_function_result_with_sending_param_note);
7936+
return true;
7937+
}
7938+
79217939
bool AssignmentTypeMismatchFailure::diagnoseMissingConformance() const {
79227940
auto srcType = getFromType();
79237941
auto dstType = getToType()->lookThroughAllOptionalTypes();

lib/Sema/CSDiagnostics.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2291,6 +2291,28 @@ class NonEphemeralConversionFailure final : public ArgumentMismatchFailure {
22912291
void emitSuggestionNotes() const;
22922292
};
22932293

2294+
class SendingOnFunctionParameterMismatchFail final : public ContextualFailure {
2295+
public:
2296+
SendingOnFunctionParameterMismatchFail(const Solution &solution, Type srcType,
2297+
Type dstType,
2298+
ConstraintLocator *locator,
2299+
FixBehavior fixBehavior)
2300+
: ContextualFailure(solution, srcType, dstType, locator, fixBehavior) {}
2301+
2302+
bool diagnoseAsError() override;
2303+
};
2304+
2305+
class SendingOnFunctionResultMismatchFailure final : public ContextualFailure {
2306+
public:
2307+
SendingOnFunctionResultMismatchFailure(const Solution &solution, Type srcType,
2308+
Type dstType,
2309+
ConstraintLocator *locator,
2310+
FixBehavior fixBehavior)
2311+
: ContextualFailure(solution, srcType, dstType, locator, fixBehavior) {}
2312+
2313+
bool diagnoseAsError() override;
2314+
};
2315+
22942316
class AssignmentTypeMismatchFailure final : public ContextualFailure {
22952317
public:
22962318
AssignmentTypeMismatchFailure(const Solution &solution,

lib/Sema/CSFix.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1845,6 +1845,34 @@ std::string TreatEphemeralAsNonEphemeral::getName() const {
18451845
return name;
18461846
}
18471847

1848+
bool AllowSendingMismatch::diagnose(const Solution &solution,
1849+
bool asNote) const {
1850+
switch (kind) {
1851+
case Kind::Parameter: {
1852+
SendingOnFunctionParameterMismatchFail failure(
1853+
solution, getFromType(), getToType(), getLocator(), fixBehavior);
1854+
return failure.diagnose(asNote);
1855+
}
1856+
case Kind::Result: {
1857+
SendingOnFunctionResultMismatchFailure failure(
1858+
solution, getFromType(), getToType(), getLocator(), fixBehavior);
1859+
return failure.diagnose(asNote);
1860+
}
1861+
}
1862+
llvm_unreachable("Covered switch isn't covered?!");
1863+
}
1864+
1865+
AllowSendingMismatch *AllowSendingMismatch::create(ConstraintSystem &cs,
1866+
ConstraintLocator *locator,
1867+
Type srcType, Type dstType,
1868+
Kind kind) {
1869+
auto fixBehavior = cs.getASTContext().LangOpts.isSwiftVersionAtLeast(6)
1870+
? FixBehavior::Error
1871+
: FixBehavior::DowngradeToWarning;
1872+
return new (cs.getAllocator())
1873+
AllowSendingMismatch(cs, srcType, dstType, locator, kind, fixBehavior);
1874+
}
1875+
18481876
bool SpecifyBaseTypeForContextualMember::diagnose(const Solution &solution,
18491877
bool asNote) const {
18501878
MissingContextualBaseInMemberRefFailure failure(solution, MemberName,

lib/Sema/CSGen.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1636,10 +1636,9 @@ namespace {
16361636
}
16371637

16381638
Type
1639-
resolveTypeReferenceInExpression(TypeRepr *repr, TypeResolverContext resCtx,
1639+
resolveTypeReferenceInExpression(TypeRepr *repr,
1640+
TypeResolutionOptions options,
16401641
const ConstraintLocatorBuilder &locator) {
1641-
TypeResolutionOptions options(resCtx);
1642-
16431642
// Introduce type variables for unbound generics.
16441643
const auto genericOpener = OpenUnboundGenericType(CS, locator);
16451644
const auto placeholderHandler = HandlePlaceholderType(CS, locator);
@@ -2528,9 +2527,11 @@ namespace {
25282527
return declaredTy;
25292528
}
25302529

2530+
auto options =
2531+
TypeResolutionOptions(TypeResolverContext::InExpression);
2532+
options.setContext(TypeResolverContext::ClosureExpr);
25312533
const auto resolvedTy = resolveTypeReferenceInExpression(
2532-
closure->getExplicitResultTypeRepr(),
2533-
TypeResolverContext::InExpression, resultLocator);
2534+
closure->getExplicitResultTypeRepr(), options, resultLocator);
25342535
if (resolvedTy)
25352536
return resolvedTy;
25362537
}

lib/Sema/CSSimplify.cpp

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3236,6 +3236,16 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2,
32363236
return getTypeMatchFailure(locator);
32373237
}
32383238

3239+
// () -> sending T can be a subtype of () -> T... but not vis-a-versa.
3240+
if (func1->hasSendingResult() != func2->hasSendingResult() &&
3241+
(!func1->hasSendingResult() || kind < ConstraintKind::Subtype)) {
3242+
auto *fix = AllowSendingMismatch::create(
3243+
*this, getConstraintLocator(locator), func1, func2,
3244+
AllowSendingMismatch::Kind::Result);
3245+
if (recordFix(fix))
3246+
return getTypeMatchFailure(locator);
3247+
}
3248+
32393249
if (!matchFunctionIsolations(func1, func2, kind, flags, locator))
32403250
return getTypeMatchFailure(locator);
32413251

@@ -3666,6 +3676,17 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2,
36663676
return getTypeMatchFailure(argumentLocator);
36673677
}
36683678

3679+
// Do not allow for functions that expect a sending parameter to match
3680+
// with a function that expects a non-sending parameter.
3681+
if (func1Param.getParameterFlags().isSending() &&
3682+
!func2Param.getParameterFlags().isSending()) {
3683+
auto *fix = AllowSendingMismatch::create(
3684+
*this, getConstraintLocator(argumentLocator), func1, func2,
3685+
AllowSendingMismatch::Kind::Parameter);
3686+
if (recordFix(fix))
3687+
return getTypeMatchFailure(argumentLocator);
3688+
}
3689+
36693690
// FIXME: We should check value ownership too, but it's not completely
36703691
// trivial because of inout-to-pointer conversions.
36713692

@@ -11770,10 +11791,10 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar,
1177011791
if (contextualParam->isIsolated() && !flags.isIsolated() && paramDecl)
1177111792
isolatedParams.insert(paramDecl);
1177211793

11773-
param =
11774-
param.withFlags(flags.withInOut(contextualParam->isInOut())
11775-
.withVariadic(contextualParam->isVariadic())
11776-
.withIsolated(contextualParam->isIsolated()));
11794+
param = param.withFlags(flags.withInOut(contextualParam->isInOut())
11795+
.withVariadic(contextualParam->isVariadic())
11796+
.withIsolated(contextualParam->isIsolated())
11797+
.withSending(contextualParam->isSending()));
1177711798
}
1177811799
}
1177911800

@@ -11900,6 +11921,12 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar,
1190011921
closureExtInfo = closureExtInfo.withSendable();
1190111922
}
1190211923

11924+
// Propagate sending result from the contextual type to the closure.
11925+
if (auto contextualFnType = contextualType->getAs<FunctionType>()) {
11926+
if (contextualFnType->hasExtInfo() && contextualFnType->hasSendingResult())
11927+
closureExtInfo = closureExtInfo.withSendingResult();
11928+
}
11929+
1190311930
// Isolated parameters override any other kind of isolation we might infer.
1190411931
if (hasIsolatedParam) {
1190511932
closureExtInfo = closureExtInfo.withIsolation(
@@ -15098,6 +15125,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
1509815125
}
1509915126
}
1510015127

15128+
case FixKind::AllowSendingMismatch:
1510115129
case FixKind::InsertCall:
1510215130
case FixKind::RemoveReturn:
1510315131
case FixKind::RemoveAddressOf:

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,15 @@ RequirementMatch swift::matchWitness(
743743
reqTypeIsIUO != witnessTypeIsIUO)
744744
return RequirementMatch(witness, MatchKind::TypeConflict, witnessType);
745745

746+
// If our requirement says that it has a sending result, then our witness
747+
// must also have a sending result since otherwise, in generic contexts,
748+
// we would be returning non-disconnected values as disconnected.
749+
if (dc->getASTContext().LangOpts.isSwiftVersionAtLeast(6)) {
750+
if (reqFnType->hasExtInfo() && reqFnType->hasSendingResult() &&
751+
(!witnessFnType->hasExtInfo() || !witnessFnType->hasSendingResult()))
752+
return RequirementMatch(witness, MatchKind::TypeConflict, witnessType);
753+
}
754+
746755
if (auto result = matchTypes(std::get<0>(types), std::get<1>(types))) {
747756
return std::move(result.value());
748757
}
@@ -775,6 +784,14 @@ RequirementMatch swift::matchWitness(
775784
if (reqParams[i].isInOut() != witnessParams[i].isInOut())
776785
return RequirementMatch(witness, MatchKind::TypeConflict, witnessType);
777786

787+
// If we have a requirement without sending and our witness expects a
788+
// sending parameter, error.
789+
if (dc->getASTContext().isSwiftVersionAtLeast(6)) {
790+
if (!reqParams[i].getParameterFlags().isSending() &&
791+
witnessParams[i].getParameterFlags().isSending())
792+
return RequirementMatch(witness, MatchKind::TypeConflict, witnessType);
793+
}
794+
778795
auto reqParamDecl = reqParamList->get(i);
779796
auto witnessParamDecl = witnessParamList->get(i);
780797

lib/Sema/TypeCheckType.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4971,7 +4971,8 @@ TypeResolver::resolveSendingTypeRepr(SendingTypeRepr *repr,
49714971
return ErrorType::get(getASTContext());
49724972
}
49734973

4974-
if (!options.is(TypeResolverContext::FunctionResult) &&
4974+
if (!options.is(TypeResolverContext::ClosureExpr) &&
4975+
!options.is(TypeResolverContext::FunctionResult) &&
49754976
(!options.is(TypeResolverContext::FunctionInput) ||
49764977
options.hasBase(TypeResolverContext::EnumElementDecl))) {
49774978
diagnoseInvalid(repr, repr->getSpecifierLoc(),

lib/Sema/TypeCheckType.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ enum class TypeResolverContext : uint8_t {
118118
/// Whether we are checking the parameter list of a subscript.
119119
SubscriptDecl,
120120

121-
/// Whether we are checking the parameter list of a closure.
121+
/// Whether we are checking the parameter list or result of a closure.
122122
ClosureExpr,
123123

124124
/// Whether we are in the input type of a function, or under one level of

0 commit comments

Comments
 (0)