Skip to content

Commit f995418

Browse files
authored
Merge pull request #74129 from gottesmm/pr-d17a3faab1ceab8b831d7649c1005be9c49d771c
[region-isolation] Implement function sub typing rules
2 parents 6dc8686 + c8d9e18 commit f995418

18 files changed

+546
-14
lines changed

include/swift/AST/DiagnosticsSema.def

+13
Original file line numberDiff line numberDiff line change
@@ -7997,6 +7997,19 @@ ERROR(sending_only_on_parameters_and_results, none,
79977997
"'sending' may only be used on parameters and results", ())
79987998
ERROR(sending_cannot_be_applied_to_tuple_elt, none,
79997999
"'sending' cannot be applied to tuple elements", ())
8000+
ERROR(sending_function_wrong_sending,none,
8001+
"converting a value of type %0 to type %1 risks causing data races",
8002+
(Type, Type))
8003+
NOTE(sending_function_param_with_sending_param_note, none,
8004+
"converting a function typed value with a sending parameter to one "
8005+
"without risks allowing actor-isolated values to escape their isolation "
8006+
"domain as an argument to an invocation of value",
8007+
())
8008+
NOTE(sending_function_result_with_sending_param_note, none,
8009+
"converting a function typed value without a sending result as one with "
8010+
"risks allowing actor-isolated values to escape their "
8011+
"isolation domain through a result of an invocation of value",
8012+
())
80008013

80018014
#define UNDEFINE_DIAGNOSTIC_MACROS
80028015
#include "DefineDiagnosticMacros.h"

include/swift/AST/Types.h

+3
Original file line numberDiff line numberDiff line change
@@ -3232,6 +3232,9 @@ class AnyFunctionType : public TypeBase {
32323232
/// Whether the parameter is 'isolated'.
32333233
bool isIsolated() const { return Flags.isIsolated(); }
32343234

3235+
/// Whether or not the parameter is 'sending'.
3236+
bool isSending() const { return Flags.isSending(); }
3237+
32353238
/// Whether the parameter is 'isCompileTimeConst'.
32363239
bool isCompileTimeConst() const { return Flags.isCompileTimeConst(); }
32373240

include/swift/Sema/CSFix.h

+54
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

+2
Original file line numberDiff line numberDiff line change
@@ -1551,6 +1551,8 @@ bool Parser::canParseType() {
15511551
consumeToken();
15521552
} else if (Tok.isContextualKeyword("each")) {
15531553
consumeToken();
1554+
} else if (Tok.isContextualKeyword("sending")) {
1555+
consumeToken();
15541556
}
15551557

15561558
switch (Tok.getKind()) {

lib/Sema/AssociatedTypeInference.cpp

+3-1
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

+18
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

+22
Original file line numberDiff line numberDiff line change
@@ -2292,6 +2292,28 @@ class NonEphemeralConversionFailure final : public ArgumentMismatchFailure {
22922292
void emitSuggestionNotes() const;
22932293
};
22942294

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

lib/Sema/CSFix.cpp

+28
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

+6-5
Original file line numberDiff line numberDiff line change
@@ -1649,10 +1649,9 @@ namespace {
16491649
}
16501650

16511651
Type
1652-
resolveTypeReferenceInExpression(TypeRepr *repr, TypeResolverContext resCtx,
1652+
resolveTypeReferenceInExpression(TypeRepr *repr,
1653+
TypeResolutionOptions options,
16531654
const ConstraintLocatorBuilder &locator) {
1654-
TypeResolutionOptions options(resCtx);
1655-
16561655
// Introduce type variables for unbound generics.
16571656
const auto genericOpener = OpenUnboundGenericType(CS, locator);
16581657
const auto placeholderHandler = HandlePlaceholderType(CS, locator);
@@ -2541,9 +2540,11 @@ namespace {
25412540
return declaredTy;
25422541
}
25432542

2543+
auto options =
2544+
TypeResolutionOptions(TypeResolverContext::InExpression);
2545+
options.setContext(TypeResolverContext::ClosureExpr);
25442546
const auto resolvedTy = resolveTypeReferenceInExpression(
2545-
closure->getExplicitResultTypeRepr(),
2546-
TypeResolverContext::InExpression, resultLocator);
2547+
closure->getExplicitResultTypeRepr(), options, resultLocator);
25472548
if (resolvedTy)
25482549
return resolvedTy;
25492550
}

lib/Sema/CSSimplify.cpp

+32-4
Original file line numberDiff line numberDiff line change
@@ -3235,6 +3235,16 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2,
32353235
return getTypeMatchFailure(locator);
32363236
}
32373237

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

@@ -3665,6 +3675,17 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2,
36653675
return getTypeMatchFailure(argumentLocator);
36663676
}
36673677

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

@@ -11785,10 +11806,10 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar,
1178511806
if (contextualParam->isIsolated() && !flags.isIsolated() && paramDecl)
1178611807
isolatedParams.insert(paramDecl);
1178711808

11788-
param =
11789-
param.withFlags(flags.withInOut(contextualParam->isInOut())
11790-
.withVariadic(contextualParam->isVariadic())
11791-
.withIsolated(contextualParam->isIsolated()));
11809+
param = param.withFlags(flags.withInOut(contextualParam->isInOut())
11810+
.withVariadic(contextualParam->isVariadic())
11811+
.withIsolated(contextualParam->isIsolated())
11812+
.withSending(contextualParam->isSending()));
1179211813
}
1179311814
}
1179411815

@@ -11915,6 +11936,12 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar,
1191511936
closureExtInfo = closureExtInfo.withSendable();
1191611937
}
1191711938

11939+
// Propagate sending result from the contextual type to the closure.
11940+
if (auto contextualFnType = contextualType->getAs<FunctionType>()) {
11941+
if (contextualFnType->hasExtInfo() && contextualFnType->hasSendingResult())
11942+
closureExtInfo = closureExtInfo.withSendingResult();
11943+
}
11944+
1191811945
// Isolated parameters override any other kind of isolation we might infer.
1191911946
if (hasIsolatedParam) {
1192011947
closureExtInfo = closureExtInfo.withIsolation(
@@ -15113,6 +15140,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
1511315140
}
1511415141
}
1511515142

15143+
case FixKind::AllowSendingMismatch:
1511615144
case FixKind::InsertCall:
1511715145
case FixKind::RemoveReturn:
1511815146
case FixKind::RemoveAddressOf:

lib/Sema/TypeCheckProtocol.cpp

+17
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

+2-1
Original file line numberDiff line numberDiff line change
@@ -4964,7 +4964,8 @@ TypeResolver::resolveSendingTypeRepr(SendingTypeRepr *repr,
49644964
return ErrorType::get(getASTContext());
49654965
}
49664966

4967-
if (!options.is(TypeResolverContext::FunctionResult) &&
4967+
if (!options.is(TypeResolverContext::ClosureExpr) &&
4968+
!options.is(TypeResolverContext::FunctionResult) &&
49684969
(!options.is(TypeResolverContext::FunctionInput) ||
49694970
options.hasBase(TypeResolverContext::EnumElementDecl))) {
49704971
diagnoseInvalid(repr, repr->getSpecifierLoc(),

lib/Sema/TypeCheckType.h

+1-1
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)