diff --git a/CHANGELOG.md b/CHANGELOG.md index a593772e306cc..ce379717da366 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,30 @@ > **Note**\ > This is in reverse chronological order, so newer entries are added to the top. +## Swift 5.10 + +* [SE-0411][]: + + Default value expressions can now have the same isolation as the enclosing + function or the corresponding stored property: + + ```swift + @MainActor + func requiresMainActor() -> Int { ... } + + class C { + @MainActor + var x: Int = requiresMainActor() + } + + @MainActor func defaultArg(value: Int = requiresMainActor()) { ... } + ``` + + For isolated default values of stored properties, the implicit initialization + only happens in the body of an `init` with the same isolation. This closes + an important data-race safety hole where global-actor-isolated default values + could inadvertently run synchronously from outside the actor. + ## Swift 5.9.2 * [SE-0407][]: @@ -9846,6 +9870,7 @@ using the `.dynamicType` member to retrieve the type of an expression should mig [SE-0394]: https://github.com/apple/swift-evolution/blob/main/proposals/0394-swiftpm-expression-macros.md [SE-0397]: https://github.com/apple/swift-evolution/blob/main/proposals/0397-freestanding-declaration-macros.md [SE-0407]: https://github.com/apple/swift-evolution/blob/main/proposals/0407-member-macro-conformances.md +[SE-0411]: https://github.com/apple/swift-evolution/blob/main/proposals/0411-isolated-default-values.md [#64927]: [#42697]: [#42728]: diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 7669634ad586d..388a2485a72e7 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -5200,9 +5200,14 @@ 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", +ERROR(conflicting_stored_property_isolation,none, + "%select{default|memberwise}0 initializer for %1 cannot be both %2 and %3", + (bool, Type, ActorIsolation, ActorIsolation)) +NOTE(property_requires_actor,none, + "initializer for %0 %1 is %2", + (DescriptiveDeclKind, Identifier, ActorIsolation)) +ERROR(isolated_default_argument_context,none, + "%0 default value in a %1 context", (ActorIsolation, ActorIsolation)) ERROR(conflicting_default_argument_isolation,none, "default argument cannot be both %0 and %1", diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index 427b60e009219..d5e7f7679544d 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -4513,6 +4513,10 @@ class DefaultArgumentExpr final : public Expr { /// default expression. PointerUnion ContextOrCallerSideExpr; + /// Whether this default argument is evaluated asynchronously because + /// it's isolated to the callee's isolation domain. + bool implicitlyAsync = false; + public: explicit DefaultArgumentExpr(ConcreteDeclRef defaultArgsOwner, unsigned paramIndex, SourceLoc loc, Type Ty, @@ -4547,6 +4551,16 @@ class DefaultArgumentExpr final : public Expr { /// argument must be written explicitly at the call-site. ActorIsolation getRequiredIsolation() const; + /// Whether this default argument is evaluated asynchronously because + /// it's isolated to the callee's isolation domain. + bool isImplicitlyAsync() const { + return implicitlyAsync; + } + + void setImplicitlyAsync() { + implicitlyAsync = true; + } + static bool classof(const Expr *E) { return E->getKind() == ExprKind::DefaultArgument; } diff --git a/include/swift/Basic/Features.def b/include/swift/Basic/Features.def index 8fa0012dd5926..d331828b19980 100644 --- a/include/swift/Basic/Features.def +++ b/include/swift/Basic/Features.def @@ -118,6 +118,7 @@ UPCOMING_FEATURE(BareSlashRegexLiterals, 354, 6) UPCOMING_FEATURE(DeprecateApplicationMain, 383, 6) UPCOMING_FEATURE(ImportObjcForwardDeclarations, 384, 6) UPCOMING_FEATURE(DisableOutwardActorInference, 401, 6) +UPCOMING_FEATURE(IsolatedDefaultValues, 411, 6) UPCOMING_FEATURE(GlobalConcurrency, 412, 6) UPCOMING_FEATURE(ExistentialAny, 335, 7) @@ -222,9 +223,6 @@ EXPERIMENTAL_FEATURE(StrictConcurrency, true) /// Defer Sendable checking to SIL diagnostic phase. EXPERIMENTAL_FEATURE(SendNonSendable, false) -/// Allow default values to require isolation at the call-site. -EXPERIMENTAL_FEATURE(IsolatedDefaultValues, 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/Decl.cpp b/lib/AST/Decl.cpp index 0433982edd34d..c2fc503af5c8c 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -10289,6 +10289,7 @@ ActorIsolation swift::getActorIsolationOfContext( DeclContext *dc, llvm::function_ref getClosureActorIsolation) { + auto &ctx = dc->getASTContext(); auto dcToUse = dc; // Defer bodies share actor isolation of their enclosing context. if (auto FD = dyn_cast(dcToUse)) { @@ -10300,21 +10301,21 @@ ActorIsolation swift::getActorIsolationOfContext( return getActorIsolation(vd); // In the context of the initializing or default-value expression of a - // stored property, the isolation varies between instance and type members: + // stored property: // - 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. + // - For a field of a nominal type, the expression can require the same + // actor isolation as the field itself. 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::IsolatedDefaultValues) && - var->isInstanceMember() && - !var->getAttrs().hasAttribute()) { - return ActorIsolation::forNonisolated(/*unsafe=*/false); - } + // If IsolatedDefaultValues are enabled, treat this context as having + // unspecified isolation. We'll compute the required isolation for + // the initializer and validate that it matches the isolation of the + // var itself in the DefaultInitializerIsolation request. + if (ctx.LangOpts.hasFeature(Feature::IsolatedDefaultValues)) + return ActorIsolation::forUnspecified(); return getActorIsolation(var); } diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 2b1b324409635..f9350ef5c62b5 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -976,8 +976,9 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.StrictConcurrencyLevel = StrictConcurrency::Minimal; } - // StrictConcurrency enables all data-race safety upcoming features. - if (Opts.hasFeature(Feature::StrictConcurrency)) { + // StrictConcurrency::Complete enables all data-race safety features. + if (Opts.StrictConcurrencyLevel == StrictConcurrency::Complete) { + Opts.Features.insert(Feature::IsolatedDefaultValues); Opts.Features.insert(Feature::GlobalConcurrency); } diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index 9cef5734ba062..dc8b799fdc5b9 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -2518,18 +2518,21 @@ class DelayedArgument { AbstractionPattern origResultType; ClaimedParamsRef paramsToEmit; SILFunctionTypeRepresentation functionRepresentation; - + bool implicitlyAsync; + DefaultArgumentStorage(SILLocation loc, ConcreteDeclRef defaultArgsOwner, unsigned destIndex, CanType resultType, AbstractionPattern origResultType, ClaimedParamsRef paramsToEmit, - SILFunctionTypeRepresentation functionRepresentation) + SILFunctionTypeRepresentation functionRepresentation, + bool implicitlyAsync) : loc(loc), defaultArgsOwner(defaultArgsOwner), destIndex(destIndex), resultType(resultType), origResultType(origResultType), paramsToEmit(paramsToEmit), - functionRepresentation(functionRepresentation) + functionRepresentation(functionRepresentation), + implicitlyAsync(implicitlyAsync) {} }; struct BorrowedLValueStorage { @@ -2656,13 +2659,15 @@ class DelayedArgument { CanType resultType, AbstractionPattern origResultType, ClaimedParamsRef params, - SILFunctionTypeRepresentation functionTypeRepresentation) + SILFunctionTypeRepresentation functionTypeRepresentation, + bool implicitlyAsync) : Kind(DefaultArgument) { Value.emplace(Kind, loc, defaultArgsOwner, destIndex, resultType, origResultType, params, - functionTypeRepresentation); + functionTypeRepresentation, + implicitlyAsync); } DelayedArgument(DelayedArgument &&other) @@ -2690,6 +2695,28 @@ class DelayedArgument { return LV().Loc; } + bool isDefaultArg() const { + return Kind == DefaultArgument; + } + + SILLocation getDefaultArgLoc() const { + assert(isDefaultArg()); + auto storage = Value.get(Kind); + return storage.loc; + } + + llvm::Optional getIsolation() const { + if (!isDefaultArg()) + return llvm::None; + + auto storage = Value.get(Kind); + if (!storage.implicitlyAsync) + return llvm::None; + + auto callee = storage.defaultArgsOwner.getDecl(); + return getActorIsolation(callee); + } + void emit(SILGenFunction &SGF, SmallVectorImpl &args, size_t &argIndex) { switch (Kind) { @@ -2915,6 +2942,31 @@ static void emitDelayedArguments(SILGenFunction &SGF, MutableArrayRef> args) { assert(!delayedArgs.empty()); + // If any of the delayed arguments are isolated default arguments, + // argument evaluation happens in the following order: + // + // 1. Left-to-right evalution of explicit r-value arguments + // 2. Left-to-right evaluation of formal access arguments + // 3. Hop to the callee's isolation domain + // 4. Left-to-right evaluation of default arguments + + // So, if any delayed arguments are isolated, all default arguments + // are collected during the first pass over the delayed arguments, + // and emitted separately after a hop to the callee's isolation domain. + + llvm::Optional defaultArgIsolation; + for (auto &arg : delayedArgs) { + if (auto isolation = arg.getIsolation()) { + defaultArgIsolation = isolation; + break; + } + } + + SmallVector, 2> isolatedArgs; + SmallVector, 4> emittedInoutArgs; auto delayedNext = delayedArgs.begin(); @@ -2923,7 +2975,8 @@ static void emitDelayedArguments(SILGenFunction &SGF, // wherever there's a delayed argument to insert. // // Note that this also begins the formal accesses in evaluation order. - for (auto &siteArgs : args) { + for (auto argsIt = args.begin(); argsIt != args.end(); ++argsIt) { + auto &siteArgs = *argsIt; // NB: siteArgs.size() may change during iteration for (size_t i = 0; i < siteArgs.size(); ) { auto &siteArg = siteArgs[i]; @@ -2936,6 +2989,15 @@ static void emitDelayedArguments(SILGenFunction &SGF, assert(delayedNext != delayedArgs.end()); auto &delayedArg = *delayedNext; + if (defaultArgIsolation && delayedArg.isDefaultArg()) { + isolatedArgs.push_back(std::make_tuple(delayedNext, argsIt, i)); + if (++delayedNext == delayedArgs.end()) { + goto done; + } else { + continue; + } + } + // Emit the delayed argument and replace it in the arguments array. delayedArg.emit(SGF, siteArgs, i); @@ -2956,6 +3018,45 @@ static void emitDelayedArguments(SILGenFunction &SGF, done: + if (defaultArgIsolation) { + assert(SGF.F.isAsync()); + assert(!isolatedArgs.empty()); + + auto &firstArg = *std::get<0>(isolatedArgs[0]); + auto loc = firstArg.getDefaultArgLoc(); + + SILValue executor; + switch (*defaultArgIsolation) { + case ActorIsolation::GlobalActor: + case ActorIsolation::GlobalActorUnsafe: + executor = SGF.emitLoadGlobalActorExecutor( + defaultArgIsolation->getGlobalActor()); + break; + + case ActorIsolation::ActorInstance: + llvm_unreachable("default arg cannot be actor instance isolated"); + + case ActorIsolation::Unspecified: + case ActorIsolation::Nonisolated: + case ActorIsolation::NonisolatedUnsafe: + llvm_unreachable("Not isolated"); + } + + // Hop to the target isolation domain once to evaluate all + // default arguments. + SGF.emitHopToTargetExecutor(loc, executor); + + size_t argsEmitted = 0; + for (auto &isolatedArg : isolatedArgs) { + auto &delayedArg = *std::get<0>(isolatedArg); + auto &siteArgs = *std::get<1>(isolatedArg); + auto argIndex = std::get<2>(isolatedArg) + argsEmitted; + auto origIndex = argIndex; + delayedArg.emit(SGF, siteArgs, argIndex); + argsEmitted += (argIndex - origIndex); + } + } + // Check to see if we have multiple inout arguments which obviously // alias. Note that we could do this in a later SILDiagnostics pass // as well: this would be stronger (more equivalences exposed) but @@ -3261,7 +3362,7 @@ class ArgEmitter { defArg->getParamIndex(), substParamType, origParamType, claimNextParameters(numParams), - Rep); + Rep, defArg->isImplicitlyAsync()); Args.push_back(ManagedValue()); maybeEmitForeignArgument(); @@ -4255,7 +4356,8 @@ void DelayedArgument::emitDefaultArgument(SILGenFunction &SGF, auto value = SGF.emitApplyOfDefaultArgGenerator(info.loc, info.defaultArgsOwner, info.destIndex, - info.resultType); + info.resultType, + info.implicitlyAsync); SmallVector loweredArgs; SmallVector delayedArgs; diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 215db002f394c..7d4c1224002ab 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -2533,6 +2533,7 @@ SILGenFunction::emitApplyOfDefaultArgGenerator(SILLocation loc, ConcreteDeclRef defaultArgsOwner, unsigned destIndex, CanType resultType, + bool implicitlyAsync, SGFContext C) { SILDeclRef generator = SILDeclRef::getDefaultArgGenerator(defaultArgsOwner.getDecl(), diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index 77520d513c216..6f6eae404afc1 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -1905,6 +1905,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction ConcreteDeclRef defaultArgsOwner, unsigned destIndex, CanType resultType, + bool implicitlyAsync, SGFContext C = SGFContext()); RValue emitApplyOfStoredPropertyInitializer( diff --git a/lib/Sema/CodeSynthesis.cpp b/lib/Sema/CodeSynthesis.cpp index 5952c81e808b1..ce558b1f65b2e 100644 --- a/lib/Sema/CodeSynthesis.cpp +++ b/lib/Sema/CodeSynthesis.cpp @@ -336,9 +336,68 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl, ctor->setSynthesized(); ctor->setAccess(accessLevel); + if (ctx.LangOpts.hasFeature(Feature::IsolatedDefaultValues) && + !decl->isActor()) { + // If any of the type's actor-isolated properties: + // 1. Have non-Sendable type, or + // 2. Have an isolated initial value + // then the initializer must also be actor-isolated. If all + // isolated properties have Sendable type and a nonisolated + // default value, then the initializer can be nonisolated. + // + // These rules only apply for global actor isolation, because actor + // initializers apply Sendable checking to arguments at the call-site, + // and actor initializers do not run on the actor, so initial values + // cannot be actor-instance-isolated. + bool shouldAddNonisolated = true; + llvm::Optional existingIsolation = llvm::None; + VarDecl *previousVar = nullptr; + + // The memberwise init properties are also effectively what the + // default init uses, e.g. default initializers initialize via + // properties wrapped and init accessors. + for (auto var : decl->getMemberwiseInitProperties()) { + auto type = var->getTypeInContext(); + auto isolation = getActorIsolation(var); + if (isolation.isGlobalActor()) { + if (!isSendableType(decl->getModuleContext(), type) || + var->getInitializerIsolation().isGlobalActor()) { + // If different isolated stored properties require different + // global actors, it is impossible to initialize this type. + if (existingIsolation && + *existingIsolation != isolation) { + ctx.Diags.diagnose(decl->getLoc(), + diag::conflicting_stored_property_isolation, + ICK == ImplicitConstructorKind::Memberwise, + decl->getDeclaredType(), *existingIsolation, isolation); + previousVar->diagnose( + diag::property_requires_actor, + previousVar->getDescriptiveKind(), + previousVar->getName(), *existingIsolation); + var->diagnose( + diag::property_requires_actor, + var->getDescriptiveKind(), + var->getName(), isolation); + } + + existingIsolation = isolation; + previousVar = var; + shouldAddNonisolated = false; + } + } + } + + if (shouldAddNonisolated) { + addNonIsolatedToSynthesized(decl, ctor); + } + } + if (ICK == ImplicitConstructorKind::Memberwise) { ctor->setIsMemberwiseInitializer(); - addNonIsolatedToSynthesized(decl, ctor); + + if (!ctx.LangOpts.hasFeature(Feature::IsolatedDefaultValues)) { + addNonIsolatedToSynthesized(decl, ctor); + } } // If we are defining a default initializer for a class that has a superclass, diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index a247978b863d0..04537e638d978 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -6636,7 +6636,8 @@ void AttributeChecker::visitNonisolatedAttr(NonisolatedAttr *attr) { // backing storage is a stored 'var' that is part of the internal state // of the actor which could only be accessed in actor's isolation context. if (var->hasAttachedPropertyWrapper()) { - diagnoseAndRemoveAttr(attr, diag::nonisolated_wrapped_property); + diagnoseAndRemoveAttr(attr, diag::nonisolated_wrapped_property) + .warnUntilSwiftVersionIf(attr->isImplicit(), 6); return; } diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 5ff4ea4135b69..cbf4ce670846d 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -2001,6 +2001,16 @@ bool swift::diagnoseApplyArgSendability(ApplyExpr *apply, const DeclContext *dec if (Expr *argExpr = arg.getExpr()) { checkSendable = shouldCheckSendable(argExpr); argType = argExpr->findOriginalType(); + + // If this is a default argument expression, don't check Sendability + // if the argument is evaluated in the callee's isolation domain. + if (auto *defaultExpr = dyn_cast(argExpr)) { + auto argIsolation = defaultExpr->getRequiredIsolation(); + auto calleeIsolation = isolationCrossing->getCalleeIsolation(); + if (argIsolation == calleeIsolation) { + continue; + } + } } } @@ -2260,8 +2270,10 @@ namespace { // recieve isolation from its decl context), then the expression cannot // require a different isolation. for (auto *dc : contextStack) { - if (!infersIsolationFromContext(dc)) + if (!infersIsolationFromContext(dc)) { + requiredIsolation.clear(); return false; + } // To refine the required isolation, the existing requirement // must either be 'nonisolated' or exactly the same as the @@ -2275,6 +2287,7 @@ namespace { requiredIsolationLoc, diag::conflicting_default_argument_isolation, isolation->second, refinedIsolation); + requiredIsolation.clear(); return true; } } @@ -2285,8 +2298,8 @@ namespace { 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'. + // argument must be evaluated asynchronously, record that in the + // expression node. auto requiredIsolation = expr->getRequiredIsolation(); auto contextIsolation = getInnermostIsolatedContext( getDeclContext(), getClosureActorIsolation); @@ -2308,10 +2321,7 @@ namespace { break; } - auto &ctx = getDeclContext()->getASTContext(); - ctx.Diags.diagnose( - expr->getLoc(), diag::isolated_default_argument, - requiredIsolation, contextIsolation); + expr->setImplicitlyAsync(); } /// Check closure captures for Sendable violations. @@ -3178,7 +3188,10 @@ namespace { if (!unsatisfiedIsolation) return false; - if (refineRequiredIsolation(*unsatisfiedIsolation)) + bool onlyArgsCrossIsolation = callOptions.contains( + ActorReferenceResult::Flags::OnlyArgsCrossIsolation); + if (!onlyArgsCrossIsolation && + refineRequiredIsolation(*unsatisfiedIsolation)) return false; // At this point, we know a jump is made to the callee that yields @@ -4798,14 +4811,19 @@ DefaultInitializerIsolation::evaluate(Evaluator &evaluator, Initializer *dc = nullptr; Expr *initExpr = nullptr; + ActorIsolation enclosingIsolation; if (auto *pbd = var->getParentPatternBinding()) { if (!var->isParentInitialized()) return ActorIsolation::forUnspecified(); auto i = pbd->getPatternEntryIndexForVarDecl(var); + if (!pbd->isInitializerChecked(i)) + TypeChecker::typeCheckPatternBinding(pbd, i); + dc = cast(pbd->getInitContext(i)); - initExpr = var->getParentInitializer(); + initExpr = pbd->getInit(i); + enclosingIsolation = getActorIsolation(var); } 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 @@ -4823,13 +4841,27 @@ DefaultInitializerIsolation::evaluate(Evaluator &evaluator, dc = param->getDefaultArgumentInitContext(); initExpr = param->getTypeCheckedDefaultExpr(); + enclosingIsolation = + getActorIsolationOfContext(param->getDeclContext()); } if (!dc || !initExpr) return ActorIsolation::forUnspecified(); + // If the default argument has isolation, it must match the + // isolation of the decl context. ActorIsolationChecker checker(dc); - return checker.computeRequiredIsolation(initExpr); + auto requiredIsolation = checker.computeRequiredIsolation(initExpr); + if (requiredIsolation.isActorIsolated()) { + if (enclosingIsolation != requiredIsolation) { + var->diagnose( + diag::isolated_default_argument_context, + requiredIsolation, enclosingIsolation); + return ActorIsolation::forUnspecified(); + } + } + + return requiredIsolation; } void swift::checkOverrideActorIsolation(ValueDecl *value) { @@ -5945,8 +5977,8 @@ bool swift::isAccessibleAcrossActors( } ActorReferenceResult ActorReferenceResult::forSameConcurrencyDomain( - ActorIsolation isolation) { - return ActorReferenceResult{SameConcurrencyDomain, llvm::None, isolation}; + ActorIsolation isolation, Options options) { + return ActorReferenceResult{SameConcurrencyDomain, options, isolation}; } ActorReferenceResult ActorReferenceResult::forEntersActor( @@ -5955,8 +5987,8 @@ ActorReferenceResult ActorReferenceResult::forEntersActor( } ActorReferenceResult ActorReferenceResult::forExitsActorToNonisolated( - ActorIsolation isolation) { - return ActorReferenceResult{ExitsActorToNonisolated, llvm::None, isolation}; + ActorIsolation isolation, Options options) { + return ActorReferenceResult{ExitsActorToNonisolated, options, isolation}; } // Determine if two actor isolation contexts are considered to be equivalent. @@ -5992,10 +6024,24 @@ ActorReferenceResult ActorReferenceResult::forReference( declIsolation = declIsolation.subst(declRef.getSubstitutions()); } + // Determine what adjustments we need to perform for cross-actor + // references. + Options options = llvm::None; + + // FIXME: Actor constructors are modeled as isolated to the actor + // so that Sendable checking is applied to their arguments, but the + // call itself does not hop to another executor. + if (auto ctor = dyn_cast(declRef.getDecl())) { + if (auto nominal = ctor->getDeclContext()->getSelfNominalTypeDecl()) { + if (nominal->isAnyActor()) + options |= Flags::OnlyArgsCrossIsolation; + } + } + // If the entity we are referencing is not a value, we're in the same // concurrency domain. if (isNonValueReference(declRef.getDecl())) - return forSameConcurrencyDomain(declIsolation); + return forSameConcurrencyDomain(declIsolation, options); // Compute the isolation of the context, if not provided. ActorIsolation contextIsolation = ActorIsolation::forUnspecified(); @@ -6014,11 +6060,11 @@ ActorReferenceResult ActorReferenceResult::forReference( if (isAsyncDecl(declRef) && contextIsolation.isActorIsolated() && !declRef.getDecl()->getAttrs() .hasAttribute()) - return forExitsActorToNonisolated(contextIsolation); + return forExitsActorToNonisolated(contextIsolation, options); // Otherwise, we stay in the same concurrency domain, whether on an actor // or in a task. - return forSameConcurrencyDomain(declIsolation); + return forSameConcurrencyDomain(declIsolation, options); } // The declaration we are accessing is actor-isolated. First, check whether @@ -6027,11 +6073,11 @@ ActorReferenceResult ActorReferenceResult::forReference( declIsolation.getActorInstanceParameter() == 0) { // If this instance is isolated, we're in the same concurrency domain. if (actorInstance->isIsolated()) - return forSameConcurrencyDomain(declIsolation); + return forSameConcurrencyDomain(declIsolation, options); } else if (equivalentIsolationContexts(declIsolation, contextIsolation)) { // The context isolation matches, so we are in the same concurrency // domain. - return forSameConcurrencyDomain(declIsolation); + return forSameConcurrencyDomain(declIsolation, options); } // Initializing an actor isolated stored property with a value effectively @@ -6051,7 +6097,8 @@ ActorReferenceResult ActorReferenceResult::forReference( // Treat the decl isolation as 'preconcurrency' to downgrade violations // to warnings, because violating Sendable here is accepted by the // Swift 5.9 compiler. - return forEntersActor(declIsolation, Flags::Preconcurrency); + options |= Flags::Preconcurrency; + return forEntersActor(declIsolation, options); } } } @@ -6061,7 +6108,7 @@ ActorReferenceResult ActorReferenceResult::forReference( if (actorInstance && checkedByFlowIsolation( fromDC, *actorInstance, declRef.getDecl(), declRefLoc, useKind)) - return forSameConcurrencyDomain(declIsolation); + return forSameConcurrencyDomain(declIsolation, options); // If we are delegating to another initializer, treat them as being in the // same concurrency domain. @@ -6070,7 +6117,7 @@ ActorReferenceResult ActorReferenceResult::forReference( if (actorInstance && actorInstance->isSelf() && isa(declRef.getDecl()) && isa(fromDC)) - return forSameConcurrencyDomain(declIsolation); + return forSameConcurrencyDomain(declIsolation, options); // If there is an instance that corresponds to 'self', // we are in a constructor or destructor, and we have a stored property of @@ -6080,18 +6127,14 @@ ActorReferenceResult ActorReferenceResult::forReference( isNonInheritedStorage(declRef.getDecl(), fromDC) && declIsolation.isGlobalActor() && (isa(fromDC) || isa(fromDC))) - return forSameConcurrencyDomain(declIsolation); - - // Determine what adjustments we need to perform for cross-actor - // references. - Options options = llvm::None; + return forSameConcurrencyDomain(declIsolation, options); // At this point, we are accessing the target from outside the actor. // First, check whether it is something that can be accessed directly, // without any kind of promotion. if (isAccessibleAcrossActors( declRef.getDecl(), declIsolation, fromDC, options, actorInstance)) - return forEntersActor(declIsolation, llvm::None); + return forEntersActor(declIsolation, options); // This is a cross-actor reference. diff --git a/lib/Sema/TypeCheckConcurrency.h b/lib/Sema/TypeCheckConcurrency.h index 2f3c0a8b72a6d..a30bd067890bd 100644 --- a/lib/Sema/TypeCheckConcurrency.h +++ b/lib/Sema/TypeCheckConcurrency.h @@ -202,6 +202,10 @@ struct ActorReferenceResult { /// The declaration is being accessed from a @preconcurrency context. Preconcurrency = 1 << 3, + + /// Only arguments cross an isolation boundary, e.g. because they + /// escape into an actor in a nonisolated actor initializer. + OnlyArgsCrossIsolation = 1 << 4, }; using Options = OptionSet; @@ -212,13 +216,13 @@ struct ActorReferenceResult { private: static ActorReferenceResult forSameConcurrencyDomain( - ActorIsolation isolation); + ActorIsolation isolation, Options options = llvm::None); static ActorReferenceResult forEntersActor( ActorIsolation isolation, Options options); static ActorReferenceResult forExitsActorToNonisolated( - ActorIsolation isolation); + ActorIsolation isolation, Options options = llvm::None); public: /// Determine what happens when referencing the given declaration from the diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index efe3e75d7868c..e511c1c74e971 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -1085,6 +1085,9 @@ static void checkDefaultArguments(ParameterList *params) { for (auto *param : *params) { auto ifacety = param->getInterfaceType(); auto *expr = param->getTypeCheckedDefaultExpr(); + + // If the default argument has isolation, it must match the + // isolation of the decl context. (void)param->getInitializerIsolation(); if (!ifacety->hasPlaceholder()) { @@ -2162,6 +2165,7 @@ class DeclChecker : public DeclVisitor { (void) VD->getPropertyWrapperAuxiliaryVariables(); (void) VD->getPropertyWrapperInitializerInfo(); (void) VD->getImplInfo(); + (void) getActorIsolation(VD); // Visit auxiliary decls first VD->visitAuxiliaryDecls([&](VarDecl *var) { diff --git a/test/Concurrency/Inputs/sendable_cycle_other.swift b/test/Concurrency/Inputs/sendable_cycle_other.swift index af86b6b6a9ef0..48fc1b916cdba 100644 --- a/test/Concurrency/Inputs/sendable_cycle_other.swift +++ b/test/Concurrency/Inputs/sendable_cycle_other.swift @@ -1,3 +1,3 @@ struct Foo { - static let member = Bar() + static let member = Bar() // expected-complete-warning {{static property 'member' is not concurrency-safe because it is not either conforming to 'Sendable' or isolated to a global actor; this is an error in Swift 6}} } diff --git a/test/Concurrency/actor_inout_isolation.swift b/test/Concurrency/actor_inout_isolation.swift index 29ee94d5e6217..46e534c69ecef 100644 --- a/test/Concurrency/actor_inout_isolation.swift +++ b/test/Concurrency/actor_inout_isolation.swift @@ -43,7 +43,7 @@ actor TestActor { } @available(SwiftStdlib 5.1, *) -func modifyAsynchronously(_ foo: inout Int) async { foo += 1 } +@Sendable func modifyAsynchronously(_ foo: inout Int) async { foo += 1 } @available(SwiftStdlib 5.1, *) enum Container { static let modifyAsyncValue = modifyAsynchronously @@ -321,4 +321,4 @@ actor ProtectDictionary { // expected-warning@-1 {{cannot call mutating async function 'mutate()' on actor-isolated property 'dict'; this is an error in Swift 6}} // expected-targeted-complete-warning@-2 {{passing argument of non-sendable type 'inout Optional' outside of actor-isolated context may introduce data races}} } -} \ No newline at end of file +} diff --git a/test/Concurrency/actor_isolation.swift b/test/Concurrency/actor_isolation.swift index 5898a48e17bc2..cdfcb539091f8 100644 --- a/test/Concurrency/actor_isolation.swift +++ b/test/Concurrency/actor_isolation.swift @@ -11,6 +11,9 @@ import OtherActors // expected-remark{{add '@preconcurrency' to suppress 'Sendable'-related warnings from module 'OtherActors'}}{{1-1=@preconcurrency }} let immutableGlobal: String = "hello" + +// expected-warning@+2 {{var 'mutableGlobal' is not concurrency-safe because it is non-isolated global shared mutable state; this is an error in Swift 6}} +// expected-note@+1 {{isolate 'mutableGlobal' to a global actor, or convert it to a 'let' constant and conform it to 'Sendable'}} var mutableGlobal: String = "can't touch this" // expected-note 5{{var declared here}} @available(SwiftStdlib 5.1, *) @@ -765,7 +768,6 @@ actor LocalFunctionIsolatedActor { @available(SwiftStdlib 5.1, *) actor LazyActor { var v: Int = 0 - // expected-note@-1 6 {{property declared here}} let l: Int = 0 @@ -773,7 +775,7 @@ actor LazyActor { lazy var l12: Int = v lazy var l13: Int = { self.v }() lazy var l14: Int = self.v - lazy var l15: Int = { [unowned self] in self.v }() // expected-error{{actor-isolated property 'v' can not be referenced from a non-isolated context}} + lazy var l15: Int = { [unowned self] in self.v }() lazy var l21: Int = { l }() lazy var l22: Int = l @@ -782,15 +784,15 @@ actor LazyActor { lazy var l25: Int = { [unowned self] in self.l }() nonisolated lazy var l31: Int = { v }() - // expected-error@-1 {{actor-isolated property 'v' can not be referenced from a non-isolated context}} + // expected-error@-1 {{actor-isolated default value in a nonisolated context}} nonisolated lazy var l32: Int = v - // expected-error@-1 {{actor-isolated property 'v' can not be referenced from a non-isolated context}} + // expected-error@-1 {{actor-isolated default value in a nonisolated context}} nonisolated lazy var l33: Int = { self.v }() - // expected-error@-1 {{actor-isolated property 'v' can not be referenced from a non-isolated context}} + // expected-error@-1 {{actor-isolated default value in a nonisolated context}} nonisolated lazy var l34: Int = self.v - // expected-error@-1 {{actor-isolated property 'v' can not be referenced from a non-isolated context}} + // expected-error@-1 {{actor-isolated default value in a nonisolated context}} nonisolated lazy var l35: Int = { [unowned self] in self.v }() - // expected-error@-1 {{actor-isolated property 'v' can not be referenced from a non-isolated context}} + // expected-error@-1 {{actor-isolated default value in a nonisolated context}} nonisolated lazy var l41: Int = { l }() nonisolated lazy var l42: Int = l diff --git a/test/Concurrency/concurrency_warnings.swift b/test/Concurrency/concurrency_warnings.swift index 2526938a1c23c..00cc7f7c93f50 100644 --- a/test/Concurrency/concurrency_warnings.swift +++ b/test/Concurrency/concurrency_warnings.swift @@ -8,8 +8,12 @@ class GlobalCounter { var counter: Int = 0 } -let rs = GlobalCounter() -var globalInt = 17 // expected-note 2{{var declared here}} +let rs = GlobalCounter() // expected-warning {{let 'rs' is not concurrency-safe because it is not either conforming to 'Sendable' or isolated to a global actor; this is an error in Swift 6}} + +var globalInt = 17 // expected-warning {{var 'globalInt' is not concurrency-safe because it is non-isolated global shared mutable state; this is an error in Swift 6}} +// expected-note@-1 {{isolate 'globalInt' to a global actor, or convert it to a 'let' constant and conform it to 'Sendable'}} +// expected-note@-2 2{{var declared here}} + class MyError: Error { // expected-warning{{non-final class 'MyError' cannot conform to 'Sendable'; use '@unchecked Sendable'}} var storage = 0 // expected-warning{{stored property 'storage' of 'Sendable'-conforming class 'MyError' is mutable}} diff --git a/test/Concurrency/concurrent_value_checking.swift b/test/Concurrency/concurrent_value_checking.swift index 2ddf92d61939c..b3402640704d3 100644 --- a/test/Concurrency/concurrent_value_checking.swift +++ b/test/Concurrency/concurrent_value_checking.swift @@ -271,7 +271,8 @@ typealias CF = @Sendable () -> NotConcurrent? typealias BadGenericCF = @Sendable () -> T? typealias GoodGenericCF = @Sendable () -> T? // okay -var concurrentFuncVar: (@Sendable (NotConcurrent) -> Void)? = nil +var concurrentFuncVar: (@Sendable (NotConcurrent) -> Void)? = nil // expected-warning{{var 'concurrentFuncVar' is not concurrency-safe because it is non-isolated global shared mutable state; this is an error in Swift 6}} +// expected-note@-1 {{isolate 'concurrentFuncVar' to a global actor, or convert it to a 'let' constant and conform it to 'Sendable'}} // ---------------------------------------------------------------------- // Sendable restriction on @Sendable closures. diff --git a/test/Concurrency/flow_isolation.swift b/test/Concurrency/flow_isolation.swift index 9cf3714a79b3c..f4d523a47d53b 100644 --- a/test/Concurrency/flow_isolation.swift +++ b/test/Concurrency/flow_isolation.swift @@ -518,7 +518,9 @@ struct CardboardBox { @available(SwiftStdlib 5.1, *) -var globalVar: EscapeArtist? // expected-note 2 {{var declared here}} +var globalVar: EscapeArtist? // expected-warning {{var 'globalVar' is not concurrency-safe because it is non-isolated global shared mutable state; this is an error in Swift 6}} +// expected-note@-1 {{isolate 'globalVar' to a global actor, or convert it to a 'let' constant and conform it to 'Sendable'}} +// expected-note@-2 2 {{var declared here}} @available(SwiftStdlib 5.1, *) actor EscapeArtist { diff --git a/test/Concurrency/global_actor_inference.swift b/test/Concurrency/global_actor_inference.swift index 5e56050007b5c..14b850d633c3b 100644 --- a/test/Concurrency/global_actor_inference.swift +++ b/test/Concurrency/global_actor_inference.swift @@ -1,10 +1,9 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -emit-module -emit-module-path %t/other_global_actor_inference.swiftmodule -module-name other_global_actor_inference -warn-concurrency %S/Inputs/other_global_actor_inference.swift -// RUN: %target-swift-frontend -I %t -disable-availability-checking %s -emit-sil -o /dev/null -verify -// RUN: %target-swift-frontend -I %t -disable-availability-checking %s -emit-sil -o /dev/null -verify -strict-concurrency=targeted +// RUN: %target-swift-frontend -emit-module -emit-module-path %t/other_global_actor_inference.swiftmodule -module-name other_global_actor_inference -strict-concurrency=complete %S/Inputs/other_global_actor_inference.swift +// RUN: %target-swift-frontend -I %t -disable-availability-checking %s -emit-sil -o /dev/null -verify -verify-additional-prefix minimal-targeted- +// RUN: %target-swift-frontend -I %t -disable-availability-checking %s -emit-sil -o /dev/null -verify -strict-concurrency=targeted -verify-additional-prefix minimal-targeted- // RUN: %target-swift-frontend -I %t -disable-availability-checking %s -emit-sil -o /dev/null -verify -strict-concurrency=complete -verify-additional-prefix complete-sns- -// RUN: %target-swift-frontend -I %t -disable-availability-checking %s -emit-sil -o /dev/null -verify -strict-concurrency=complete -enable-experimental-feature SendNonSendable -verify-additional-prefix complete-sns- // REQUIRES: concurrency // REQUIRES: asserts @@ -390,7 +389,7 @@ struct HasWrapperOnActor { synced = 17 } - @WrapperActor var actorSynced: Int = 0 + @WrapperActor var actorSynced: Int = 0 // expected-warning{{'nonisolated' is not supported on properties with property wrappers}} func testActorSynced() { _ = actorSynced @@ -437,13 +436,15 @@ actor WrapperActorBad2 { struct WrapperWithMainActorDefaultInit { var wrappedValue: Int { fatalError() } - @MainActor init() {} // expected-note 2 {{calls to initializer 'init()' from outside of its actor context are implicitly asynchronous}} + @MainActor init() {} // expected-note {{calls to initializer 'init()' from outside of its actor context are implicitly asynchronous}} + // expected-minimal-targeted-note@-1 {{calls to initializer 'init()' from outside of its actor context are implicitly asynchronous}} } actor ActorWithWrapper { @WrapperOnActor var synced: Int = 0 // expected-note@-1 3{{property declared here}} - @WrapperWithMainActorDefaultInit var property: Int // expected-error {{call to main actor-isolated initializer 'init()' in a synchronous actor-isolated context}} + @WrapperWithMainActorDefaultInit var property: Int // expected-minimal-targeted-error {{call to main actor-isolated initializer 'init()' in a synchronous actor-isolated context}} + // expected-complete-sns-error@-1 {{main actor-isolated default value in a actor-isolated context}} func f() { _ = synced // expected-error{{main actor-isolated property 'synced' can not be referenced on a different actor instance}} _ = $synced // expected-error{{global actor 'SomeGlobalActor'-isolated property '$synced' can not be referenced on a different actor instance}} @@ -489,10 +490,10 @@ struct SimplePropertyWrapper { class WrappedContainsNonisolatedAttr { @SimplePropertyWrapper nonisolated var value // expected-error@-1 {{'nonisolated' is not supported on properties with property wrappers}} - // expected-note@-2 2{{property declared here}} + // expected-note@-2 {{property declared here}} nonisolated func test() { - _ = value // expected-error {{main actor-isolated property 'value' can not be referenced from a non-isolated context}} + _ = value _ = $value // expected-error {{main actor-isolated property '$value' can not be referenced from a non-isolated context}} } } @@ -557,8 +558,9 @@ struct WrapperOnUnsafeActor { } } +// HasWrapperOnUnsafeActor gets an inferred @MainActor attribute. struct HasWrapperOnUnsafeActor { - @WrapperOnUnsafeActor var synced: Int = 0 + @WrapperOnUnsafeActor var synced: Int = 0 // expected-complete-sns-error {{global actor 'OtherGlobalActor'-isolated default value in a main actor-isolated context}} // expected-note @-1 3{{property declared here}} // expected-complete-sns-note @-2 3{{property declared here}} @@ -643,11 +645,11 @@ func acceptAsyncSendableClosureInheriting(@_inheritActorContext _: @Sendable // defer bodies inherit global actor-ness @MainActor -var statefulThingy: Bool = false // expected-note {{var declared here}} +var statefulThingy: Bool = false // expected-minimal-targeted-note {{var declared here}} // expected-complete-sns-error @-1 {{top-level code variables cannot have a global actor}} @MainActor -func useFooInADefer() -> String { // expected-note {{calls to global function 'useFooInADefer()' from outside of its actor context are implicitly asynchronous}} +func useFooInADefer() -> String { // expected-minimal-targeted-note {{calls to global function 'useFooInADefer()' from outside of its actor context are implicitly asynchronous}} defer { statefulThingy = true } @@ -677,9 +679,11 @@ class Cutter { @SomeGlobalActor class Butter { - var a = useFooInADefer() // expected-error {{call to main actor-isolated global function 'useFooInADefer()' in a synchronous global actor 'SomeGlobalActor'-isolated context}} + var a = useFooInADefer() // expected-minimal-targeted-error {{call to main actor-isolated global function 'useFooInADefer()' in a synchronous global actor 'SomeGlobalActor'-isolated context}} + // expected-complete-sns-error@-1 {{main actor-isolated default value in a global actor 'SomeGlobalActor'-isolated context}} - nonisolated let b = statefulThingy // expected-error {{main actor-isolated var 'statefulThingy' can not be referenced from a non-isolated context}} + nonisolated let b = statefulThingy // expected-minimal-targeted-error {{main actor-isolated var 'statefulThingy' can not be referenced from a non-isolated context}} + // expected-complete-sns-error@-1 {{main actor-isolated default value in a nonisolated context}} var c: Int = { return getGlobal7() diff --git a/test/Concurrency/global_actor_inference_swift6.swift b/test/Concurrency/global_actor_inference_swift6.swift index 74a01f9e60c48..87d3b8360d50b 100644 --- a/test/Concurrency/global_actor_inference_swift6.swift +++ b/test/Concurrency/global_actor_inference_swift6.swift @@ -117,7 +117,7 @@ struct HasWrapperOnActor { synced = 17 } - @WrapperActor var actorSynced: Int = 0 + @WrapperActor var actorSynced: Int = 0 // expected-error{{'nonisolated' is not supported on properties with property wrappers}} func testActorSynced() { _ = actorSynced diff --git a/test/Concurrency/isolated_default_argument_eval.swift b/test/Concurrency/isolated_default_argument_eval.swift new file mode 100644 index 0000000000000..0ccfd01ca1de5 --- /dev/null +++ b/test/Concurrency/isolated_default_argument_eval.swift @@ -0,0 +1,70 @@ +// RUN: %target-swift-emit-silgen -I %t -disable-availability-checking -strict-concurrency=complete -enable-upcoming-feature IsolatedDefaultValues -parse-as-library %s | %FileCheck %s + +// REQUIRES: concurrency +// REQUIRES: asserts + +@MainActor +func requiresMainActor() -> Int { 0 } + +@MainActor +func mainActorDefaultArg(value: Int = requiresMainActor()) {} + +@MainActor +func mainActorMultiDefaultArg(x: Int = requiresMainActor(), + y: Int = 0, + tuple: (Int, Int) = (requiresMainActor(), 2), + z: Int = 0) {} + +// CHECK-LABEL: sil hidden [ossa] @$s30isolated_default_argument_eval15mainActorCalleryyF +@MainActor func mainActorCaller() { + mainActorDefaultArg() + mainActorMultiDefaultArg() +} + +// CHECK-LABEL: sil hidden [ossa] @$s30isolated_default_argument_eval22nonisolatedAsyncCalleryyYaF +func nonisolatedAsyncCaller() async { + // CHECK: hop_to_executor {{.*}} : $Optional + // CHECK: hop_to_executor {{.*}} : $MainActor + // CHECK: [[GET_VALUE:%[0-9]+]] = function_ref @$s30isolated_default_argument_eval19mainActorDefaultArg5valueySi_tFfA_ + // CHECK-NEXT: apply [[GET_VALUE]]() + // CHECK: hop_to_executor {{.*}} : $Optional + await mainActorDefaultArg() + + // CHECK: hop_to_executor {{.*}} : $MainActor + // CHECK: [[GET_X:%[0-9]+]] = function_ref @$s30isolated_default_argument_eval24mainActorMultiDefaultArg1x1y5tuple1zySi_S2i_SitSitFfA_ + // CHECK-NEXT: apply [[GET_X]]() + // CHECK-NOT: hop_to_executor + // CHECK: [[GET_Y:%[0-9]+]] = function_ref @$s30isolated_default_argument_eval24mainActorMultiDefaultArg1x1y5tuple1zySi_S2i_SitSitFfA0_ + // CHECK-NEXT: apply [[GET_Y]]() + // CHECK-NOT: hop_to_executor + // CHECK: [[GET_TUPLE:%[0-9]+]] = function_ref @$s30isolated_default_argument_eval24mainActorMultiDefaultArg1x1y5tuple1zySi_S2i_SitSitFfA1_ + // CHECK-NEXT: apply [[GET_TUPLE]]() + // CHECK-NOT: hop_to_executor + // CHECK: [[GET_Z:%[0-9]+]] = function_ref @$s30isolated_default_argument_eval24mainActorMultiDefaultArg1x1y5tuple1zySi_S2i_SitSitFfA2_ + // CHECK-NEXT: apply [[GET_Z]]() + // CHECK: hop_to_executor {{.*}} : $Optional + await mainActorMultiDefaultArg() +} + +@MainActor +func isolatedDefaultInoutMix(x: inout Int, y: Int, z: Int = requiresMainActor()) {} + +var argValue: Int { 0 } + +// CHECK-LABEL: sil hidden [ossa] @$s30isolated_default_argument_eval20passInoutWithDefaultyyYaF +func passInoutWithDefault() async { + // CHECK: hop_to_executor {{.*}} : $Optional + + var x = 0 + + // CHECK: [[GET_ARG_VALUE:%[0-9]+]] = function_ref @$s30isolated_default_argument_eval8argValueSivg + // CHECK-NEXT: [[ARG_VALUE:%[0-9]+]] = apply [[GET_ARG_VALUE]]() + // CHECK-NEXT: [[INOUT_X:%[0-9]+]] = begin_access [modify] + // CHECK: hop_to_executor {{.*}} : $MainActor + // CHECK: [[GET_Z:%[0-9]+]] = function_ref @$s30isolated_default_argument_eval0A15DefaultInoutMix1x1y1zySiz_S2itFfA1_ + // CHECK-NEXT: [[Z:%[0-9]+]] = apply [[GET_Z]]() + // CHECK: [[FN:%[0-9]+]] = function_ref @$s30isolated_default_argument_eval0A15DefaultInoutMix1x1y1zySiz_S2itF : $@convention(thin) (@inout Int, Int, Int) -> () + // CHECK: apply [[FN]]([[INOUT_X]], [[ARG_VALUE]], [[Z]]) + // CHECK: hop_to_executor {{.*}} : $Optional + await isolatedDefaultInoutMix(x: &x, y: argValue) +} diff --git a/test/Concurrency/isolated_default_arguments.swift b/test/Concurrency/isolated_default_arguments.swift index 8ae99b295a706..6e9eeac7af4c8 100644 --- a/test/Concurrency/isolated_default_arguments.swift +++ b/test/Concurrency/isolated_default_arguments.swift @@ -2,8 +2,8 @@ // 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 IsolatedDefaultValues -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 IsolatedDefaultValues -enable-experimental-feature SendNonSendable %s +// RUN: %target-swift-frontend -I %t -disable-availability-checking -strict-concurrency=complete -enable-upcoming-feature IsolatedDefaultValues -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-upcoming-feature IsolatedDefaultValues -enable-experimental-feature SendNonSendable %s // REQUIRES: concurrency // REQUIRES: asserts @@ -20,16 +20,28 @@ func requiresMainActor() -> Int { 0 } @SomeGlobalActor func requiresSomeGlobalActor() -> Int { 0 } +// expected-error@+1 {{main actor-isolated default value in a nonisolated context}} +func mainActorDefaultArgInvalid(value: Int = requiresMainActor()) {} + +func mainActorClosureInvalid( + closure: () -> Int = { // expected-error {{main actor-isolated default value in a nonisolated context}} + requiresMainActor() + } +) {} + +// expected-note@+2 {{calls to global function 'mainActorDefaultArg(value:)' from outside of its actor context are implicitly asynchronous}} +@MainActor func mainActorDefaultArg(value: Int = requiresMainActor()) {} -func mainActorClosure( +// expected-note@+1 {{calls to global function 'mainActorClosure(closure:)' from outside of its actor context are implicitly asynchronous}} +@MainActor func mainActorClosure( closure: () -> Int = { requiresMainActor() } ) {} func mainActorClosureCall( - closure: Int = { + closure: Int = { // expected-error {{main actor-isolated default value in a nonisolated context}} requiresMainActor() }() ) {} @@ -40,40 +52,29 @@ func mainActorClosureCall( mainActorClosureCall() } +// expected-note@+1 2 {{add '@MainActor' to make global function 'nonisolatedCaller()' part of global actor 'MainActor'}} func nonisolatedCaller() { - // expected-error@+1 {{main actor-isolated default argument cannot be synchronously evaluated from a nonisolated context}} + // expected-error@+1 {{call to main actor-isolated global function 'mainActorDefaultArg(value:)' in a synchronous nonisolated context}} mainActorDefaultArg() - // FIXME: confusing error message. - // expected-error@+1 {{main actor-isolated default argument cannot be synchronously evaluated from a nonisolated context}} + // expected-error@+1 {{call to main actor-isolated global function 'mainActorClosure(closure:)' in a synchronous 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}} + // expected-error@+2 {{expression is 'async' but is not marked with 'await'}} + // expected-note@+1 {{calls to global function 'mainActorDefaultArg(value:)' from outside of its actor context are implicitly asynchronous}} mainActorDefaultArg() - // expected-error@+1 {{main actor-isolated default argument cannot be synchronously evaluated from a nonisolated context}} + // expected-error@+2 {{expression is 'async' but is not marked with 'await'}} + // expected-note@+1 {{calls to global function 'mainActorClosure(closure:)' from outside of its actor context are implicitly asynchronous}} 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() + mainActorClosureCall() } func conflictingIsolationDefaultArg( @@ -125,6 +126,7 @@ func closureWithIsolatedParam( } ) {} +// expected-note@+2 3 {{calls to initializer 'init(required:x:y:)' from outside of its actor context are implicitly asynchronous}} @MainActor struct S1 { var required: Int @@ -136,6 +138,7 @@ struct S1 { static var z: Int = requiresMainActor() } +// expected-note@+2 3 {{calls to initializer 'init(required:x:y:)' from outside of its actor context are implicitly asynchronous}} @SomeGlobalActor struct S2 { var required: Int @@ -152,36 +155,77 @@ struct S3 { var (x, y, z) = (requiresMainActor(), requiresSomeGlobalActor(), 10) } +struct S4 { + // expected-error@+1 {{main actor-isolated default value in a nonisolated context}} + var x: Int = requiresMainActor() +} + @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}} + // expected-error@+1 {{call to global actor 'SomeGlobalActor'-isolated initializer 'init(required:x:y:)' in a synchronous 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}} + // expected-error@+1 {{call to main actor-isolated initializer 'init(required:x:y:)' in a synchronous global actor 'SomeGlobalActor'-isolated context}} _ = S1(required: 10) _ = S2(required: 10) } +// expected-note@+2 {{add '@MainActor' to make global function 'initializeFromNonisolated()' part of global actor 'MainActor'}} +// expected-note@+1 {{add '@SomeGlobalActor' to make global function 'initializeFromNonisolated()' part of global actor 'SomeGlobalActor'}} func initializeFromNonisolated() { - // expected-error@+1 {{main actor-isolated default argument cannot be synchronously evaluated from a nonisolated context}} + // expected-error@+1 {{call to main actor-isolated initializer 'init(required:x:y:)' in a synchronous nonisolated context}} _ = S1(required: 10) - // expected-error@+1 {{global actor 'SomeGlobalActor'-isolated default argument cannot be synchronously evaluated from a nonisolated context}} + // expected-error@+1 {{call to global actor 'SomeGlobalActor'-isolated initializer 'init(required:x:y:)' in a synchronous 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}} + // expected-error@+1 {{call to main actor-isolated initializer 'init(required:x:y:)' in a synchronous actor-isolated context}} _ = S1(required: 10) - // expected-error@+1 {{global actor 'SomeGlobalActor'-isolated default argument cannot be synchronously evaluated from a actor-isolated context}} + // expected-error@+1 {{call to global actor 'SomeGlobalActor'-isolated initializer 'init(required:x:y:)' in a synchronous actor-isolated context}} _ = S2(required: 10) } } + +// expected-error@+1 {{default initializer for 'C1' cannot be both main actor-isolated and global actor 'SomeGlobalActor'-isolated}} +class C1 { + // expected-note@+1 {{initializer for property 'x' is main actor-isolated}} + @MainActor var x = requiresMainActor() + // expected-note@+1 {{initializer for property 'y' is global actor 'SomeGlobalActor'-isolated}} + @SomeGlobalActor var y = requiresSomeGlobalActor() +} + +class NonSendable {} + +// expected-error@+1 {{default initializer for 'C2' cannot be both main actor-isolated and global actor 'SomeGlobalActor'-isolated}} +class C2 { + // expected-note@+1 {{initializer for property 'x' is main actor-isolated}} + @MainActor var x = NonSendable() + // expected-note@+1 {{initializer for property 'y' is global actor 'SomeGlobalActor'-isolated}} + @SomeGlobalActor var y = NonSendable() +} + +class C3 { + @MainActor var x = 1 + @SomeGlobalActor var y = 2 +} + +@MainActor struct NonIsolatedInit { + var x = 0 + var y = 0 +} + +func callDefaultInit() async { + _ = C2() + _ = NonIsolatedInit() + _ = NonIsolatedInit(x: 10) +} diff --git a/test/Concurrency/isolated_default_arguments_serialized.swift b/test/Concurrency/isolated_default_arguments_serialized.swift index b284fef283c9d..9ae856eed281d 100644 --- a/test/Concurrency/isolated_default_arguments_serialized.swift +++ b/test/Concurrency/isolated_default_arguments_serialized.swift @@ -1,8 +1,8 @@ // 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 IsolatedDefaultValues %S/Inputs/serialized_default_arguments.swift +// RUN: %target-swift-frontend -emit-module -swift-version 5 -emit-module-path %t/SerializedDefaultArguments.swiftmodule -module-name SerializedDefaultArguments -enable-upcoming-feature IsolatedDefaultValues %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 IsolatedDefaultValues +// RUN: %target-swift-frontend %s -emit-sil -o /dev/null -verify -disable-availability-checking -swift-version 6 -I %t -enable-experimental-feature SendNonSendable -enable-upcoming-feature IsolatedDefaultValues // REQUIRES: concurrency // REQUIRES: asserts @@ -16,7 +16,6 @@ func mainActorCaller() { } func nonisolatedCaller() async { - // expected-error@+1 {{main actor-isolated default argument cannot be synchronously evaluated from a nonisolated context}} await useMainActorDefault() await useMainActorDefault(mainActorDefaultArg()) diff --git a/test/Concurrency/isolated_default_property_inits.swift b/test/Concurrency/isolated_default_property_inits.swift index 23eabff16b3d9..fccb08870008c 100644 --- a/test/Concurrency/isolated_default_property_inits.swift +++ b/test/Concurrency/isolated_default_property_inits.swift @@ -2,8 +2,8 @@ // 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 IsolatedDefaultValues -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 IsolatedDefaultValues -enable-experimental-feature SendNonSendable %s +// RUN: %target-swift-frontend -I %t -disable-availability-checking -strict-concurrency=complete -enable-upcoming-feature IsolatedDefaultValues -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-upcoming-feature IsolatedDefaultValues -enable-experimental-feature SendNonSendable %s // REQUIRES: concurrency // REQUIRES: asserts @@ -19,11 +19,11 @@ func requiresMainActor() -> Int { 0 } @SomeGlobalActor func requiresSomeGlobalActor() -> Int { 0 } -struct S1 { +class C1 { // expected-note@+1 2 {{'self.x' not initialized}} - var x = requiresMainActor() + @MainActor var x = requiresMainActor() // expected-note@+1 2 {{'self.y' not initialized}} - var y = requiresSomeGlobalActor() + @SomeGlobalActor var y = requiresSomeGlobalActor() var z = 10 // expected-error@+1 {{return from initializer without initializing all stored properties}} @@ -36,9 +36,9 @@ struct S1 { @SomeGlobalActor init(c: Int) {} } -struct S2 { - var x = requiresMainActor() - var y = requiresSomeGlobalActor() +class C2 { + @MainActor var x = requiresMainActor() + @SomeGlobalActor var y = requiresSomeGlobalActor() var z = 10 nonisolated init(x: Int, y: Int) { diff --git a/test/Concurrency/predates_concurrency.swift b/test/Concurrency/predates_concurrency.swift index 376d60f2b00b5..6f2f855200de7 100644 --- a/test/Concurrency/predates_concurrency.swift +++ b/test/Concurrency/predates_concurrency.swift @@ -73,13 +73,16 @@ func testElsewhere(x: X) { // expected-complete-sns-note @-1 {{calls to global function 'onMainActorAlways()' from outside of its actor context are implicitly asynchronous}} @preconcurrency @MainActor class MyModelClass { - // expected-complete-sns-note @-1 {{calls to initializer 'init()' from outside of its actor context are implicitly asynchronous}} + + // default init() is 'nonisolated' in '-strict-concurrency=complete' + func f() { } // expected-complete-sns-note @-1 {{calls to instance method 'f()' from outside of its actor context are implicitly asynchronous}} } func testCalls(x: X) { - // expected-complete-sns-note @-1 3{{add '@MainActor' to make global function 'testCalls(x:)' part of global actor 'MainActor'}} + // expected-complete-sns-note @-1 2{{add '@MainActor' to make global function 'testCalls(x:)' part of global actor 'MainActor'}} + unsafelyMainActorClosure { onMainActor() } @@ -94,7 +97,7 @@ func testCalls(x: X) { } (X.unsafelyDoEverythingClosure)(x)( { onMainActor() - }) + }) onMainActorAlways() // okay with minimal/targeted concurrency. Not ok with complete. // expected-complete-sns-warning @-1 {{call to main actor-isolated global function 'onMainActorAlways()' in a synchronous nonisolated context}} @@ -102,8 +105,9 @@ func testCalls(x: X) { // Ok with minimal/targeted concurrency, Not ok with complete. let _: () -> Void = onMainActorAlways // expected-complete-sns-warning {{converting function value of type '@MainActor () -> ()' to '() -> Void' loses global actor 'MainActor'}} - // both okay with minimal/targeted... an error with complete. - let c = MyModelClass() // expected-complete-sns-error {{call to main actor-isolated initializer 'init()' in a synchronous nonisolated context}} + let c = MyModelClass() + + // okay with minimal/targeted... an error with complete. c.f() // expected-complete-sns-error {{call to main actor-isolated instance method 'f()' in a synchronous nonisolated context}} } @@ -113,8 +117,9 @@ func testCallsWithAsync() async { let _: () -> Void = onMainActorAlways // expected-warning {{converting function value of type '@MainActor () -> ()' to '() -> Void' loses global actor 'MainActor'}} - let c = MyModelClass() // expected-error{{expression is 'async' but is not marked with 'await'}} - // expected-note@-1{{calls to initializer 'init()' from outside of its actor context are implicitly asynchronous}} + let c = MyModelClass() // expected-minimal-targeted-error{{expression is 'async' but is not marked with 'await'}} + // expected-minimal-targeted-note@-1{{calls to initializer 'init()' from outside of its actor context are implicitly asynchronous}} + c.f() // expected-error{{expression is 'async' but is not marked with 'await'}} // expected-note@-1{{calls to instance method 'f()' from outside of its actor context are implicitly asynchronous}} } diff --git a/test/Concurrency/predates_concurrency_swift6.swift b/test/Concurrency/predates_concurrency_swift6.swift index 297eb6cd1887e..7072169d3a24a 100644 --- a/test/Concurrency/predates_concurrency_swift6.swift +++ b/test/Concurrency/predates_concurrency_swift6.swift @@ -59,18 +59,18 @@ func testElsewhere(x: X) { // expected-note@-1{{are implicitly asynchronous}} @preconcurrency @MainActor class MyModelClass { - // expected-note@-1{{are implicitly asynchronous}} func f() { } // expected-note@-1{{are implicitly asynchronous}} } func testCalls(x: X) { - // expected-note@-1 3{{add '@MainActor' to make global function 'testCalls(x:)' part of global actor 'MainActor'}} + // expected-note@-1 2{{add '@MainActor' to make global function 'testCalls(x:)' part of global actor 'MainActor'}} onMainActorAlways() // expected-error{{call to main actor-isolated global function 'onMainActorAlways()' in a synchronous nonisolated context}} let _: () -> Void = onMainActorAlways // expected-error{{converting function value of type '@MainActor () -> ()' to '() -> Void' loses global actor 'MainActor'}} - let c = MyModelClass() // expected-error{{call to main actor-isolated initializer 'init()' in a synchronous nonisolated context}} + let c = MyModelClass() // okay, synthesized init() is 'nonisolated' + c.f() // expected-error{{call to main actor-isolated instance method 'f()' in a synchronous nonisolated context}} } @@ -80,8 +80,8 @@ func testCallsWithAsync() async { let _: () -> Void = onMainActorAlways // expected-error{{converting function value of type '@MainActor () -> ()' to '() -> Void' loses global actor 'MainActor'}} - let c = MyModelClass() // expected-error{{expression is 'async' but is not marked with 'await'}} - // expected-note@-1{{calls to initializer 'init()' from outside of its actor context are implicitly asynchronous}} + let c = MyModelClass() // okay, synthesized init() is 'nonisolated' + c.f() // expected-error{{expression is 'async' but is not marked with 'await'}} // expected-note@-1{{calls to instance method 'f()' from outside of its actor context are implicitly asynchronous}} } diff --git a/test/Concurrency/sendable_cycle.swift b/test/Concurrency/sendable_cycle.swift index c2d7e001a7af9..bf71e99f0eb99 100644 --- a/test/Concurrency/sendable_cycle.swift +++ b/test/Concurrency/sendable_cycle.swift @@ -1,7 +1,6 @@ // RUN: %target-swift-frontend %S/Inputs/sendable_cycle_other.swift -disable-availability-checking %s -verify -emit-sil -o /dev/null // RUN: %target-swift-frontend %S/Inputs/sendable_cycle_other.swift -disable-availability-checking %s -verify -emit-sil -o /dev/null -strict-concurrency=targeted -// RUN: %target-swift-frontend %S/Inputs/sendable_cycle_other.swift -disable-availability-checking %s -verify -emit-sil -o /dev/null -strict-concurrency=complete -// RUN: %target-swift-frontend %S/Inputs/sendable_cycle_other.swift -disable-availability-checking %s -verify -emit-sil -o /dev/null -strict-concurrency=complete -enable-experimental-feature SendNonSendable +// RUN: %target-swift-frontend %S/Inputs/sendable_cycle_other.swift -disable-availability-checking %s -verify -emit-sil -o /dev/null -strict-concurrency=complete -verify-additional-prefix complete- // REQUIRES: concurrency // REQUIRES: asserts diff --git a/test/Concurrency/task_local.swift b/test/Concurrency/task_local.swift index 7e7d9abc09097..0703602a58582 100644 --- a/test/Concurrency/task_local.swift +++ b/test/Concurrency/task_local.swift @@ -1,6 +1,6 @@ // RUN: %target-swift-frontend -strict-concurrency=targeted -disable-availability-checking -emit-sil -verify -o /dev/null %s -// RUN: %target-swift-frontend -strict-concurrency=complete -disable-availability-checking -emit-sil -verify -o /dev/null %s -// RUN: %target-swift-frontend -strict-concurrency=complete -disable-availability-checking -emit-sil -verify -o /dev/null %s -enable-experimental-feature SendNonSendable +// RUN: %target-swift-frontend -strict-concurrency=complete -disable-availability-checking -emit-sil -verify -o /dev/null %s -verify-additional-prefix complete- +// RUN: %target-swift-frontend -strict-concurrency=complete -disable-availability-checking -emit-sil -verify -o /dev/null %s -enable-experimental-feature SendNonSendable -verify-additional-prefix complete- // REQUIRES: concurrency // REQUIRES: asserts @@ -8,14 +8,18 @@ @available(SwiftStdlib 5.1, *) struct TL { @TaskLocal - static var number: Int = 0 + static var number: Int = 0 // expected-complete-warning {{static property 'number' is not concurrency-safe because it is non-isolated global shared mutable state; this is an error in Swift 6}} + // expected-complete-note@-1 {{isolate 'number' to a global actor, or convert it to a 'let' constant and conform it to 'Sendable'}} @TaskLocal - static var someNil: Int? + static var someNil: Int? // expected-complete-warning {{static property 'someNil' is not concurrency-safe because it is non-isolated global shared mutable state; this is an error in Swift 6}} + // expected-complete-note@-1 {{isolate 'someNil' to a global actor, or convert it to a 'let' constant and conform it to 'Sendable'}} @TaskLocal static var noValue: Int // expected-error{{'static var' declaration requires an initializer expression or an explicitly stated getter}} // expected-note@-1{{add an initializer to silence this error}} + // expected-complete-warning@-2 {{static property 'noValue' is not concurrency-safe because it is non-isolated global shared mutable state; this is an error in Swift 6}} + // expected-complete-note@-3 {{isolate 'noValue' to a global actor, or convert it to a 'let' constant and conform it to 'Sendable'}} @TaskLocal var notStatic: String? // expected-error{{property 'notStatic', must be static because property wrapper 'TaskLocal' can only be applied to static properties}}