Skip to content

Commit 2f33d87

Browse files
authored
Merge pull request #76988 from hborla/init-checking-order
[Sema] Fix an issue with the ordering of effects checking and actor isolation checking.
2 parents 7c3f965 + e1bf198 commit 2f33d87

10 files changed

+108
-66
lines changed

include/swift/AST/Decl.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2476,8 +2476,12 @@ class PatternBindingDecl final : public Decl,
24762476
getMutablePatternList()[i].setOriginalInit(E);
24772477
}
24782478

2479-
/// Returns a fully typechecked init expression for the pattern at the given
2479+
/// Returns a typechecked init expression for the pattern at the given
24802480
/// index.
2481+
Expr *getContextualizedInit(unsigned i) const;
2482+
2483+
/// Returns an init expression for the pattern at the given index.
2484+
/// The initializer is fully type checked, including effects checking.
24812485
Expr *getCheckedAndContextualizedInit(unsigned i) const;
24822486

24832487
/// Returns the result of `getCheckedAndContextualizedInit()` if the init is

include/swift/AST/TypeCheckRequests.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3929,6 +3929,25 @@ class CheckInconsistentWeakLinkedImportsRequest
39293929
bool isCached() const { return true; }
39303930
};
39313931

3932+
/// Run effects checking for an initializer expression.
3933+
class CheckInitEffectsRequest
3934+
: public SimpleRequest<CheckInitEffectsRequest,
3935+
evaluator::SideEffect(Initializer *, Expr *),
3936+
RequestFlags::Cached> {
3937+
public:
3938+
using SimpleRequest::SimpleRequest;
3939+
3940+
private:
3941+
friend SimpleRequest;
3942+
3943+
evaluator::SideEffect evaluate(Evaluator &evaluator,
3944+
Initializer *initCtx,
3945+
Expr *init) const;
3946+
3947+
public:
3948+
bool isCached() const { return true; }
3949+
};
3950+
39323951
/// Retrieves the primary source files in the main module.
39333952
// FIXME: This isn't really a type-checking request, if we ever split off a
39343953
// zone for more basic AST requests, this should be moved there.

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ SWIFT_REQUEST(TypeChecker, CheckInconsistentAccessLevelOnImportSameFileRequest,
4343
evaluator::SideEffect(SourceFile *), Cached, NoLocationInfo)
4444
SWIFT_REQUEST(TypeChecker, CheckInconsistentWeakLinkedImportsRequest,
4545
evaluator::SideEffect(ModuleDecl *), Cached, NoLocationInfo)
46+
SWIFT_REQUEST(TypeChecker, CheckInitEffectsRequest,
47+
evaluator::SideEffect(Initializer *, Expr *),
48+
Cached, NoLocationInfo)
4649
SWIFT_REQUEST(TypeChecker, CheckRedeclarationRequest,
4750
evaluator::SideEffect(ValueDecl *),
4851
Uncached, NoLocationInfo)

lib/AST/Decl.cpp

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2344,11 +2344,35 @@ PatternBindingDecl::getInitializerIsolation(unsigned i) const {
23442344
return var->getInitializerIsolation();
23452345
}
23462346

2347+
Expr *PatternBindingDecl::getContextualizedInit(unsigned i) const {
2348+
auto *mutableThis = const_cast<PatternBindingDecl *>(this);
2349+
return evaluateOrDefault(
2350+
getASTContext().evaluator,
2351+
PatternBindingCheckedAndContextualizedInitRequest{mutableThis, i},
2352+
nullptr);
2353+
}
2354+
23472355
Expr *PatternBindingDecl::getCheckedAndContextualizedInit(unsigned i) const {
2348-
return evaluateOrDefault(getASTContext().evaluator,
2349-
PatternBindingCheckedAndContextualizedInitRequest{
2350-
const_cast<PatternBindingDecl *>(this), i},
2351-
nullptr);
2356+
auto *expr = getContextualizedInit(i);
2357+
2358+
if (auto *initContext = getInitContext(i)) {
2359+
// Property wrapper isolation is checked separately while
2360+
// synthesizing the backing property wrapper initializer.
2361+
auto *var = getSingleVar();
2362+
if (!(var && var->hasAttachedPropertyWrapper())) {
2363+
(void)getInitializerIsolation(i);
2364+
}
2365+
2366+
// Effects checking for 'async' needs actor isolation to be
2367+
// computed. Always run effects checking after the actor
2368+
// isolation checker.
2369+
evaluateOrDefault(
2370+
getASTContext().evaluator,
2371+
CheckInitEffectsRequest{initContext, expr},
2372+
{});
2373+
}
2374+
2375+
return expr;
23522376
}
23532377

23542378
Expr *PatternBindingDecl::getCheckedAndContextualizedExecutableInit(

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 22 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -698,27 +698,6 @@ static bool isAsyncCall(
698698
/// features.
699699
static bool shouldDiagnoseExistingDataRaces(const DeclContext *dc);
700700

701-
/// Determine whether this closure should be treated as Sendable.
702-
///
703-
/// \param forActorIsolation Whether this check is for the purposes of
704-
/// determining whether the closure must be non-isolated.
705-
static bool isSendableClosure(
706-
const AbstractClosureExpr *closure, bool forActorIsolation) {
707-
if (auto explicitClosure = dyn_cast<ClosureExpr>(closure)) {
708-
if (forActorIsolation && explicitClosure->inheritsActorContext()) {
709-
return false;
710-
}
711-
}
712-
713-
if (auto type = closure->getType()) {
714-
if (auto fnType = type->getAs<AnyFunctionType>())
715-
if (fnType->isSendable())
716-
return true;
717-
}
718-
719-
return false;
720-
}
721-
722701
/// Returns true if this closure acts as an inference boundary in the AST. An
723702
/// inference boundary is an expression in the AST where we newly infer
724703
/// isolation different from our parent decl context.
@@ -733,30 +712,23 @@ static bool isSendableClosure(
733712
/// function. That @MainActor closure would act as an Isolation Inference
734713
/// Boundary.
735714
///
736-
/// \arg forActorIsolation we currently have two slightly varying semantics
737-
/// here. If this is set, then we assuming that we are being called recursively
738-
/// while walking up a decl context path to determine the actor isolation of a
739-
/// closure. In such a case, we do not want to be a boundary if we should
740-
/// inheritActorContext. In other contexts though, we want to determine if the
741-
/// closure is part of an init or deinit. In such a case, we are walking up the
742-
/// decl context chain and we want to stop if we see a sending parameter since
743-
/// in such a case, the sending closure parameter is known to not be part of the
744-
/// init or deinit.
715+
/// \param canInheritActorContext Whether or not the closure is allowed to
716+
/// inherit the isolation of the enclosing context. If this is \c true ,
717+
/// the closure is not considered an isolation inference boundary if the
718+
/// \c @_inheritActorContext attribute is applied to the closure. This
719+
/// attribute is inferred from a parameter declaration for closure arguments,
720+
/// and it's set on the closure in CSApply.
745721
static bool
746722
isIsolationInferenceBoundaryClosure(const AbstractClosureExpr *closure,
747-
bool forActorIsolation) {
723+
bool canInheritActorContext) {
748724
if (auto *ce = dyn_cast<ClosureExpr>(closure)) {
749-
if (!forActorIsolation) {
750-
// For example, one would along this path see if for flow sensitive
751-
// isolation the closure is part of an init or deinit.
752-
if (ce->isPassedToSendingParameter())
753-
return true;
754-
} else {
755-
// This is for actor isolation. If we have inheritActorContext though, we
756-
// do not want to do anything since we are part of our parent's isolation.
757-
if (!ce->inheritsActorContext() && ce->isPassedToSendingParameter())
758-
return true;
759-
}
725+
// If the closure can inherit the isolation of the enclosing context,
726+
// it is not an actor isolation inference boundary.
727+
if (canInheritActorContext && ce->inheritsActorContext())
728+
return false;
729+
730+
if (ce->isPassedToSendingParameter())
731+
return true;
760732
}
761733

762734
// An autoclosure for an async let acts as a boundary. It is non-Sendable
@@ -766,7 +738,7 @@ isIsolationInferenceBoundaryClosure(const AbstractClosureExpr *closure,
766738
return true;
767739
}
768740

769-
return isSendableClosure(closure, forActorIsolation);
741+
return closure->isSendable();
770742
}
771743

772744
/// Add Fix-It text for the given nominal type to adopt Sendable.
@@ -1693,7 +1665,7 @@ swift::isActorInitOrDeInitContext(const DeclContext *dc) {
16931665
// Stop looking if we hit an isolation inference boundary.
16941666
if (auto *closure = dyn_cast<AbstractClosureExpr>(dc)) {
16951667
if (isIsolationInferenceBoundaryClosure(closure,
1696-
false /*is for actor isolation*/))
1668+
/*canInheritActorContext*/false))
16971669
return nullptr;
16981670

16991671
// Otherwise, look through our closure at the closure's parent decl
@@ -2703,7 +2675,8 @@ namespace {
27032675
// function type, but this is okay for non-Sendable closures
27042676
// because they cannot leave the isolation domain they're created
27052677
// in anyway.
2706-
if (closure->isSendable())
2678+
if (isIsolationInferenceBoundaryClosure(
2679+
closure, /*canInheritActorContext*/false))
27072680
return false;
27082681

27092682
if (closure->getActorIsolation().isActorIsolated())
@@ -3237,7 +3210,7 @@ namespace {
32373210
case ActorIsolation::Unspecified:
32383211
case ActorIsolation::Nonisolated:
32393212
case ActorIsolation::NonisolatedUnsafe:
3240-
if (isSendableClosure(closure, /*forActorIsolation=*/true)) {
3213+
if (closure->isSendable()) {
32413214
return ReferencedActor(var, isPotentiallyIsolated, ReferencedActor::SendableClosure);
32423215
}
32433216

@@ -4515,7 +4488,7 @@ namespace {
45154488
// know that all Sendable closures must be nonisolated. That is why it is
45164489
// safe to rely on this path to handle Sendable closures.
45174490
if (isIsolationInferenceBoundaryClosure(
4518-
closure, true /*is for closure isolation*/))
4491+
closure, /*canInheritActorContext*/true))
45194492
return ActorIsolation::forNonisolated(/*unsafe=*/false)
45204493
.withPreconcurrency(preconcurrency);
45214494

@@ -4606,7 +4579,7 @@ bool ActorIsolationChecker::mayExecuteConcurrentlyWith(
46064579
while (useContext != defContext) {
46074580
// If we find a concurrent closure... it can be run concurrently.
46084581
if (auto closure = dyn_cast<AbstractClosureExpr>(useContext)) {
4609-
if (isSendableClosure(closure, /*forActorIsolation=*/false))
4582+
if (closure->isSendable())
46104583
return true;
46114584

46124585
if (isolatedStateMayEscape)
@@ -6063,7 +6036,7 @@ DefaultInitializerIsolation::evaluate(Evaluator &evaluator,
60636036
auto i = pbd->getPatternEntryIndexForVarDecl(var);
60646037

60656038
dc = cast<Initializer>(pbd->getInitContext(i));
6066-
initExpr = pbd->getCheckedAndContextualizedInit(i);
6039+
initExpr = pbd->getContextualizedInit(i);
60676040
enclosingIsolation = getActorIsolation(var);
60686041
} else if (auto *param = dyn_cast<ParamDecl>(var)) {
60696042
// If this parameter corresponds to a stored property for a

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2885,15 +2885,6 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
28852885
// Trigger a request that will complete typechecking for the
28862886
// initializer.
28872887
(void) PBD->getCheckedAndContextualizedInit(i);
2888-
2889-
if (auto *var = PBD->getSingleVar()) {
2890-
if (var->hasAttachedPropertyWrapper())
2891-
return;
2892-
}
2893-
2894-
if (!PBD->getDeclContext()->isLocalContext()) {
2895-
(void) PBD->getInitializerIsolation(i);
2896-
}
28972888
}
28982889
}
28992890

lib/Sema/TypeCheckEffects.cpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3794,12 +3794,25 @@ void TypeChecker::checkFunctionEffects(AbstractFunctionDecl *fn) {
37943794
superInit->walk(checker);
37953795
}
37963796

3797-
void TypeChecker::checkInitializerEffects(Initializer *initCtx,
3798-
Expr *init) {
3797+
evaluator::SideEffect
3798+
CheckInitEffectsRequest::evaluate(Evaluator &evaluator,
3799+
Initializer *initCtx,
3800+
Expr *init) const {
37993801
auto &ctx = initCtx->getASTContext();
38003802
CheckEffectsCoverage checker(ctx, Context::forInitializer(initCtx));
38013803
init->walk(checker);
38023804
init->walk(LocalFunctionEffectsChecker());
3805+
3806+
return {};
3807+
}
3808+
3809+
3810+
void TypeChecker::checkInitializerEffects(Initializer *initCtx,
3811+
Expr *init) {
3812+
evaluateOrDefault(
3813+
initCtx->getASTContext().evaluator,
3814+
CheckInitEffectsRequest{initCtx, init},
3815+
{});
38033816
}
38043817

38053818
void TypeChecker::checkCallerSideDefaultArgumentEffects(DeclContext *initCtx,

lib/Sema/TypeCheckStorage.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -619,7 +619,6 @@ static void checkAndContextualizePatternBindingInit(PatternBindingDecl *binding,
619619
if (auto *initContext = binding->getInitContext(i)) {
620620
auto *init = binding->getInit(i);
621621
TypeChecker::contextualizeInitializer(initContext, init);
622-
TypeChecker::checkInitializerEffects(initContext, init);
623622
}
624623
}
625624

test/Concurrency/isolated_default_arguments.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,18 @@ class C3 {
214214
@SomeGlobalActor var y = 2
215215
}
216216

217+
class C4 {
218+
let task1 = Task {
219+
// expected-error@+2 {{expression is 'async' but is not marked with 'await'}}
220+
// expected-note@+1 {{calls to global function 'requiresMainActor()' from outside of its actor context are implicitly asynchronous}}
221+
requiresMainActor()
222+
}
223+
224+
let task2 = Task {
225+
await requiresMainActor() // okay
226+
}
227+
}
228+
217229
@MainActor struct NonIsolatedInit {
218230
var x = 0
219231
var y = 0

test/Concurrency/isolated_default_property_inits.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
// REQUIRES: concurrency
99
// REQUIRES: asserts
1010

11+
// This tests errors emitted in definite initialization; this test file cannot
12+
// have any type checker errors. Type checker errors for IsolatedDefaultValues
13+
// are tested in isolated_default_arguments.swift
14+
1115
@globalActor
1216
actor SomeGlobalActor {
1317
static let shared = SomeGlobalActor()

0 commit comments

Comments
 (0)