Skip to content

Commit d703e5a

Browse files
committed
[Constraint solver] Track "isolated by preconcurrency" in the solver.
Rather than only setting the isolated-by-preconcurrency bit during constraint application, track the closures it will be set for as part of the constraint system and solution. Then, use that bit when performing "strict concurrency context" checks and type adjustments, so we don't treat an inferred-to-by-`@Sendable`-by-preconcurrency closure in the solver as if it weren't related to preconcurrency. Fixes the spurious warning from #59910.
1 parent f08097e commit d703e5a

File tree

9 files changed

+112
-31
lines changed

9 files changed

+112
-31
lines changed

include/swift/Sema/ConstraintSystem.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1276,6 +1276,10 @@ class Solution {
12761276
/// The set of parameters that have been inferred to be 'isolated'.
12771277
llvm::SmallVector<ParamDecl *, 2> isolatedParams;
12781278

1279+
/// The set of closures that have been inferred to be "isolated by
1280+
/// preconcurrency".
1281+
llvm::SmallVector<const ClosureExpr *, 2> preconcurrencyClosures;
1282+
12791283
/// The set of functions that have been transformed by a result builder.
12801284
llvm::MapVector<AnyFunctionRef, AppliedBuilderTransform>
12811285
resultBuilderTransformed;
@@ -2479,6 +2483,13 @@ struct GetClosureType {
24792483
Type operator()(const AbstractClosureExpr *expr) const;
24802484
};
24812485

2486+
/// Retrieve the closure type from the constraint system.
2487+
struct ClosureIsolatedByPreconcurrency {
2488+
ConstraintSystem &cs;
2489+
2490+
bool operator()(const ClosureExpr *expr) const;
2491+
};
2492+
24822493
/// Describes the type produced when referencing a declaration.
24832494
struct DeclReferenceType {
24842495
/// The "opened" type, which is the type of the declaration where any
@@ -2533,6 +2544,7 @@ class ConstraintSystem {
25332544
friend class ConjunctionElement;
25342545
friend class RequirementFailure;
25352546
friend class MissingMemberFailure;
2547+
friend struct ClosureIsolatedByPreconcurrency;
25362548

25372549
class SolverScope;
25382550

@@ -2663,6 +2675,10 @@ class ConstraintSystem {
26632675
/// The set of parameters that have been inferred to be 'isolated'.
26642676
llvm::SmallSetVector<ParamDecl *, 2> isolatedParams;
26652677

2678+
/// The set of closures that have been inferred to be "isolated by
2679+
/// preconcurrency".
2680+
llvm::SmallSetVector<const ClosureExpr *, 2> preconcurrencyClosures;
2681+
26662682
/// Maps closure parameters to type variables.
26672683
llvm::DenseMap<const ParamDecl *, TypeVariableType *>
26682684
OpenedParameterTypes;
@@ -3233,6 +3249,9 @@ class ConstraintSystem {
32333249
/// The length of \c isolatedParams.
32343250
unsigned numIsolatedParams;
32353251

3252+
/// The length of \c PreconcurrencyClosures.
3253+
unsigned numPreconcurrencyClosures;
3254+
32363255
/// The length of \c ImplicitValueConversions.
32373256
unsigned numImplicitValueConversions;
32383257

@@ -4511,6 +4530,10 @@ class ConstraintSystem {
45114530
llvm::function_ref<Type(const AbstractClosureExpr *)> getClosureType =
45124531
[](const AbstractClosureExpr *) {
45134532
return Type();
4533+
},
4534+
llvm::function_ref<bool(const ClosureExpr *)> isolatedByPreconcurrency =
4535+
[](const ClosureExpr *closure) {
4536+
return closure->isIsolatedByPreconcurrency();
45144537
});
45154538

45164539
/// Given the opened type and a pile of information about a member reference,

lib/Sema/CSApply.cpp

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5638,25 +5638,22 @@ static bool hasCurriedSelf(ConstraintSystem &cs, ConcreteDeclRef callee,
56385638

56395639
/// Apply the contextually Sendable flag to the given expression,
56405640
static void applyContextualClosureFlags(
5641-
Expr *expr, bool implicitSelfCapture, bool inheritActorContext,
5642-
bool isolatedByPreconcurrency) {
5641+
Expr *expr, bool implicitSelfCapture, bool inheritActorContext) {
56435642
if (auto closure = dyn_cast<ClosureExpr>(expr)) {
56445643
closure->setAllowsImplicitSelfCapture(implicitSelfCapture);
56455644
closure->setInheritsActorContext(inheritActorContext);
5646-
closure->setIsolatedByPreconcurrency(isolatedByPreconcurrency);
56475645
return;
56485646
}
56495647

56505648
if (auto captureList = dyn_cast<CaptureListExpr>(expr)) {
56515649
applyContextualClosureFlags(
56525650
captureList->getClosureBody(), implicitSelfCapture,
5653-
inheritActorContext, isolatedByPreconcurrency);
5651+
inheritActorContext);
56545652
}
56555653

56565654
if (auto identity = dyn_cast<IdentityExpr>(expr)) {
56575655
applyContextualClosureFlags(
5658-
identity->getSubExpr(), implicitSelfCapture, inheritActorContext,
5659-
isolatedByPreconcurrency);
5656+
identity->getSubExpr(), implicitSelfCapture, inheritActorContext);
56605657
}
56615658
}
56625659

@@ -5683,8 +5680,6 @@ ArgumentList *ExprRewriter::coerceCallArguments(
56835680
// Determine the parameter bindings.
56845681
ParameterListInfo paramInfo(params, callee.getDecl(), skipCurriedSelf);
56855682

5686-
bool preconcurrency = callee && callee.getDecl()->preconcurrency();
5687-
56885683
// If this application is an init(wrappedValue:) call that needs an injected
56895684
// wrapped value placeholder, the first non-defaulted argument must be
56905685
// wrapped in an OpaqueValueExpr.
@@ -5707,14 +5702,6 @@ ArgumentList *ExprRewriter::coerceCallArguments(
57075702
auto matches = args->matches(params, [&](Expr *E) { return cs.getType(E); });
57085703
if (matches && !shouldInjectWrappedValuePlaceholder &&
57095704
!paramInfo.anyContextualInfo()) {
5710-
// Propagate preconcurrency to any closure arguments.
5711-
if (preconcurrency) {
5712-
for (const auto &arg : *args) {
5713-
Expr *argExpr = arg.getExpr();
5714-
applyContextualClosureFlags(argExpr, false, false, preconcurrency);
5715-
}
5716-
}
5717-
57185705
return args;
57195706
}
57205707

@@ -5865,7 +5852,7 @@ ArgumentList *ExprRewriter::coerceCallArguments(
58655852
bool isImplicitSelfCapture = paramInfo.isImplicitSelfCapture(paramIdx);
58665853
bool inheritsActorContext = paramInfo.inheritsActorContext(paramIdx);
58675854
applyContextualClosureFlags(
5868-
argExpr, isImplicitSelfCapture, inheritsActorContext, preconcurrency);
5855+
argExpr, isImplicitSelfCapture, inheritsActorContext);
58695856

58705857
// If the types exactly match, this is easy.
58715858
auto paramType = param.getOldType();

lib/Sema/CSClosure.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1656,6 +1656,9 @@ SolutionApplicationToFunctionResult ConstraintSystem::applySolution(
16561656
param->setIsolated(true);
16571657
}
16581658

1659+
if (llvm::is_contained(solution.preconcurrencyClosures, closure))
1660+
closure->setIsolatedByPreconcurrency();
1661+
16591662
// Coerce the result type, if it was written explicitly.
16601663
if (closure->hasExplicitResultType()) {
16611664
closure->setExplicitResultType(closureFnType->getResult());

lib/Sema/CSSimplify.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2738,7 +2738,7 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2,
27382738
// For a @preconcurrency callee outside of a strict concurrency
27392739
// context, ignore.
27402740
if (hasPreconcurrencyCallee(this, locator) &&
2741-
!contextRequiresStrictConcurrencyChecking(DC, GetClosureType{*this}))
2741+
!contextRequiresStrictConcurrencyChecking(DC, GetClosureType{*this}, ClosureIsolatedByPreconcurrency{*this}))
27422742
return FixBehavior::Suppress;
27432743

27442744
// Otherwise, warn until Swift 6.
@@ -9841,6 +9841,10 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar,
98419841
auto *closure = castToExpr<ClosureExpr>(closureLocator->getAnchor());
98429842
auto *inferredClosureType = getClosureType(closure);
98439843

9844+
// Note if this closure is isolated by preconcurrency.
9845+
if (hasPreconcurrencyCallee(this, locator))
9846+
preconcurrencyClosures.insert(closure);
9847+
98449848
// Let's look through all optionals associated with contextual
98459849
// type to make it possible to infer parameter/result type of
98469850
// the closure faster e.g.:

lib/Sema/CSSolver.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,8 @@ Solution ConstraintSystem::finalize() {
182182
solution.solutionApplicationTargets = solutionApplicationTargets;
183183
solution.caseLabelItems = caseLabelItems;
184184
solution.isolatedParams.append(isolatedParams.begin(), isolatedParams.end());
185+
solution.preconcurrencyClosures.append(preconcurrencyClosures.begin(),
186+
preconcurrencyClosures.end());
185187

186188
for (const auto &transformed : resultBuilderTransformed) {
187189
solution.resultBuilderTransformed.insert(transformed);
@@ -295,6 +297,10 @@ void ConstraintSystem::applySolution(const Solution &solution) {
295297
isolatedParams.insert(param);
296298
}
297299

300+
for (auto closure : solution.preconcurrencyClosures) {
301+
preconcurrencyClosures.insert(closure);
302+
}
303+
298304
for (const auto &transformed : solution.resultBuilderTransformed) {
299305
resultBuilderTransformed.insert(transformed);
300306
}
@@ -535,6 +541,7 @@ ConstraintSystem::SolverScope::SolverScope(ConstraintSystem &cs)
535541
numSolutionApplicationTargets = cs.solutionApplicationTargets.size();
536542
numCaseLabelItems = cs.caseLabelItems.size();
537543
numIsolatedParams = cs.isolatedParams.size();
544+
numPreconcurrencyClosures = cs.preconcurrencyClosures.size();
538545
numImplicitValueConversions = cs.ImplicitValueConversions.size();
539546
numArgumentLists = cs.ArgumentLists.size();
540547
numImplicitCallAsFunctionRoots = cs.ImplicitCallAsFunctionRoots.size();
@@ -646,6 +653,9 @@ ConstraintSystem::SolverScope::~SolverScope() {
646653
// Remove any isolated parameters.
647654
truncate(cs.isolatedParams, numIsolatedParams);
648655

656+
// Remove any preconcurrency closures.
657+
truncate(cs.preconcurrencyClosures, numPreconcurrencyClosures);
658+
649659
// Remove any implicit value conversions.
650660
truncate(cs.ImplicitValueConversions, numImplicitValueConversions);
651661

lib/Sema/ConstraintSystem.cpp

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1202,6 +1202,12 @@ Type GetClosureType::operator()(const AbstractClosureExpr *expr) const {
12021202
return Type();
12031203
}
12041204

1205+
bool
1206+
ClosureIsolatedByPreconcurrency::operator()(const ClosureExpr *expr) const {
1207+
return expr->isIsolatedByPreconcurrency() ||
1208+
cs.preconcurrencyClosures.count(expr);
1209+
}
1210+
12051211
Type ConstraintSystem::getUnopenedTypeOfReference(
12061212
VarDecl *value, Type baseType, DeclContext *UseDC,
12071213
ConstraintLocator *memberLocator, bool wantInterfaceType) {
@@ -1217,20 +1223,22 @@ Type ConstraintSystem::getUnopenedTypeOfReference(
12171223

12181224
return wantInterfaceType ? var->getInterfaceType() : var->getType();
12191225
},
1220-
memberLocator, wantInterfaceType, GetClosureType{*this});
1226+
memberLocator, wantInterfaceType, GetClosureType{*this},
1227+
ClosureIsolatedByPreconcurrency{*this});
12211228
}
12221229

12231230
Type ConstraintSystem::getUnopenedTypeOfReference(
12241231
VarDecl *value, Type baseType, DeclContext *UseDC,
12251232
llvm::function_ref<Type(VarDecl *)> getType,
12261233
ConstraintLocator *memberLocator, bool wantInterfaceType,
1227-
llvm::function_ref<Type(const AbstractClosureExpr *)> getClosureType) {
1234+
llvm::function_ref<Type(const AbstractClosureExpr *)> getClosureType,
1235+
llvm::function_ref<bool(const ClosureExpr *)> isolatedByPreconcurrency) {
12281236
Type requestedType =
12291237
getType(value)->getWithoutSpecifierType()->getReferenceStorageReferent();
12301238

12311239
// Adjust the type for concurrency.
12321240
requestedType = adjustVarTypeForConcurrency(
1233-
requestedType, value, UseDC, getClosureType);
1241+
requestedType, value, UseDC, getClosureType, isolatedByPreconcurrency);
12341242

12351243
// If we're dealing with contextual types, and we referenced this type from
12361244
// a different context, map the type.
@@ -1409,7 +1417,8 @@ AnyFunctionType *ConstraintSystem::adjustFunctionTypeForConcurrency(
14091417
unsigned numApplies, bool isMainDispatchQueue,
14101418
OpenedTypeMap &replacements) {
14111419
return swift::adjustFunctionTypeForConcurrency(
1412-
fnType, decl, dc, numApplies, isMainDispatchQueue, GetClosureType{*this},
1420+
fnType, decl, dc, numApplies, isMainDispatchQueue,
1421+
GetClosureType{*this}, ClosureIsolatedByPreconcurrency{*this},
14131422
[&](Type type) {
14141423
if (replacements.empty())
14151424
return type;
@@ -2500,7 +2509,8 @@ Type ConstraintSystem::getEffectiveOverloadType(ConstraintLocator *locator,
25002509
type = withDynamicSelfResultReplaced(type, /*uncurryLevel=*/0);
25012510
}
25022511
type = adjustVarTypeForConcurrency(
2503-
type, var, useDC, GetClosureType{*this});
2512+
type, var, useDC, GetClosureType{*this},
2513+
ClosureIsolatedByPreconcurrency{*this});
25042514
} else if (isa<AbstractFunctionDecl>(decl) || isa<EnumElementDecl>(decl)) {
25052515
if (decl->isInstanceMember() &&
25062516
(!overload.getBaseType() ||

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,9 @@ static void addSendableFixIt(
600600
static bool shouldDiagnoseExistingDataRaces(const DeclContext *dc) {
601601
return contextRequiresStrictConcurrencyChecking(dc, [](const AbstractClosureExpr *) {
602602
return Type();
603+
},
604+
[](const ClosureExpr *closure) {
605+
return closure->isIsolatedByPreconcurrency();
603606
});
604607
}
605608

@@ -3972,7 +3975,8 @@ void swift::checkOverrideActorIsolation(ValueDecl *value) {
39723975

39733976
bool swift::contextRequiresStrictConcurrencyChecking(
39743977
const DeclContext *dc,
3975-
llvm::function_ref<Type(const AbstractClosureExpr *)> getType) {
3978+
llvm::function_ref<Type(const AbstractClosureExpr *)> getType,
3979+
llvm::function_ref<bool(const ClosureExpr *)> isolatedByPreconcurrency) {
39763980
switch (dc->getASTContext().LangOpts.StrictConcurrencyLevel) {
39773981
case StrictConcurrency::Complete:
39783982
return true;
@@ -3993,7 +3997,7 @@ bool swift::contextRequiresStrictConcurrencyChecking(
39933997

39943998
// Don't take any more cues if this only got its type information by
39953999
// being provided to a `@preconcurrency` operation.
3996-
if (explicitClosure->isIsolatedByPreconcurrency()) {
4000+
if (isolatedByPreconcurrency(explicitClosure)) {
39974001
dc = dc->getParent();
39984002
continue;
39994003
}
@@ -4551,11 +4555,13 @@ static bool hasKnownUnsafeSendableFunctionParams(AbstractFunctionDecl *func) {
45514555

45524556
Type swift::adjustVarTypeForConcurrency(
45534557
Type type, VarDecl *var, DeclContext *dc,
4554-
llvm::function_ref<Type(const AbstractClosureExpr *)> getType) {
4558+
llvm::function_ref<Type(const AbstractClosureExpr *)> getType,
4559+
llvm::function_ref<bool(const ClosureExpr *)> isolatedByPreconcurrency) {
45554560
if (!var->preconcurrency())
45564561
return type;
45574562

4558-
if (contextRequiresStrictConcurrencyChecking(dc, getType))
4563+
if (contextRequiresStrictConcurrencyChecking(
4564+
dc, getType, isolatedByPreconcurrency))
45594565
return type;
45604566

45614567
bool isLValue = false;
@@ -4676,9 +4682,11 @@ AnyFunctionType *swift::adjustFunctionTypeForConcurrency(
46764682
AnyFunctionType *fnType, ValueDecl *decl, DeclContext *dc,
46774683
unsigned numApplies, bool isMainDispatchQueue,
46784684
llvm::function_ref<Type(const AbstractClosureExpr *)> getType,
4685+
llvm::function_ref<bool(const ClosureExpr *)> isolatedByPreconcurrency,
46794686
llvm::function_ref<Type(Type)> openType) {
46804687
// Apply unsafe concurrency features to the given function type.
4681-
bool strictChecking = contextRequiresStrictConcurrencyChecking(dc, getType);
4688+
bool strictChecking = contextRequiresStrictConcurrencyChecking(
4689+
dc, getType, isolatedByPreconcurrency);
46824690
fnType = applyUnsafeConcurrencyToFunctionType(
46834691
fnType, decl, strictChecking, numApplies, isMainDispatchQueue);
46844692

@@ -4738,7 +4746,10 @@ bool swift::completionContextUsesConcurrencyFeatures(const DeclContext *dc) {
47384746
return contextRequiresStrictConcurrencyChecking(
47394747
dc, [](const AbstractClosureExpr *) {
47404748
return Type();
4741-
});
4749+
},
4750+
[](const ClosureExpr *closure) {
4751+
return closure->isIsolatedByPreconcurrency();
4752+
});
47424753
}
47434754

47444755
AbstractFunctionDecl const *swift::isActorInitOrDeInitContext(
@@ -4847,6 +4858,9 @@ static ActorIsolation getActorIsolationForReference(
48474858
fromDC,
48484859
[](const AbstractClosureExpr *closure) {
48494860
return closure->getType();
4861+
},
4862+
[](const ClosureExpr *closure) {
4863+
return closure->isIsolatedByPreconcurrency();
48504864
})) {
48514865
declIsolation = ActorIsolation::forGlobalActor(
48524866
declIsolation.getGlobalActor(), /*unsafe=*/false);

lib/Sema/TypeCheckConcurrency.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,8 @@ void checkOverrideActorIsolation(ValueDecl *value);
102102
/// code where strict checking has been enabled.
103103
bool contextRequiresStrictConcurrencyChecking(
104104
const DeclContext *dc,
105-
llvm::function_ref<Type(const AbstractClosureExpr *)> getType);
105+
llvm::function_ref<Type(const AbstractClosureExpr *)> getType,
106+
llvm::function_ref<bool(const ClosureExpr *)> isolatedByPreconcurrency);
106107

107108
/// Describes a referenced actor variable and whether it is isolated.
108109
struct ReferencedActor {
@@ -417,7 +418,8 @@ Type getExplicitGlobalActor(ClosureExpr *closure);
417418
/// Adjust the type of the variable for concurrency.
418419
Type adjustVarTypeForConcurrency(
419420
Type type, VarDecl *var, DeclContext *dc,
420-
llvm::function_ref<Type(const AbstractClosureExpr *)> getType);
421+
llvm::function_ref<Type(const AbstractClosureExpr *)> getType,
422+
llvm::function_ref<bool(const ClosureExpr *)> isolatedByPreconcurrency);
421423

422424
/// Adjust the given function type to account for concurrency-specific
423425
/// attributes whose affect on the type might differ based on context.
@@ -428,6 +430,7 @@ AnyFunctionType *adjustFunctionTypeForConcurrency(
428430
AnyFunctionType *fnType, ValueDecl *decl, DeclContext *dc,
429431
unsigned numApplies, bool isMainDispatchQueue,
430432
llvm::function_ref<Type(const AbstractClosureExpr *)> getType,
433+
llvm::function_ref<bool(const ClosureExpr *)> isolatedByPreconcurrency,
431434
llvm::function_ref<Type(Type)> openType);
432435

433436
/// Determine whether the given name is that of a DispatchQueue operation that

test/Concurrency/predates_concurrency.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,3 +152,30 @@ func testStringPlacement() {
152152
let fn2 = StringPlacement.position(before:)
153153
let _: Int = fn2 // expected-error{{cannot convert value of type '(String) -> ([String]) -> Int' to specified type 'Int'}}
154154
}
155+
156+
// @preconcurrency in an outer closure
157+
// (https://github.com/apple/swift/issues/59910)
158+
struct Scheduled<T> { }
159+
160+
@preconcurrency
161+
func doPreconcurrency(_: @Sendable () -> Void) { }
162+
163+
class EventLoop {
164+
@discardableResult
165+
@preconcurrency
166+
func scheduleTask<T>(deadline: Int, _ task: @escaping @Sendable () throws -> T) -> Scheduled<T> { fatalError("") }
167+
}
168+
169+
class C {
170+
var ev: EventLoop? = nil
171+
172+
func test(i: Int) {
173+
func doNext() {
174+
doPreconcurrency {
175+
self.ev?.scheduleTask(deadline: i, doNext)
176+
return
177+
}
178+
}
179+
}
180+
}
181+

0 commit comments

Comments
 (0)