From 95a710787215ca47a87df3245fb4c372d34bae2c Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 26 Sep 2023 12:26:42 -0700 Subject: [PATCH 1/5] [Concurrency] Allow default arguments to require actor isolation. Type checking a default argument expression will compute the required actor isolation for evaluating that argument value synchronously. Actor isolation checking is deferred to the caller; it is an error to use a default argument from across isolation domains. Currently gated behind -enable-experimental-feature IsolatedDefaultArguments. --- include/swift/AST/Decl.h | 5 + include/swift/AST/DiagnosticsSema.def | 4 + include/swift/AST/Expr.h | 5 + include/swift/AST/TypeCheckRequests.h | 18 +++ include/swift/AST/TypeCheckerTypeIDZone.def | 2 + include/swift/Basic/Features.def | 3 + lib/AST/ASTPrinter.cpp | 4 + lib/AST/Decl.cpp | 8 ++ lib/AST/Expr.cpp | 6 + lib/Sema/TypeCheckConcurrency.cpp | 96 ++++++++++++++++ lib/Sema/TypeCheckConcurrency.h | 4 + lib/Sema/TypeCheckDeclPrimary.cpp | 15 ++- .../isolated_default_arguments.swift | 108 ++++++++++++++++++ 13 files changed, 276 insertions(+), 2 deletions(-) create mode 100644 test/Concurrency/isolated_default_arguments.swift diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 24309220b9f56..26f89d1462bf8 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -6415,6 +6415,11 @@ class ParamDecl : public VarDecl { /// at the call site in order to have the correct context information. Expr *getTypeCheckedDefaultExpr() const; + /// The actor isolation required of the caller in order to use the + /// default argument for this parameter. If the required isolation is + /// not met, an argument must be written explicitly at the call-site. + ActorIsolation getDefaultArgumentIsolation() const; + /// Retrieve the potentially un-type-checked default argument expression for /// this parameter, which can be queried for information such as its source /// range and textual representation. Returns \c nullptr if there is no diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 466a8685a2c83..64afff15840de 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -5236,6 +5236,10 @@ ERROR(distributed_actor_isolated_non_self_reference,none, "distributed actor-isolated %kind0 can not be accessed from a " "non-isolated context", (const ValueDecl *)) +ERROR(isolated_default_argument,none, + "%0 default argument cannot be synchronously evaluated from a " + "%1 context", + (ActorIsolation, ActorIsolation)) ERROR(distributed_actor_needs_explicit_distributed_import,none, "'Distributed' module not imported, required for 'distributed actor'", ()) diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index 59d8ea8646919..d9a15c53a8724 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -4569,6 +4569,11 @@ class DefaultArgumentExpr final : public Expr { /// expression within the context of the call site. Expr *getCallerSideDefaultExpr() const; + /// Get the required actor isolation for evaluating this default argument + /// synchronously. If the caller does not meet the required isolation, the + /// argument must be written explicitly at the call-site. + ActorIsolation getRequiredIsolation() const; + static bool classof(const Expr *E) { return E->getKind() == ExprKind::DefaultArgument; } diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 118fd02e612b6..63fdb44eba8f4 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -2934,6 +2934,24 @@ class DefaultArgumentTypeRequest void cacheResult(Type type) const; }; +/// Compute the actor isolation needed to synchronously evaluate the +/// default argument for the given parameter. +class DefaultArgumentIsolation + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + ActorIsolation evaluate(Evaluator &evaluator, ParamDecl *param) const; + +public: + bool isCached() const { return true; } +}; + /// Computes the fully type-checked caller-side default argument within the /// context of the call site that it will be inserted into. class CallerSideDefaultArgExprRequest diff --git a/include/swift/AST/TypeCheckerTypeIDZone.def b/include/swift/AST/TypeCheckerTypeIDZone.def index bd89936fcaa15..6d34f079efe43 100644 --- a/include/swift/AST/TypeCheckerTypeIDZone.def +++ b/include/swift/AST/TypeCheckerTypeIDZone.def @@ -62,6 +62,8 @@ SWIFT_REQUEST(TypeChecker, DefaultArgumentExprRequest, Expr *(ParamDecl *), SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, DefaultArgumentTypeRequest, Type(ParamDecl *), SeparatelyCached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, DefaultArgumentIsolation, + ActorIsolation(ParamDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, DefaultArgumentInitContextRequest, Initializer *(ParamDecl *), SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, DefaultDefinitionTypeRequest, diff --git a/include/swift/Basic/Features.def b/include/swift/Basic/Features.def index 8b40891b95553..cc79cde564aaf 100644 --- a/include/swift/Basic/Features.def +++ b/include/swift/Basic/Features.def @@ -222,6 +222,9 @@ EXPERIMENTAL_FEATURE(SendNonSendable, false) /// Within strict concurrency, narrow global variable isolation requirements. EXPERIMENTAL_FEATURE(GlobalConcurrency, false) +/// Allow default arguments to require isolation at the call-site. +EXPERIMENTAL_FEATURE(IsolatedDefaultArguments, false) + /// Enable extended callbacks (with additional parameters) to be used when the /// "playground transform" is enabled. EXPERIMENTAL_FEATURE(PlaygroundExtendedCallbacks, true) diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 560403536edfb..af3f866058df1 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -3551,6 +3551,10 @@ static bool usesFeatureSendNonSendable(Decl *decl) { static bool usesFeatureGlobalConcurrency(Decl *decl) { return false; } +static bool usesFeatureIsolatedDefaultArguments(Decl *decl) { + return false; +} + static bool usesFeaturePlaygroundExtendedCallbacks(Decl *decl) { return false; } diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index fab1c48621352..b08f595cca36f 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -8263,6 +8263,14 @@ Type ParamDecl::getTypeOfDefaultExpr() const { return Type(); } +ActorIsolation ParamDecl::getDefaultArgumentIsolation() const { + auto &ctx = getASTContext(); + return evaluateOrDefault( + ctx.evaluator, + DefaultArgumentIsolation{const_cast(this)}, + ActorIsolation::forNonisolated()); +} + void ParamDecl::setDefaultExpr(Expr *E, bool isTypeChecked) { if (!DefaultValueAndFlags.getPointer()) { if (!E) return; diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 0db9de12dda6f..e827a8694d264 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -1606,6 +1606,12 @@ Expr *DefaultArgumentExpr::getCallerSideDefaultExpr() const { new (ctx) ErrorExpr(getSourceRange(), getType())); } +ActorIsolation +DefaultArgumentExpr::getRequiredIsolation() const { + auto *param = getParamDecl(); + return param->getDefaultArgumentIsolation(); +} + ValueDecl *ApplyExpr::getCalledValue(bool skipFunctionConversions) const { return ::getCalledValue(Fn, skipFunctionConversions); } diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index de0ad395b3469..6dacf5937069f 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -1939,6 +1939,12 @@ namespace { llvm::function_ref getClosureActorIsolation; + bool shouldRefineIsolation = false; + + /// Used under the mode to compute required actor isolation for + /// an expression or function. + ActorIsolation requiredIsolation = ActorIsolation::forNonisolated(); + /// Keeps track of the capture context of variables that have been /// explicitly captured in closures. llvm::SmallDenseMap> @@ -2123,6 +2129,65 @@ namespace { } } + bool refineRequiredIsolation(ActorIsolation refinedIsolation) { + if (!shouldRefineIsolation) + return false; + + if (auto *closure = dyn_cast(getDeclContext())) { + // We cannot infer a more specific actor isolation for a @Sendable + // closure. It is an error to cast away actor isolation from a function + // type, but this is okay for non-Sendable closures because they cannot + // leave the isolation domain they're created in anyway. + if (closure->isSendable()) + return false; + + if (closure->getActorIsolation().isActorIsolated()) + return false; + } + + // To refine the required isolation, the existing requirement + // must either be 'nonisolated' or exactly the same as the + // new refinement. + if (requiredIsolation == ActorIsolation::Nonisolated || + requiredIsolation == refinedIsolation) { + requiredIsolation = refinedIsolation; + return true; + } + + return false; + } + + void checkDefaultArgument(DefaultArgumentExpr *expr) { + // Check the context isolation against the required isolation for + // evaluating the default argument synchronously. If the default + // argument must be evaluated asynchronously, it must be written + // explicitly in the argument list with 'await'. + auto requiredIsolation = expr->getRequiredIsolation(); + auto contextIsolation = getInnermostIsolatedContext( + getDeclContext(), getClosureActorIsolation); + + if (requiredIsolation == contextIsolation) + return; + + switch (requiredIsolation) { + // Nonisolated is okay from any caller isolation because + // default arguments cannot have any async calls. + case ActorIsolation::Unspecified: + case ActorIsolation::Nonisolated: + return; + + case ActorIsolation::GlobalActor: + case ActorIsolation::GlobalActorUnsafe: + case ActorIsolation::ActorInstance: + break; + } + + auto &ctx = getDeclContext()->getASTContext(); + ctx.Diags.diagnose( + expr->getLoc(), diag::isolated_default_argument, + requiredIsolation, contextIsolation); + } + /// Check closure captures for Sendable violations. void checkLocalCaptures(AnyFunctionRef localFunc) { SmallVector captures; @@ -2180,6 +2245,16 @@ namespace { contextStack.push_back(dc); } + ActorIsolation computeRequiredIsolation(Expr *expr) { + auto &ctx = getDeclContext()->getASTContext(); + + shouldRefineIsolation = + ctx.LangOpts.hasFeature(Feature::IsolatedDefaultArguments); + expr->walk(*this); + shouldRefineIsolation = false; + return requiredIsolation; + } + /// Searches the applyStack from back to front for the inner-most CallExpr /// and marks that CallExpr as implicitly async. /// @@ -2375,6 +2450,10 @@ namespace { checkFunctionConversion(funcConv); } + if (auto *defaultArg = dyn_cast(expr)) { + checkDefaultArgument(defaultArg); + } + return Action::Continue(expr); } @@ -2920,6 +2999,9 @@ namespace { if (!unsatisfiedIsolation) return false; + if (refineRequiredIsolation(*unsatisfiedIsolation)) + return false; + // At this point, we know a jump is made to the callee that yields // an isolation requirement unsatisfied by the calling context, so // set the unsatisfiedIsolationJump fields of the ApplyExpr appropriately @@ -3255,6 +3337,14 @@ namespace { case AsyncMarkingResult::SyncContext: case AsyncMarkingResult::NotFound: + // If we found an implicitly async reference in a sync + // context and we're computing the required isolation for + // an expression, the calling context requires the isolation + // of the reference. + if (refineRequiredIsolation(result.isolation)) { + return false; + } + // Complain about access outside of the isolation domain. auto useKind = static_cast( kindOfUsage(decl, context).value_or(VarRefUseEnv::Read)); @@ -3467,6 +3557,12 @@ void swift::checkInitializerActorIsolation(Initializer *init, Expr *expr) { expr->walk(checker); } +ActorIsolation +swift::computeRequiredIsolation(Initializer *init, Expr *expr) { + ActorIsolationChecker checker(init); + return checker.computeRequiredIsolation(expr); +} + void swift::checkEnumElementActorIsolation( EnumElementDecl *element, Expr *expr) { ActorIsolationChecker checker(element); diff --git a/lib/Sema/TypeCheckConcurrency.h b/lib/Sema/TypeCheckConcurrency.h index 3947fa1d3b6a2..bfc1f6878a6e5 100644 --- a/lib/Sema/TypeCheckConcurrency.h +++ b/lib/Sema/TypeCheckConcurrency.h @@ -60,6 +60,10 @@ void checkInitializerActorIsolation(Initializer *init, Expr *expr); void checkEnumElementActorIsolation(EnumElementDecl *element, Expr *expr); void checkPropertyWrapperActorIsolation(VarDecl *wrappedVar, Expr *expr); +/// Compute the actor isolation required in order to evaluate the given +/// initializer expression synchronously. +ActorIsolation computeRequiredIsolation(Initializer *init, Expr *expr); + /// States the reason for checking the Sendability of a given declaration. enum class SendableCheckReason { /// A reference to an actor from outside that actor. diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index bbe04d408cb1f..488def9dde7c2 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -1069,6 +1069,8 @@ static void checkDefaultArguments(ParameterList *params) { for (auto *param : *params) { auto ifacety = param->getInterfaceType(); auto *expr = param->getTypeCheckedDefaultExpr(); + (void)param->getDefaultArgumentIsolation(); + if (!ifacety->hasPlaceholder()) { continue; } @@ -1149,13 +1151,22 @@ Expr *DefaultArgumentExprRequest::evaluate(Evaluator &evaluator, // Walk the checked initializer and contextualize any closures // we saw there. TypeChecker::contextualizeInitializer(dc, initExpr); - - checkInitializerActorIsolation(dc, initExpr); TypeChecker::checkInitializerEffects(dc, initExpr); return initExpr; } +ActorIsolation +DefaultArgumentIsolation::evaluate(Evaluator &evaluator, + ParamDecl *param) const { + if (!param->hasDefaultExpr()) + return ActorIsolation::forUnspecified(); + + auto *dc = param->getDefaultArgumentInitContext(); + auto *initExpr = param->getTypeCheckedDefaultExpr(); + return computeRequiredIsolation(dc, initExpr); +} + Type DefaultArgumentTypeRequest::evaluate(Evaluator &evaluator, ParamDecl *param) const { if (auto *expr = param->getTypeCheckedDefaultExpr()) { diff --git a/test/Concurrency/isolated_default_arguments.swift b/test/Concurrency/isolated_default_arguments.swift new file mode 100644 index 0000000000000..6cb05ab3ab873 --- /dev/null +++ b/test/Concurrency/isolated_default_arguments.swift @@ -0,0 +1,108 @@ +// RUN: %empty-directory(%t) + +// RUN: %target-swift-frontend -emit-module -emit-module-path %t/OtherActors.swiftmodule -module-name OtherActors %S/Inputs/OtherActors.swift -disable-availability-checking + +// RUN: %target-swift-frontend -I %t -disable-availability-checking -strict-concurrency=complete -enable-experimental-feature IsolatedDefaultArguments -parse-as-library -emit-sil -o /dev/null -verify %s +// RUN: %target-swift-frontend -I %t -disable-availability-checking -strict-concurrency=complete -parse-as-library -emit-sil -o /dev/null -verify -enable-experimental-feature IsolatedDefaultArguments -enable-experimental-feature SendNonSendable %s + +// REQUIRES: concurrency +// REQUIRES: asserts + +@globalActor +actor SomeGlobalActor { + static let shared = SomeGlobalActor() +} + +@MainActor +func requiresMainActor() -> Int { 0 } + +// expected-note@+2 2 {{calls to global function 'requiresSomeGlobalActor()' from outside of its actor context are implicitly asynchronous}} +@SomeGlobalActor +func requiresSomeGlobalActor() -> Int { 0 } + +func mainActorDefaultArg(value: Int = requiresMainActor()) {} + +func mainActorClosure( + closure: () -> Int = { + requiresMainActor() + } +) {} + +func mainActorClosureCall( + closure: Int = { + requiresMainActor() + }() +) {} + +@MainActor func mainActorCaller() { + mainActorDefaultArg() + mainActorClosure() + mainActorClosureCall() +} + +func nonisolatedCaller() { + // expected-error@+1 {{main actor-isolated default argument cannot be synchronously evaluated from a nonisolated context}} + mainActorDefaultArg() + + // FIXME: confusing error message. + // expected-error@+1 {{main actor-isolated default argument cannot be synchronously evaluated from a nonisolated context}} + mainActorClosure() + + // expected-error@+1 {{main actor-isolated default argument cannot be synchronously evaluated from a nonisolated context}} + mainActorClosureCall() +} + +func nonisolatedAsyncCaller() async { + // expected-error@+1 {{main actor-isolated default argument cannot be synchronously evaluated from a nonisolated context}} + mainActorDefaultArg() + + // expected-error@+1 {{main actor-isolated default argument cannot be synchronously evaluated from a nonisolated context}} + mainActorClosure() + + // expected-error@+1 {{main actor-isolated default argument cannot be synchronously evaluated from a nonisolated context}} + mainActorClosureCall() + + await mainActorDefaultArg(value: requiresMainActor()) + + // 'await' doesn't help closures in default arguments; the calling context needs a matching + // actor isolation for that isolation to be inferred for the closure value. + + // expected-error@+2 {{main actor-isolated default argument cannot be synchronously evaluated from a nonisolated context}} + // expected-warning@+1 {{no 'async' operations occur within 'await' expression}} + await mainActorClosure() + + // expected-error@+2 {{main actor-isolated default argument cannot be synchronously evaluated from a nonisolated context}} + // expected-warning@+1 {{no 'async' operations occur within 'await' expression}} + await mainActorClosureCall() +} + +func conflictingIsolationDefaultArg( + // expected-error@+1 {{call to global actor 'SomeGlobalActor'-isolated global function 'requiresSomeGlobalActor()' in a synchronous nonisolated context}} + value: (Int, Int) = (requiresMainActor(), requiresSomeGlobalActor()) +) {} + +func closureLosesIsolation( + closure: @Sendable () -> Void = { + // expected-note@+1 {{calls to local function 'f()' from outside of its actor context are implicitly asynchronous}} + @MainActor func f() {} + // expected-error@+1 {{call to main actor-isolated local function 'f()' in a synchronous nonisolated context}} + f() + } +) {} + +func closureIsolationMismatch( + closure: @SomeGlobalActor () -> Void = { + // expected-note@+1 {{calls to local function 'f()' from outside of its actor context are implicitly asynchronous}} + @MainActor func f() {} + // expected-error@+1 {{call to main actor-isolated local function 'f()' in a synchronous global actor 'SomeGlobalActor'-isolated context}} + f() + } +) {} + +func conflictingClosureIsolation( + closure: () -> Void = { + _ = requiresMainActor() + // expected-error@+1 {{call to global actor 'SomeGlobalActor'-isolated global function 'requiresSomeGlobalActor()' in a synchronous nonisolated context}} + _ = requiresSomeGlobalActor() + } +) {} From 88b2332ba0285fb2f52098ca8d0c7e0351d6a94c Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 26 Sep 2023 23:07:44 -0700 Subject: [PATCH 2/5] [Concurrency] Handle nested decl contexts while computing the required isolation for a default argument, and improve diagnostics for conflicting isolation. --- include/swift/AST/DiagnosticsSema.def | 3 + lib/Sema/TypeCheckConcurrency.cpp | 78 +++++++++++++------ .../isolated_default_arguments.swift | 24 +++++- 3 files changed, 78 insertions(+), 27 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 64afff15840de..2cbef41a8887c 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -5240,6 +5240,9 @@ ERROR(isolated_default_argument,none, "%0 default argument cannot be synchronously evaluated from a " "%1 context", (ActorIsolation, ActorIsolation)) +ERROR(conflicting_default_argument_isolation,none, + "default argument cannot be both %0 and %1", + (ActorIsolation, ActorIsolation)) ERROR(distributed_actor_needs_explicit_distributed_import,none, "'Distributed' module not imported, required for 'distributed actor'", ()) diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 6dacf5937069f..c6e3b09a5f0be 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -1939,11 +1939,11 @@ namespace { llvm::function_ref getClosureActorIsolation; - bool shouldRefineIsolation = false; + SourceLoc requiredIsolationLoc; /// Used under the mode to compute required actor isolation for /// an expression or function. - ActorIsolation requiredIsolation = ActorIsolation::forNonisolated(); + llvm::SmallDenseMap requiredIsolation; /// Keeps track of the capture context of variables that have been /// explicitly captured in closures. @@ -2130,31 +2130,60 @@ namespace { } bool refineRequiredIsolation(ActorIsolation refinedIsolation) { - if (!shouldRefineIsolation) + if (requiredIsolationLoc.isInvalid()) return false; - if (auto *closure = dyn_cast(getDeclContext())) { - // We cannot infer a more specific actor isolation for a @Sendable - // closure. It is an error to cast away actor isolation from a function - // type, but this is okay for non-Sendable closures because they cannot - // leave the isolation domain they're created in anyway. - if (closure->isSendable()) - return false; + auto infersIsolationFromContext = + [](const DeclContext *dc) -> bool { + // Isolation for declarations is based solely on explicit + // annotations; only infer isolation for initializer expressions + // and closures. + if (dc->getAsDecl()) + return false; + + if (auto *closure = dyn_cast(dc)) { + // We cannot infer a more specific actor isolation for a Sendable + // closure. It is an error to cast away actor isolation from a + // function type, but this is okay for non-Sendable closures + // because they cannot leave the isolation domain they're created + // in anyway. + if (closure->isSendable()) + return false; - if (closure->getActorIsolation().isActorIsolated()) + if (closure->getActorIsolation().isActorIsolated()) + return false; + } + + return true; + }; + + // For the call to require the given actor isolation, every DeclContext + // in the current context stack must require the same isolation. If + // along the way to the innermost context, we find a DeclContext that + // has a different isolation (e.g. it's a local function that does not + // recieve isolation from its decl context), then the expression cannot + // require a different isolation. + for (auto *dc : contextStack) { + if (!infersIsolationFromContext(dc)) return false; - } - // To refine the required isolation, the existing requirement - // must either be 'nonisolated' or exactly the same as the - // new refinement. - if (requiredIsolation == ActorIsolation::Nonisolated || - requiredIsolation == refinedIsolation) { - requiredIsolation = refinedIsolation; - return true; + // To refine the required isolation, the existing requirement + // must either be 'nonisolated' or exactly the same as the + // new refinement. + auto isolation = requiredIsolation.find(dc); + if (isolation == requiredIsolation.end() || + isolation->second == ActorIsolation::Nonisolated) { + requiredIsolation[dc] = refinedIsolation; + } else if (isolation->second != refinedIsolation) { + dc->getASTContext().Diags.diagnose( + requiredIsolationLoc, + diag::conflicting_default_argument_isolation, + isolation->second, refinedIsolation); + return true; + } } - return false; + return true; } void checkDefaultArgument(DefaultArgumentExpr *expr) { @@ -2248,11 +2277,12 @@ namespace { ActorIsolation computeRequiredIsolation(Expr *expr) { auto &ctx = getDeclContext()->getASTContext(); - shouldRefineIsolation = - ctx.LangOpts.hasFeature(Feature::IsolatedDefaultArguments); + if (ctx.LangOpts.hasFeature(Feature::IsolatedDefaultArguments)) + requiredIsolationLoc = expr->getLoc(); + expr->walk(*this); - shouldRefineIsolation = false; - return requiredIsolation; + requiredIsolationLoc = SourceLoc(); + return requiredIsolation[getDeclContext()]; } /// Searches the applyStack from back to front for the inner-most CallExpr diff --git a/test/Concurrency/isolated_default_arguments.swift b/test/Concurrency/isolated_default_arguments.swift index 6cb05ab3ab873..6617e0e785836 100644 --- a/test/Concurrency/isolated_default_arguments.swift +++ b/test/Concurrency/isolated_default_arguments.swift @@ -13,10 +13,10 @@ actor SomeGlobalActor { static let shared = SomeGlobalActor() } +// expected-note@+2 2 {{calls to global function 'requiresMainActor()' from outside of its actor context are implicitly asynchronous}} @MainActor func requiresMainActor() -> Int { 0 } -// expected-note@+2 2 {{calls to global function 'requiresSomeGlobalActor()' from outside of its actor context are implicitly asynchronous}} @SomeGlobalActor func requiresSomeGlobalActor() -> Int { 0 } @@ -77,7 +77,7 @@ func nonisolatedAsyncCaller() async { } func conflictingIsolationDefaultArg( - // expected-error@+1 {{call to global actor 'SomeGlobalActor'-isolated global function 'requiresSomeGlobalActor()' in a synchronous nonisolated context}} + // expected-error@+1 {{default argument cannot be both main actor-isolated and global actor 'SomeGlobalActor'-isolated}} value: (Int, Int) = (requiresMainActor(), requiresSomeGlobalActor()) ) {} @@ -100,9 +100,27 @@ func closureIsolationMismatch( ) {} func conflictingClosureIsolation( + // expected-error@+1 {{default argument cannot be both main actor-isolated and global actor 'SomeGlobalActor'-isolated}} closure: () -> Void = { _ = requiresMainActor() - // expected-error@+1 {{call to global actor 'SomeGlobalActor'-isolated global function 'requiresSomeGlobalActor()' in a synchronous nonisolated context}} _ = requiresSomeGlobalActor() } ) {} + +func isolationInLocalFunction( + closure: () -> Void = { + nonisolated func local() -> Int { + // expected-error@+1 {{call to main actor-isolated global function 'requiresMainActor()' in a synchronous nonisolated context}} + requiresMainActor() + } + } +) {} + +actor A {} + +func closureWithIsolatedParam( + closure: (isolated A) -> Void = { _ in + // expected-error@+1 {{call to main actor-isolated global function 'requiresMainActor()' in a synchronous actor-isolated context}} + _ = requiresMainActor() + } +) {} From 7f119474acb1888a0c1c21d5a2c16077518db5af Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Thu, 28 Sep 2023 21:37:59 -0700 Subject: [PATCH 3/5] [Concurrency] Allow default initializer expressions for stored instance properties to require actor isolation. Member initializer expressions are only used in a constructor with matching actor isolation. If the isolation prohibits the member initializer from being evaluated synchronously (or propagating required isolation through closure bodies), then the default value cannot be used and the member must be explicitly initialized in the constructor. Member initializer expressions are also used as default arguments for the memberwise initializer, and the same rules for default argument isolation apply. --- include/swift/AST/Decl.h | 13 +++++ include/swift/AST/TypeCheckRequests.h | 14 +++-- include/swift/AST/TypeCheckerTypeIDZone.def | 5 +- lib/AST/Decl.cpp | 55 +++++++++++++++++- lib/AST/TypeCheckRequests.cpp | 18 ++++++ lib/SILGen/SILGenConstructor.cpp | 20 +++++++ lib/Sema/TypeCheckConcurrency.cpp | 5 -- lib/Sema/TypeCheckConcurrency.h | 1 - lib/Sema/TypeCheckDeclPrimary.cpp | 13 ++--- lib/Sema/TypeCheckStorage.cpp | 4 +- .../isolated_default_arguments.swift | 56 ++++++++++++++++++ .../isolated_default_property_inits.swift | 57 +++++++++++++++++++ 12 files changed, 237 insertions(+), 24 deletions(-) create mode 100644 test/Concurrency/isolated_default_property_inits.swift diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 26f89d1462bf8..43ced66a787f4 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -1973,6 +1973,15 @@ class PatternBindingEntry { SourceRange getOriginalInitRange() const; void setInit(Expr *E); + /// Get the required actor isolation for evaluating the initializer + /// expression synchronously (if there is one). + /// + /// If this pattern binding entry is for a stored instance property, the + /// initializer can only be used in an `init` that meets the required + /// isolation; otherwise, the property must be explicitly initialized in + /// the `init`. + ActorIsolation getInitializerIsolation() const; + /// Gets the text of the initializer expression, stripping out inactive /// branches of any #ifs inside the expression. StringRef getInitStringRepresentation(SmallVectorImpl &scratch) const; @@ -2226,6 +2235,10 @@ class PatternBindingDecl final : public Decl, getMutablePatternList()[i].setOriginalInit(E); } + ActorIsolation getInitializerIsolation(unsigned i) const { + return getPatternList()[i].getInitializerIsolation(); + } + Pattern *getPattern(unsigned i) const { return getPatternList()[i].getPattern(); } diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 63fdb44eba8f4..93b67ab61aa48 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -2935,10 +2935,10 @@ class DefaultArgumentTypeRequest }; /// Compute the actor isolation needed to synchronously evaluate the -/// default argument for the given parameter. -class DefaultArgumentIsolation - : public SimpleRequest { public: using SimpleRequest::SimpleRequest; @@ -2946,12 +2946,16 @@ class DefaultArgumentIsolation private: friend SimpleRequest; - ActorIsolation evaluate(Evaluator &evaluator, ParamDecl *param) const; + ActorIsolation evaluate(Evaluator &evaluator, + Initializer *init, + Expr *initExpr) const; public: bool isCached() const { return true; } }; +void simple_display(llvm::raw_ostream &out, Initializer *init); + /// Computes the fully type-checked caller-side default argument within the /// context of the call site that it will be inserted into. class CallerSideDefaultArgExprRequest diff --git a/include/swift/AST/TypeCheckerTypeIDZone.def b/include/swift/AST/TypeCheckerTypeIDZone.def index 6d34f079efe43..d67a71977eb23 100644 --- a/include/swift/AST/TypeCheckerTypeIDZone.def +++ b/include/swift/AST/TypeCheckerTypeIDZone.def @@ -62,8 +62,9 @@ SWIFT_REQUEST(TypeChecker, DefaultArgumentExprRequest, Expr *(ParamDecl *), SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, DefaultArgumentTypeRequest, Type(ParamDecl *), SeparatelyCached, NoLocationInfo) -SWIFT_REQUEST(TypeChecker, DefaultArgumentIsolation, - ActorIsolation(ParamDecl *), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, DefaultInitializerIsolation, + ActorIsolation(Initializer *, Expr *), + Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, DefaultArgumentInitContextRequest, Initializer *(ParamDecl *), SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, DefaultDefinitionTypeRequest, diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index b08f595cca36f..74ba6a5c01fc1 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -1976,6 +1976,20 @@ void PatternBindingEntry::setInit(Expr *E) { PatternFlags::IsText); } +ActorIsolation PatternBindingEntry::getInitializerIsolation() const { + if (!isInitialized()) + return ActorIsolation::forUnspecified(); + + auto *dc = cast(getInitContext()); + auto &ctx = dc->getASTContext(); + auto *initExpr = getExecutableInit(); + + return evaluateOrDefault( + ctx.evaluator, + DefaultInitializerIsolation{dc, initExpr}, + ActorIsolation::forNonisolated()); +} + VarDecl *PatternBindingEntry::getAnchoringVarDecl() const { SmallVector variables; getPattern()->collectVariables(variables); @@ -8264,10 +8278,30 @@ Type ParamDecl::getTypeOfDefaultExpr() const { } ActorIsolation ParamDecl::getDefaultArgumentIsolation() const { + // If this parameter corresponds to a stored property for a + // memberwise initializer, the default argument is the default + // initializer expression. + auto *var = getStoredProperty(); + if (var && !var->isInvalid()) { + // FIXME: Force computation of property wrapper initializers. + if (auto *wrapped = var->getOriginalWrappedProperty()) + (void)wrapped->getPropertyWrapperInitializerInfo(); + + auto *pbd = var->getParentPatternBinding(); + auto i = pbd->getPatternEntryIndexForVarDecl(var); + return var->getParentPatternBinding()->getInitializerIsolation(i); + } + + if (!hasDefaultExpr()) + return ActorIsolation::forUnspecified(); + auto &ctx = getASTContext(); + auto *dc = getDefaultArgumentInitContext(); + auto *initExpr = getTypeCheckedDefaultExpr(); + return evaluateOrDefault( ctx.evaluator, - DefaultArgumentIsolation{const_cast(this)}, + DefaultInitializerIsolation{dc, initExpr}, ActorIsolation::forNonisolated()); } @@ -10453,8 +10487,25 @@ ActorIsolation swift::getActorIsolationOfContext( if (auto *vd = dyn_cast_or_null(dcToUse->getAsDecl())) return getActorIsolation(vd); - if (auto *var = dcToUse->getNonLocalVarDecl()) + // In the context of the initializing or default-value expression of a + // stored property, the isolation varies between instance and type members: + // - For a static stored property, the isolation matches the VarDecl. + // Static properties are initialized upon first use, so the isolation + // of the initializer must match the isolation required to access the + // property. + // - For a field of a nominal type, the expression can require a specific + // actor isolation. That default expression may only be used from inits + // that meet the required isolation. + if (auto *var = dcToUse->getNonLocalVarDecl()) { + auto &ctx = dc->getASTContext(); + if (ctx.LangOpts.hasFeature(Feature::IsolatedDefaultArguments) && + var->isInstanceMember() && + !var->getAttrs().hasAttribute()) { + return ActorIsolation::forNonisolated(); + } + return getActorIsolation(var); + } if (auto *closure = dyn_cast(dcToUse)) { return getClosureActorIsolation(closure); diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index aac2824c41d00..9660506ece0bf 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -1302,6 +1302,24 @@ void DefaultArgumentTypeRequest::cacheResult(Type type) const { param->setDefaultExprType(type); } +//----------------------------------------------------------------------------// +// DefaultInitializerIsolation computation. +//----------------------------------------------------------------------------// + +void swift::simple_display(llvm::raw_ostream &out, Initializer *init) { + switch (init->getInitializerKind()) { + case InitializerKind::PatternBinding: + out << "pattern binding initializer"; + break; + case InitializerKind::DefaultArgument: + out << "default argument initializer"; + break; + case InitializerKind::PropertyWrapper: + out << "property wrapper initializer"; + break; + } +} + //----------------------------------------------------------------------------// // CallerSideDefaultArgExprRequest computation. //----------------------------------------------------------------------------// diff --git a/lib/SILGen/SILGenConstructor.cpp b/lib/SILGen/SILGenConstructor.cpp index cb6fa6f7a098f..825a7f5afb55f 100644 --- a/lib/SILGen/SILGenConstructor.cpp +++ b/lib/SILGen/SILGenConstructor.cpp @@ -1542,6 +1542,26 @@ void SILGenFunction::emitMemberInitializer(DeclContext *dc, VarDecl *selfDecl, if (!init) continue; + // Member initializer expressions are only used in a constructor with + // matching actor isolation. If the isolation prohibits the member + // initializer from being evaluated synchronously (or propagating required + // isolation through closure bodies), then the default value cannot be used + // and the member must be explicitly initialized in the constructor. + auto requiredIsolation = field->getInitializerIsolation(i); + auto contextIsolation = getActorIsolationOfContext(dc); + switch (requiredIsolation) { + // 'nonisolated' expressions can be evaluated from anywhere + case ActorIsolation::Unspecified: + case ActorIsolation::Nonisolated: + break; + + case ActorIsolation::GlobalActor: + case ActorIsolation::GlobalActorUnsafe: + case ActorIsolation::ActorInstance: + if (requiredIsolation != contextIsolation) + continue; + } + auto *varPattern = field->getPattern(i); // Cleanup after this initialization. diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index c6e3b09a5f0be..f1a7f24b6f82b 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -3582,11 +3582,6 @@ void swift::checkFunctionActorIsolation(AbstractFunctionDecl *decl) { } } -void swift::checkInitializerActorIsolation(Initializer *init, Expr *expr) { - ActorIsolationChecker checker(init); - expr->walk(checker); -} - ActorIsolation swift::computeRequiredIsolation(Initializer *init, Expr *expr) { ActorIsolationChecker checker(init); diff --git a/lib/Sema/TypeCheckConcurrency.h b/lib/Sema/TypeCheckConcurrency.h index bfc1f6878a6e5..8df9654fee918 100644 --- a/lib/Sema/TypeCheckConcurrency.h +++ b/lib/Sema/TypeCheckConcurrency.h @@ -56,7 +56,6 @@ void addAsyncNotes(AbstractFunctionDecl const* func); /// Check actor isolation rules. void checkTopLevelActorIsolation(TopLevelCodeDecl *decl); void checkFunctionActorIsolation(AbstractFunctionDecl *decl); -void checkInitializerActorIsolation(Initializer *init, Expr *expr); void checkEnumElementActorIsolation(EnumElementDecl *element, Expr *expr); void checkPropertyWrapperActorIsolation(VarDecl *wrappedVar, Expr *expr); diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index 488def9dde7c2..44c7106881707 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -1157,14 +1157,13 @@ Expr *DefaultArgumentExprRequest::evaluate(Evaluator &evaluator, } ActorIsolation -DefaultArgumentIsolation::evaluate(Evaluator &evaluator, - ParamDecl *param) const { - if (!param->hasDefaultExpr()) +DefaultInitializerIsolation::evaluate(Evaluator &evaluator, + Initializer *init, + Expr *initExpr) const { + if (!init || !initExpr) return ActorIsolation::forUnspecified(); - auto *dc = param->getDefaultArgumentInitContext(); - auto *initExpr = param->getTypeCheckedDefaultExpr(); - return computeRequiredIsolation(dc, initExpr); + return computeRequiredIsolation(init, initExpr); } Type DefaultArgumentTypeRequest::evaluate(Evaluator &evaluator, @@ -2477,7 +2476,7 @@ class DeclChecker : public DeclVisitor { PBD->getInitContext(i)); if (initContext) { TypeChecker::contextualizeInitializer(initContext, init); - checkInitializerActorIsolation(initContext, init); + (void)PBD->getInitializerIsolation(i); TypeChecker::checkInitializerEffects(initContext, init); } } diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index d5ed89f6b428b..429cd3ff05179 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -3226,7 +3226,7 @@ PropertyWrapperInitializerInfoRequest::evaluate(Evaluator &evaluator, // synthesize a computed property for '$foo'. Expr *projectedValueInit = nullptr; if (auto *projection = var->getPropertyWrapperProjectionVar()) { - createPBD(projection); + auto *pbd = createPBD(projection); if (var->hasExternalPropertyWrapper()) { // Projected-value initialization is currently only supported for parameters. @@ -3240,7 +3240,7 @@ PropertyWrapperInitializerInfoRequest::evaluate(Evaluator &evaluator, // Check initializer effects. auto *initContext = new (ctx) PropertyWrapperInitializer( dc, param, PropertyWrapperInitializer::Kind::ProjectedValue); - checkInitializerActorIsolation(initContext, projectedValueInit); + (void)pbd->getInitializerIsolation(0); TypeChecker::checkInitializerEffects(initContext, projectedValueInit); } } diff --git a/test/Concurrency/isolated_default_arguments.swift b/test/Concurrency/isolated_default_arguments.swift index 6617e0e785836..c63a4782a3caf 100644 --- a/test/Concurrency/isolated_default_arguments.swift +++ b/test/Concurrency/isolated_default_arguments.swift @@ -124,3 +124,59 @@ func closureWithIsolatedParam( _ = requiresMainActor() } ) {} + +@MainActor +struct S1 { + var required: Int + + var x: Int = requiresMainActor() + + lazy var y: Int = requiresMainActor() + + static var z: Int = requiresMainActor() +} + +@SomeGlobalActor +struct S2 { + var required: Int + + var x: Int = requiresSomeGlobalActor() + + lazy var y: Int = requiresSomeGlobalActor() + + static var z: Int = requiresSomeGlobalActor() +} + +@MainActor +func initializeFromMainActor() { + _ = S1(required: 10) + + // expected-error@+1 {{global actor 'SomeGlobalActor'-isolated default argument cannot be synchronously evaluated from a main actor-isolated context}} + _ = S2(required: 10) +} + +@SomeGlobalActor +func initializeFromSomeGlobalActor() { + // expected-error@+1 {{main actor-isolated default argument cannot be synchronously evaluated from a global actor 'SomeGlobalActor'-isolated context}} + _ = S1(required: 10) + + _ = S2(required: 10) +} + +func initializeFromNonisolated() { + // expected-error@+1 {{main actor-isolated default argument cannot be synchronously evaluated from a nonisolated context}} + _ = S1(required: 10) + + // expected-error@+1 {{global actor 'SomeGlobalActor'-isolated default argument cannot be synchronously evaluated from a nonisolated context}} + _ = S2(required: 10) +} + +extension A { + func initializeFromActorInstance() { + // expected-error@+1 {{main actor-isolated default argument cannot be synchronously evaluated from a actor-isolated context}} + _ = S1(required: 10) + + // expected-error@+1 {{global actor 'SomeGlobalActor'-isolated default argument cannot be synchronously evaluated from a actor-isolated context}} + _ = S2(required: 10) + } +} diff --git a/test/Concurrency/isolated_default_property_inits.swift b/test/Concurrency/isolated_default_property_inits.swift new file mode 100644 index 0000000000000..aae65d9ee0fbe --- /dev/null +++ b/test/Concurrency/isolated_default_property_inits.swift @@ -0,0 +1,57 @@ +// RUN: %empty-directory(%t) + +// RUN: %target-swift-frontend -emit-module -emit-module-path %t/OtherActors.swiftmodule -module-name OtherActors %S/Inputs/OtherActors.swift -disable-availability-checking + +// RUN: %target-swift-frontend -I %t -disable-availability-checking -strict-concurrency=complete -enable-experimental-feature IsolatedDefaultArguments -parse-as-library -emit-sil -o /dev/null -verify %s +// RUN: %target-swift-frontend -I %t -disable-availability-checking -strict-concurrency=complete -parse-as-library -emit-sil -o /dev/null -verify -enable-experimental-feature IsolatedDefaultArguments -enable-experimental-feature SendNonSendable %s + +// REQUIRES: concurrency +// REQUIRES: asserts + +@globalActor +actor SomeGlobalActor { + static let shared = SomeGlobalActor() +} + +@MainActor +func requiresMainActor() -> Int { 0 } + +@SomeGlobalActor +func requiresSomeGlobalActor() -> Int { 0 } + +struct S1 { + // expected-note@+1 2 {{'self.x' not initialized}} + var x = requiresMainActor() + // expected-note@+1 2 {{'self.y' not initialized}} + var y = requiresSomeGlobalActor() + var z = 10 + + // expected-error@+1 {{return from initializer without initializing all stored properties}} + nonisolated init(a: Int) {} + + // expected-error@+1 {{return from initializer without initializing all stored properties}} + @MainActor init(b: Int) {} + + // expected-error@+1 {{return from initializer without initializing all stored properties}} + @SomeGlobalActor init(c: Int) {} +} + +struct S2 { + var x = requiresMainActor() + var y = requiresSomeGlobalActor() + var z = 10 + + nonisolated init(x: Int, y: Int) { + self.x = x + self.y = y + } + + @MainActor init(y: Int) { + self.y = y + } + + @SomeGlobalActor init(x: Int) { + self.x = x + } +} + From 3cb9bb89bc2b883e560fb67258389b6768c8d186 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Sat, 30 Sep 2023 16:43:48 -0700 Subject: [PATCH 4/5] [Concurrency] Serialize default argument isolation. --- include/swift/AST/Decl.h | 31 +++++------- include/swift/AST/TypeCheckRequests.h | 5 +- include/swift/AST/TypeCheckerTypeIDZone.def | 2 +- lib/AST/Decl.cpp | 50 +++---------------- lib/AST/Expr.cpp | 3 +- lib/SILGen/SILGenConstructor.cpp | 3 +- lib/Sema/TypeCheckDeclPrimary.cpp | 47 ++++++++++++++--- lib/Sema/TypeCheckStorage.cpp | 4 +- lib/Serialization/Deserialization.cpp | 45 ++++++++++++++++- lib/Serialization/ModuleFormat.h | 15 +++++- lib/Serialization/Serialization.cpp | 23 +++++++++ .../Inputs/serialized_default_arguments.swift | 11 ++++ ...solated_default_arguments_serialized.swift | 25 ++++++++++ 13 files changed, 187 insertions(+), 77 deletions(-) create mode 100644 test/Concurrency/Inputs/serialized_default_arguments.swift create mode 100644 test/Concurrency/isolated_default_arguments_serialized.swift diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 43ced66a787f4..011d5664378ac 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -1973,15 +1973,6 @@ class PatternBindingEntry { SourceRange getOriginalInitRange() const; void setInit(Expr *E); - /// Get the required actor isolation for evaluating the initializer - /// expression synchronously (if there is one). - /// - /// If this pattern binding entry is for a stored instance property, the - /// initializer can only be used in an `init` that meets the required - /// isolation; otherwise, the property must be explicitly initialized in - /// the `init`. - ActorIsolation getInitializerIsolation() const; - /// Gets the text of the initializer expression, stripping out inactive /// branches of any #ifs inside the expression. StringRef getInitStringRepresentation(SmallVectorImpl &scratch) const; @@ -2235,10 +2226,6 @@ class PatternBindingDecl final : public Decl, getMutablePatternList()[i].setOriginalInit(E); } - ActorIsolation getInitializerIsolation(unsigned i) const { - return getPatternList()[i].getInitializerIsolation(); - } - Pattern *getPattern(unsigned i) const { return getPatternList()[i].getPattern(); } @@ -5986,6 +5973,19 @@ class VarDecl : public AbstractStorageDecl { return getParentExecutableInitializer() != nullptr; } + /// Get the required actor isolation for evaluating the initializer + /// expression synchronously (if there is one). + /// + /// If this VarDecl is a stored instance property, the initializer + /// can only be used in an `init` that meets the required isolation. + /// Otherwise, the property must be explicitly initialized in the `init`. + /// + /// If this is a ParamDecl, the initializer isolation is required at + /// the call-site in order to use the default argument for this parameter. + /// If the required isolation is not met, an argument must be written + /// explicitly at the call-site. + ActorIsolation getInitializerIsolation() const; + // Return whether this VarDecl has an initial value, either by checking // if it has an initializer in its parent pattern binding or if it has // the @_hasInitialValue attribute. @@ -6428,11 +6428,6 @@ class ParamDecl : public VarDecl { /// at the call site in order to have the correct context information. Expr *getTypeCheckedDefaultExpr() const; - /// The actor isolation required of the caller in order to use the - /// default argument for this parameter. If the required isolation is - /// not met, an argument must be written explicitly at the call-site. - ActorIsolation getDefaultArgumentIsolation() const; - /// Retrieve the potentially un-type-checked default argument expression for /// this parameter, which can be queried for information such as its source /// range and textual representation. Returns \c nullptr if there is no diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 93b67ab61aa48..91a01856dd8c7 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -2938,7 +2938,7 @@ class DefaultArgumentTypeRequest /// default initializer expression. class DefaultInitializerIsolation : public SimpleRequest { public: using SimpleRequest::SimpleRequest; @@ -2947,8 +2947,7 @@ class DefaultInitializerIsolation friend SimpleRequest; ActorIsolation evaluate(Evaluator &evaluator, - Initializer *init, - Expr *initExpr) const; + VarDecl *) const; public: bool isCached() const { return true; } diff --git a/include/swift/AST/TypeCheckerTypeIDZone.def b/include/swift/AST/TypeCheckerTypeIDZone.def index d67a71977eb23..dc3228d61f630 100644 --- a/include/swift/AST/TypeCheckerTypeIDZone.def +++ b/include/swift/AST/TypeCheckerTypeIDZone.def @@ -63,7 +63,7 @@ SWIFT_REQUEST(TypeChecker, DefaultArgumentExprRequest, SWIFT_REQUEST(TypeChecker, DefaultArgumentTypeRequest, Type(ParamDecl *), SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, DefaultInitializerIsolation, - ActorIsolation(Initializer *, Expr *), + ActorIsolation(VarDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, DefaultArgumentInitContextRequest, Initializer *(ParamDecl *), SeparatelyCached, NoLocationInfo) diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 74ba6a5c01fc1..c26ef21e35c8c 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -1976,20 +1976,6 @@ void PatternBindingEntry::setInit(Expr *E) { PatternFlags::IsText); } -ActorIsolation PatternBindingEntry::getInitializerIsolation() const { - if (!isInitialized()) - return ActorIsolation::forUnspecified(); - - auto *dc = cast(getInitContext()); - auto &ctx = dc->getASTContext(); - auto *initExpr = getExecutableInit(); - - return evaluateOrDefault( - ctx.evaluator, - DefaultInitializerIsolation{dc, initExpr}, - ActorIsolation::forNonisolated()); -} - VarDecl *PatternBindingEntry::getAnchoringVarDecl() const { SmallVector variables; getPattern()->collectVariables(variables); @@ -7141,6 +7127,14 @@ Expr *VarDecl::getParentExecutableInitializer() const { return nullptr; } +ActorIsolation VarDecl::getInitializerIsolation() const { + auto *mutableThis = const_cast(this); + return evaluateOrDefault( + getASTContext().evaluator, + DefaultInitializerIsolation{mutableThis}, + ActorIsolation::forUnspecified()); +} + SourceRange VarDecl::getSourceRange() const { if (auto Param = dyn_cast(this)) return Param->getSourceRange(); @@ -8277,34 +8271,6 @@ Type ParamDecl::getTypeOfDefaultExpr() const { return Type(); } -ActorIsolation ParamDecl::getDefaultArgumentIsolation() const { - // If this parameter corresponds to a stored property for a - // memberwise initializer, the default argument is the default - // initializer expression. - auto *var = getStoredProperty(); - if (var && !var->isInvalid()) { - // FIXME: Force computation of property wrapper initializers. - if (auto *wrapped = var->getOriginalWrappedProperty()) - (void)wrapped->getPropertyWrapperInitializerInfo(); - - auto *pbd = var->getParentPatternBinding(); - auto i = pbd->getPatternEntryIndexForVarDecl(var); - return var->getParentPatternBinding()->getInitializerIsolation(i); - } - - if (!hasDefaultExpr()) - return ActorIsolation::forUnspecified(); - - auto &ctx = getASTContext(); - auto *dc = getDefaultArgumentInitContext(); - auto *initExpr = getTypeCheckedDefaultExpr(); - - return evaluateOrDefault( - ctx.evaluator, - DefaultInitializerIsolation{dc, initExpr}, - ActorIsolation::forNonisolated()); -} - void ParamDecl::setDefaultExpr(Expr *E, bool isTypeChecked) { if (!DefaultValueAndFlags.getPointer()) { if (!E) return; diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index e827a8694d264..d854759dcb7bc 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -1608,8 +1608,7 @@ Expr *DefaultArgumentExpr::getCallerSideDefaultExpr() const { ActorIsolation DefaultArgumentExpr::getRequiredIsolation() const { - auto *param = getParamDecl(); - return param->getDefaultArgumentIsolation(); + return getParamDecl()->getInitializerIsolation(); } ValueDecl *ApplyExpr::getCalledValue(bool skipFunctionConversions) const { diff --git a/lib/SILGen/SILGenConstructor.cpp b/lib/SILGen/SILGenConstructor.cpp index 825a7f5afb55f..031b0a5428d8c 100644 --- a/lib/SILGen/SILGenConstructor.cpp +++ b/lib/SILGen/SILGenConstructor.cpp @@ -1547,7 +1547,8 @@ void SILGenFunction::emitMemberInitializer(DeclContext *dc, VarDecl *selfDecl, // initializer from being evaluated synchronously (or propagating required // isolation through closure bodies), then the default value cannot be used // and the member must be explicitly initialized in the constructor. - auto requiredIsolation = field->getInitializerIsolation(i); + auto *var = field->getAnchoringVarDecl(i); + auto requiredIsolation = var->getInitializerIsolation(); auto contextIsolation = getActorIsolationOfContext(dc); switch (requiredIsolation) { // 'nonisolated' expressions can be evaluated from anywhere diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index 44c7106881707..2db67e0867bce 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -1069,7 +1069,7 @@ static void checkDefaultArguments(ParameterList *params) { for (auto *param : *params) { auto ifacety = param->getInterfaceType(); auto *expr = param->getTypeCheckedDefaultExpr(); - (void)param->getDefaultArgumentIsolation(); + (void)param->getInitializerIsolation(); if (!ifacety->hasPlaceholder()) { continue; @@ -1158,12 +1158,43 @@ Expr *DefaultArgumentExprRequest::evaluate(Evaluator &evaluator, ActorIsolation DefaultInitializerIsolation::evaluate(Evaluator &evaluator, - Initializer *init, - Expr *initExpr) const { - if (!init || !initExpr) + VarDecl *var) const { + if (var->isInvalid()) return ActorIsolation::forUnspecified(); - return computeRequiredIsolation(init, initExpr); + Initializer *dc = nullptr; + Expr *initExpr = nullptr; + + if (auto *pbd = var->getParentPatternBinding()) { + if (!var->isParentInitialized()) + return ActorIsolation::forUnspecified(); + + auto i = pbd->getPatternEntryIndexForVarDecl(var); + dc = cast(pbd->getInitContext(i)); + initExpr = var->getParentInitializer(); + } else if (auto *param = dyn_cast(var)) { + // If this parameter corresponds to a stored property for a + // memberwise initializer, the default argument is the default + // initializer expression. + if (auto *property = param->getStoredProperty()) { + // FIXME: Force computation of property wrapper initializers. + if (auto *wrapped = property->getOriginalWrappedProperty()) + (void)property->getPropertyWrapperInitializerInfo(); + + return property->getInitializerIsolation(); + } + + if (!param->hasDefaultExpr()) + return ActorIsolation::forUnspecified(); + + dc = param->getDefaultArgumentInitContext(); + initExpr = param->getTypeCheckedDefaultExpr(); + } + + if (!dc || !initExpr) + return ActorIsolation::forUnspecified(); + + return computeRequiredIsolation(dc, initExpr); } Type DefaultArgumentTypeRequest::evaluate(Evaluator &evaluator, @@ -2476,7 +2507,11 @@ class DeclChecker : public DeclVisitor { PBD->getInitContext(i)); if (initContext) { TypeChecker::contextualizeInitializer(initContext, init); - (void)PBD->getInitializerIsolation(i); + if (auto *singleVar = PBD->getSingleVar()) { + (void)singleVar->getInitializerIsolation(); + } else { + computeRequiredIsolation(initContext, init); + } TypeChecker::checkInitializerEffects(initContext, init); } } diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index 429cd3ff05179..3deddf2eb4533 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -3226,7 +3226,7 @@ PropertyWrapperInitializerInfoRequest::evaluate(Evaluator &evaluator, // synthesize a computed property for '$foo'. Expr *projectedValueInit = nullptr; if (auto *projection = var->getPropertyWrapperProjectionVar()) { - auto *pbd = createPBD(projection); + createPBD(projection); if (var->hasExternalPropertyWrapper()) { // Projected-value initialization is currently only supported for parameters. @@ -3240,7 +3240,7 @@ PropertyWrapperInitializerInfoRequest::evaluate(Evaluator &evaluator, // Check initializer effects. auto *initContext = new (ctx) PropertyWrapperInitializer( dc, param, PropertyWrapperInitializer::Kind::ProjectedValue); - (void)pbd->getInitializerIsolation(0); + (void)projection->getInitializerIsolation(); TypeChecker::checkInitializerEffects(initContext, projectedValueInit); } } diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 76015325ac4ab..40d18e78e43d0 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -455,6 +455,22 @@ getActualDefaultArgKind(uint8_t raw) { return llvm::None; } +static llvm::Optional +getActualActorIsolationKind(uint8_t raw) { + switch (static_cast(raw)) { +#define CASE(X) \ + case serialization::ActorIsolation::X: \ + return swift::ActorIsolation::Kind::X; + CASE(Unspecified) + CASE(ActorInstance) + CASE(Nonisolated) + CASE(GlobalActor) + CASE(GlobalActorUnsafe) +#undef CASE + } + return llvm::None; +} + static llvm::Optional getActualClangDeclPathComponentKind(uint64_t raw) { switch (static_cast(raw)) { @@ -3764,6 +3780,8 @@ class DeclDeserializer { bool isCompileTimeConst; uint8_t rawDefaultArg; TypeID defaultExprType; + uint8_t rawDefaultArgIsolation; + TypeID globalActorTypeID; decls_block::ParamLayout::readRecord(scratch, argNameID, paramNameID, contextID, rawSpecifier, @@ -3771,7 +3789,9 @@ class DeclDeserializer { isAutoClosure, isIsolated, isCompileTimeConst, rawDefaultArg, - defaultExprType); + defaultExprType, + rawDefaultArgIsolation, + globalActorTypeID); auto argName = MF.getIdentifier(argNameID); auto paramName = MF.getIdentifier(paramNameID); @@ -3816,6 +3836,29 @@ class DeclDeserializer { if (auto exprType = MF.getType(defaultExprType)) param->setDefaultExprType(exprType); + auto isoKind = *getActualActorIsolationKind(rawDefaultArgIsolation); + auto globalActor = MF.getType(globalActorTypeID); + ActorIsolation isolation; + switch (isoKind) { + case ActorIsolation::Unspecified: + case ActorIsolation::Nonisolated: + isolation = ActorIsolation::forUnspecified(); + break; + + case ActorIsolation::GlobalActor: + case ActorIsolation::GlobalActorUnsafe: + isolation = ActorIsolation::forGlobalActor( + globalActor, isoKind == ActorIsolation::GlobalActorUnsafe); + break; + + case ActorIsolation::ActorInstance: + llvm_unreachable("default arg cannot be actor instance isolated"); + } + + ctx.evaluator.cacheOutput( + DefaultInitializerIsolation{param}, + std::move(isolation)); + if (!blobData.empty()) param->setDefaultValueStringRepresentation(blobData); } diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index c514bb45259c3..9b58cdf2a8e06 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 810; // access enforcement +const uint16_t SWIFTMODULE_VERSION_MINOR = 811; // default argument isolation /// A standard hash seed used for all string hashes in a serialized module. /// @@ -526,6 +526,17 @@ enum class DefaultArgumentKind : uint8_t { }; using DefaultArgumentField = BCFixed<4>; +/// These IDs must \em not be renumbered or reordered without incrementing +/// the module version. +enum class ActorIsolation : uint8_t { + Unspecified = 0, + ActorInstance, + Nonisolated, + GlobalActor, + GlobalActorUnsafe +}; +using ActorIsolationField = BCFixed<3>; + // These IDs must \em not be renumbered or reordered without incrementing // the module version. enum LibraryKind : uint8_t { @@ -1568,6 +1579,8 @@ namespace decls_block { BCFixed<1>, // isCompileTimeConst? DefaultArgumentField, // default argument kind TypeIDField, // default argument type + ActorIsolationField, // default argument isolation + TypeIDField, // global actor isolation BCBlob // default argument text >; diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index fc4393b45d50c..a1f447d8ed246 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -1392,6 +1392,22 @@ static uint8_t getRawStableDefaultArgumentKind(swift::DefaultArgumentKind kind) llvm_unreachable("Unhandled DefaultArgumentKind in switch."); } +static uint8_t +getRawStableActorIsolationKind(swift::ActorIsolation::Kind kind) { + switch (kind) { +#define CASE(X) \ + case swift::ActorIsolation::X: \ + return static_cast(serialization::ActorIsolation::X); + CASE(Unspecified) + CASE(ActorInstance) + CASE(Nonisolated) + CASE(GlobalActor) + CASE(GlobalActorUnsafe) +#undef CASE + } + llvm_unreachable("bad actor isolation"); +} + static uint8_t getRawStableMetatypeRepresentation(const AnyMetatypeType *metatype) { if (!metatype->hasRepresentation()) { @@ -4304,6 +4320,11 @@ class Serializer::DeclSerializer : public DeclVisitor { defaultExprType = param->getTypeOfDefaultExpr(); } + auto isolation = param->getInitializerIsolation(); + Type globalActorType; + if (isolation.isGlobalActor()) + globalActorType = isolation.getGlobalActor(); + unsigned abbrCode = S.DeclTypeAbbrCodes[ParamLayout::Code]; ParamLayout::emitRecord(S.Out, S.ScratchRecord, abbrCode, S.addDeclBaseNameRef(param->getArgumentName()), @@ -4318,6 +4339,8 @@ class Serializer::DeclSerializer : public DeclVisitor { param->isCompileTimeConst(), getRawStableDefaultArgumentKind(argKind), S.addTypeRef(defaultExprType), + getRawStableActorIsolationKind(isolation.getKind()), + S.addTypeRef(globalActorType), defaultArgumentText); if (interfaceType->hasError() && !S.allowCompilerErrors()) { diff --git a/test/Concurrency/Inputs/serialized_default_arguments.swift b/test/Concurrency/Inputs/serialized_default_arguments.swift new file mode 100644 index 0000000000000..8035135ec78d6 --- /dev/null +++ b/test/Concurrency/Inputs/serialized_default_arguments.swift @@ -0,0 +1,11 @@ + +@MainActor +public func mainActorDefaultArg() -> Int { 0 } + +@MainActor +public func useMainActorDefault(_ value: Int = mainActorDefaultArg()) {} + +public func nonisolatedDefaultArg() -> Int { 0 } + +@MainActor +public func useNonisolatedDefault(_ value: Int = nonisolatedDefaultArg()) {} diff --git a/test/Concurrency/isolated_default_arguments_serialized.swift b/test/Concurrency/isolated_default_arguments_serialized.swift new file mode 100644 index 0000000000000..6f228693e8103 --- /dev/null +++ b/test/Concurrency/isolated_default_arguments_serialized.swift @@ -0,0 +1,25 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module -swift-version 5 -emit-module-path %t/SerializedDefaultArguments.swiftmodule -module-name SerializedDefaultArguments -enable-experimental-feature IsolatedDefaultArguments %S/Inputs/serialized_default_arguments.swift + +// RUN: %target-swift-frontend %s -emit-sil -o /dev/null -verify -disable-availability-checking -swift-version 6 -I %t +// RUN: %target-swift-frontend %s -emit-sil -o /dev/null -verify -disable-availability-checking -swift-version 6 -I %t -enable-experimental-feature SendNonSendable -enable-experimental-feature IsolatedDefaultArguments + +// REQUIRES: concurrency +// REQUIRES: asserts + +import SerializedDefaultArguments + +@MainActor +func mainActorCaller() { + useMainActorDefault() + useNonisolatedDefault() +} + +func nonisolatedCaller() async { + // expected-error@+1 {{main actor-isolated default argument cannot be synchronously evaluated from a nonisolated context}} + await useMainActorDefault() + + await useMainActorDefault(mainActorDefaultArg()) + + await useNonisolatedDefault() +} From 67e3326048bd0bf201ebc33ac9b4a80ab9e6a4fe Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 3 Oct 2023 19:50:29 -0700 Subject: [PATCH 5/5] [Concurrency] Remove swift::computeRequiredIsolation and change all callers to go through the DefaultInitializerIsolation request. --- include/swift/AST/Decl.h | 4 +- lib/AST/Decl.cpp | 8 ++++ lib/Sema/TypeCheckConcurrency.cpp | 48 ++++++++++++++++--- lib/Sema/TypeCheckConcurrency.h | 4 -- lib/Sema/TypeCheckDeclPrimary.cpp | 47 +----------------- .../isolated_default_arguments.swift | 5 ++ 6 files changed, 59 insertions(+), 57 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 011d5664378ac..87631f3000eb9 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -2273,7 +2273,9 @@ class PatternBindingDecl final : public Decl, void setInitializerSubsumed(unsigned i) { getMutablePatternList()[i].setInitializerSubsumed(); } - + + ActorIsolation getInitializerIsolation(unsigned i) const; + /// Does this binding declare something that requires storage? bool hasStorage() const; diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index c26ef21e35c8c..1acd3dcb01216 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -2088,6 +2088,14 @@ bool PatternBindingDecl::isAsyncLet() const { return false; } +ActorIsolation +PatternBindingDecl::getInitializerIsolation(unsigned i) const { + auto *var = getPatternList()[i].getAnchoringVarDecl(); + if (!var) + return ActorIsolation::forUnspecified(); + + return var->getInitializerIsolation(); +} bool PatternBindingDecl::hasStorage() const { // Walk the pattern, to check to see if any of the VarDecls included in it diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index f1a7f24b6f82b..ee603d53c4643 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -3582,12 +3582,6 @@ void swift::checkFunctionActorIsolation(AbstractFunctionDecl *decl) { } } -ActorIsolation -swift::computeRequiredIsolation(Initializer *init, Expr *expr) { - ActorIsolationChecker checker(init); - return checker.computeRequiredIsolation(expr); -} - void swift::checkEnumElementActorIsolation( EnumElementDecl *element, Expr *expr) { ActorIsolationChecker checker(element); @@ -4618,6 +4612,48 @@ bool HasIsolatedSelfRequest::evaluate( return true; } +ActorIsolation +DefaultInitializerIsolation::evaluate(Evaluator &evaluator, + VarDecl *var) const { + if (var->isInvalid()) + return ActorIsolation::forUnspecified(); + + Initializer *dc = nullptr; + Expr *initExpr = nullptr; + + if (auto *pbd = var->getParentPatternBinding()) { + if (!var->isParentInitialized()) + return ActorIsolation::forUnspecified(); + + auto i = pbd->getPatternEntryIndexForVarDecl(var); + dc = cast(pbd->getInitContext(i)); + initExpr = var->getParentInitializer(); + } else if (auto *param = dyn_cast(var)) { + // If this parameter corresponds to a stored property for a + // memberwise initializer, the default argument is the default + // initializer expression. + if (auto *property = param->getStoredProperty()) { + // FIXME: Force computation of property wrapper initializers. + if (auto *wrapped = property->getOriginalWrappedProperty()) + (void)property->getPropertyWrapperInitializerInfo(); + + return property->getInitializerIsolation(); + } + + if (!param->hasDefaultExpr()) + return ActorIsolation::forUnspecified(); + + dc = param->getDefaultArgumentInitContext(); + initExpr = param->getTypeCheckedDefaultExpr(); + } + + if (!dc || !initExpr) + return ActorIsolation::forUnspecified(); + + ActorIsolationChecker checker(dc); + return checker.computeRequiredIsolation(initExpr); +} + void swift::checkOverrideActorIsolation(ValueDecl *value) { if (isa(value)) return; diff --git a/lib/Sema/TypeCheckConcurrency.h b/lib/Sema/TypeCheckConcurrency.h index 8df9654fee918..40a01dde168ad 100644 --- a/lib/Sema/TypeCheckConcurrency.h +++ b/lib/Sema/TypeCheckConcurrency.h @@ -59,10 +59,6 @@ void checkFunctionActorIsolation(AbstractFunctionDecl *decl); void checkEnumElementActorIsolation(EnumElementDecl *element, Expr *expr); void checkPropertyWrapperActorIsolation(VarDecl *wrappedVar, Expr *expr); -/// Compute the actor isolation required in order to evaluate the given -/// initializer expression synchronously. -ActorIsolation computeRequiredIsolation(Initializer *init, Expr *expr); - /// States the reason for checking the Sendability of a given declaration. enum class SendableCheckReason { /// A reference to an actor from outside that actor. diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index 2db67e0867bce..628c846c92be3 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -1156,47 +1156,6 @@ Expr *DefaultArgumentExprRequest::evaluate(Evaluator &evaluator, return initExpr; } -ActorIsolation -DefaultInitializerIsolation::evaluate(Evaluator &evaluator, - VarDecl *var) const { - if (var->isInvalid()) - return ActorIsolation::forUnspecified(); - - Initializer *dc = nullptr; - Expr *initExpr = nullptr; - - if (auto *pbd = var->getParentPatternBinding()) { - if (!var->isParentInitialized()) - return ActorIsolation::forUnspecified(); - - auto i = pbd->getPatternEntryIndexForVarDecl(var); - dc = cast(pbd->getInitContext(i)); - initExpr = var->getParentInitializer(); - } else if (auto *param = dyn_cast(var)) { - // If this parameter corresponds to a stored property for a - // memberwise initializer, the default argument is the default - // initializer expression. - if (auto *property = param->getStoredProperty()) { - // FIXME: Force computation of property wrapper initializers. - if (auto *wrapped = property->getOriginalWrappedProperty()) - (void)property->getPropertyWrapperInitializerInfo(); - - return property->getInitializerIsolation(); - } - - if (!param->hasDefaultExpr()) - return ActorIsolation::forUnspecified(); - - dc = param->getDefaultArgumentInitContext(); - initExpr = param->getTypeCheckedDefaultExpr(); - } - - if (!dc || !initExpr) - return ActorIsolation::forUnspecified(); - - return computeRequiredIsolation(dc, initExpr); -} - Type DefaultArgumentTypeRequest::evaluate(Evaluator &evaluator, ParamDecl *param) const { if (auto *expr = param->getTypeCheckedDefaultExpr()) { @@ -2507,11 +2466,7 @@ class DeclChecker : public DeclVisitor { PBD->getInitContext(i)); if (initContext) { TypeChecker::contextualizeInitializer(initContext, init); - if (auto *singleVar = PBD->getSingleVar()) { - (void)singleVar->getInitializerIsolation(); - } else { - computeRequiredIsolation(initContext, init); - } + (void)PBD->getInitializerIsolation(i); TypeChecker::checkInitializerEffects(initContext, init); } } diff --git a/test/Concurrency/isolated_default_arguments.swift b/test/Concurrency/isolated_default_arguments.swift index c63a4782a3caf..ba092542f06fe 100644 --- a/test/Concurrency/isolated_default_arguments.swift +++ b/test/Concurrency/isolated_default_arguments.swift @@ -147,6 +147,11 @@ struct S2 { static var z: Int = requiresSomeGlobalActor() } +struct S3 { + // expected-error@+1 {{default argument cannot be both main actor-isolated and global actor 'SomeGlobalActor'-isolated}} + var (x, y, z) = (requiresMainActor(), requiresSomeGlobalActor(), 10) +} + @MainActor func initializeFromMainActor() { _ = S1(required: 10)