diff --git a/include/swift/AST/ActorIsolation.h b/include/swift/AST/ActorIsolation.h index fdcfd5a9f2fdb..af036c944d50a 100644 --- a/include/swift/AST/ActorIsolation.h +++ b/include/swift/AST/ActorIsolation.h @@ -88,21 +88,75 @@ class ActorIsolation { /// Set to true if this was parsed from SIL. unsigned silParsed : 1; - unsigned parameterIndex : 27; + /// The opaque value of an EncodedParameterIndex. + /// Only meaningful for ActorInstance. + unsigned encodedParameterIndex : 27; + + class EncodedParameterIndex { + enum : unsigned { + SpecialIndex_Capture, + SpecialIndex_Self, + NumSpecialIndexes + }; + + /// Either a special index or (parameter index + NumSpecialIndexes). + unsigned value; + + constexpr EncodedParameterIndex(unsigned value) : value(value) {} + + public: + static constexpr EncodedParameterIndex parameter(unsigned index) { + return EncodedParameterIndex(NumSpecialIndexes + index); + } + static constexpr EncodedParameterIndex self() { + return EncodedParameterIndex(SpecialIndex_Self); + } + static constexpr EncodedParameterIndex capture() { + return EncodedParameterIndex(SpecialIndex_Capture); + } + + unsigned getParameterIndex() const { + assert(value >= NumSpecialIndexes); + return value - NumSpecialIndexes; + } + bool isSelf() const { + return value == SpecialIndex_Self; + } + bool isCapture() const { + return value == SpecialIndex_Capture; + } - ActorIsolation(Kind kind, NominalTypeDecl *actor, unsigned parameterIndex); + static EncodedParameterIndex fromOpaqueValue(unsigned value) { + return EncodedParameterIndex(value); + } + unsigned getOpaqueValue() const { + return value; + } + }; - ActorIsolation(Kind kind, VarDecl *actor, unsigned parameterIndex); + ActorIsolation(Kind kind, NominalTypeDecl *actor, + EncodedParameterIndex parameterIndex); - ActorIsolation(Kind kind, Expr *actor, unsigned parameterIndex); + ActorIsolation(Kind kind, VarDecl *actor, + EncodedParameterIndex parameterIndex); + + ActorIsolation(Kind kind, Expr *actor, + EncodedParameterIndex parameterIndex); ActorIsolation(Kind kind, Type globalActor); + EncodedParameterIndex getEncodedParameterIndex() const { + return EncodedParameterIndex::fromOpaqueValue(encodedParameterIndex); + } + public: // No-argument constructor needed for DenseMap use in PostfixCompletion.cpp explicit ActorIsolation(Kind kind = Unspecified, bool isSILParsed = false) : pointer(nullptr), kind(kind), isolatedByPreconcurrency(false), - silParsed(isSILParsed), parameterIndex(0) {} + silParsed(isSILParsed), encodedParameterIndex(0) { + // SIL's use of this has weaker invariants for now. + assert(kind != ActorInstance || isSILParsed); + } static ActorIsolation forUnspecified() { return ActorIsolation(Unspecified); @@ -125,19 +179,22 @@ class ActorIsolation { static ActorIsolation forActorInstanceParameter(NominalTypeDecl *actor, unsigned parameterIndex) { - return ActorIsolation(ActorInstance, actor, parameterIndex + 1); + return ActorIsolation(ActorInstance, actor, + EncodedParameterIndex::parameter(parameterIndex)); } static ActorIsolation forActorInstanceParameter(VarDecl *actor, unsigned parameterIndex) { - return ActorIsolation(ActorInstance, actor, parameterIndex + 1); + return ActorIsolation(ActorInstance, actor, + EncodedParameterIndex::parameter(parameterIndex)); } static ActorIsolation forActorInstanceParameter(Expr *actor, unsigned parameterIndex); static ActorIsolation forActorInstanceCapture(VarDecl *capturedActor) { - return ActorIsolation(ActorInstance, capturedActor, 0); + return ActorIsolation(ActorInstance, capturedActor, + EncodedParameterIndex::capture()); } static ActorIsolation forGlobalActor(Type globalActor) { @@ -153,21 +210,14 @@ class ActorIsolation { static std::optional forSILString(StringRef string) { auto kind = llvm::StringSwitch>(string) - .Case("unspecified", - std::optional(ActorIsolation::Unspecified)) - .Case("actor_instance", - std::optional(ActorIsolation::ActorInstance)) - .Case("nonisolated", - std::optional(ActorIsolation::Nonisolated)) - .Case("nonisolated_unsafe", std::optional( - ActorIsolation::NonisolatedUnsafe)) - .Case("global_actor", - std::optional(ActorIsolation::GlobalActor)) - .Case("global_actor_unsafe", - std::optional(ActorIsolation::GlobalActor)) + .Case("unspecified", ActorIsolation::Unspecified) + .Case("actor_instance", ActorIsolation::ActorInstance) + .Case("nonisolated", ActorIsolation::Nonisolated) + .Case("nonisolated_unsafe", ActorIsolation::NonisolatedUnsafe) + .Case("global_actor", ActorIsolation::GlobalActor) + .Case("global_actor_unsafe", ActorIsolation::GlobalActor) .Case("caller_isolation_inheriting", - std::optional( - ActorIsolation::CallerIsolationInheriting)) + ActorIsolation::CallerIsolationInheriting) .Default(std::nullopt); if (kind == std::nullopt) return std::nullopt; @@ -187,17 +237,22 @@ class ActorIsolation { bool isNonisolatedUnsafe() const { return kind == NonisolatedUnsafe; } /// Retrieve the parameter to which actor-instance isolation applies. - /// - /// Parameter 0 is `self`. - unsigned getActorInstanceParameter() const { + unsigned getActorInstanceParameterIndex() const { assert(getKind() == ActorInstance); - return parameterIndex; + return getEncodedParameterIndex().getParameterIndex(); + } + + /// Given that this is actor instance isolation, is it a capture? + bool isActorInstanceForCapture() const { + assert(getKind() == ActorInstance); + return getEncodedParameterIndex().isCapture(); } /// Returns true if this is an actor-instance isolation that additionally /// applies to the self parameter of a method. bool isActorInstanceForSelfParameter() const { - return getActorInstanceParameter() == 0; + assert(getKind() == ActorInstance); + return getEncodedParameterIndex().isSelf(); } bool isSILParsed() const { return silParsed; } @@ -285,13 +340,13 @@ class ActorIsolation { id.AddPointer(pointer); id.AddBoolean(isolatedByPreconcurrency); id.AddBoolean(silParsed); - id.AddInteger(parameterIndex); + id.AddInteger(encodedParameterIndex); } friend llvm::hash_code hash_value(const ActorIsolation &state) { return llvm::hash_combine(state.kind, state.pointer, state.isolatedByPreconcurrency, state.silParsed, - state.parameterIndex); + state.encodedParameterIndex); } void print(llvm::raw_ostream &os) const; diff --git a/lib/AST/ActorIsolation.cpp b/lib/AST/ActorIsolation.cpp index 19c06555a08da..57283f8c6599d 100644 --- a/lib/AST/ActorIsolation.cpp +++ b/lib/AST/ActorIsolation.cpp @@ -23,23 +23,27 @@ ActorIsolation ActorIsolation::forMainActor(ASTContext &ctx) { ctx.getMainActorType()->mapTypeOutOfContext()); } +// These constructors are defined out-of-line so that including ActorIsolation.h +// doesn't require a bunch of other headers to be included. + ActorIsolation::ActorIsolation(Kind kind, NominalTypeDecl *actor, - unsigned parameterIndex) + EncodedParameterIndex parameterIndex) : actorInstance(actor), kind(kind), isolatedByPreconcurrency(false), - silParsed(false), parameterIndex(parameterIndex) {} + silParsed(false), encodedParameterIndex(parameterIndex.getOpaqueValue()) {} ActorIsolation::ActorIsolation(Kind kind, VarDecl *actor, - unsigned parameterIndex) + EncodedParameterIndex parameterIndex) : actorInstance(actor), kind(kind), isolatedByPreconcurrency(false), - silParsed(false), parameterIndex(parameterIndex) {} + silParsed(false), encodedParameterIndex(parameterIndex.getOpaqueValue()) {} -ActorIsolation::ActorIsolation(Kind kind, Expr *actor, unsigned parameterIndex) +ActorIsolation::ActorIsolation(Kind kind, Expr *actor, + EncodedParameterIndex parameterIndex) : actorInstance(actor), kind(kind), isolatedByPreconcurrency(false), - silParsed(false), parameterIndex(parameterIndex) {} + silParsed(false), encodedParameterIndex(parameterIndex.getOpaqueValue()) {} ActorIsolation::ActorIsolation(Kind kind, Type globalActor) : globalActor(globalActor), kind(kind), isolatedByPreconcurrency(false), - silParsed(false), parameterIndex(0) {} + silParsed(false), encodedParameterIndex(0) {} ActorIsolation ActorIsolation::forActorInstanceParameter(Expr *actor, @@ -67,25 +71,29 @@ ActorIsolation::forActorInstanceParameter(Expr *actor, } } - return ActorIsolation(ActorInstance, actor, parameterIndex + 1); + return ActorIsolation(ActorInstance, actor, + EncodedParameterIndex::parameter(parameterIndex)); } ActorIsolation ActorIsolation::forActorInstanceSelf(ValueDecl *decl) { if (auto *fn = dyn_cast(decl)) - return ActorIsolation(ActorInstance, fn->getImplicitSelfDecl(), 0); + return ActorIsolation(ActorInstance, fn->getImplicitSelfDecl(), + EncodedParameterIndex::self()); if (auto *storage = dyn_cast(decl)) { if (auto *fn = storage->getAccessor(AccessorKind::Get)) { - return ActorIsolation(ActorInstance, fn->getImplicitSelfDecl(), 0); + return ActorIsolation(ActorInstance, fn->getImplicitSelfDecl(), + EncodedParameterIndex::self()); } } auto *dc = decl->getDeclContext(); - return ActorIsolation(ActorInstance, dc->getSelfNominalTypeDecl(), 0); + return ActorIsolation(ActorInstance, dc->getSelfNominalTypeDecl(), + EncodedParameterIndex::self()); } ActorIsolation ActorIsolation::forActorInstanceSelf(NominalTypeDecl *selfDecl) { - return ActorIsolation(ActorInstance, selfDecl, 0); + return ActorIsolation(ActorInstance, selfDecl, EncodedParameterIndex::self()); } NominalTypeDecl *ActorIsolation::getActor() const { @@ -183,6 +191,9 @@ bool ActorIsolation::isEqual(const ActorIsolation &lhs, auto *lhsActor = lhs.getActorInstance(); auto *rhsActor = rhs.getActorInstance(); if (lhsActor && rhsActor) { + if (lhsActor == rhsActor) + return true; + // FIXME: This won't work for arbitrary isolated parameter captures. if ((lhsActor->isSelfParameter() && rhsActor->isSelfParamCapture()) || (lhsActor->isSelfParamCapture() && rhsActor->isSelfParameter())) { diff --git a/lib/AST/ConformanceLookup.cpp b/lib/AST/ConformanceLookup.cpp index f8b2232c8e5fc..29089041a968a 100644 --- a/lib/AST/ConformanceLookup.cpp +++ b/lib/AST/ConformanceLookup.cpp @@ -269,8 +269,13 @@ static ProtocolConformanceRef getBuiltinTupleTypeConformance( return ProtocolConformanceRef::forMissingOrInvalid(type, protocol); } +// We can end up checking builtin conformances for generic function types +// when e.g. we're checking whether a captured local func declaration is +// sendable. That's fine, we can answer that question in the abstract +// without needing to generally support conformances on generic function +// types. using EitherFunctionType = - llvm::PointerUnion; + llvm::PointerUnion; /// Whether the given function type conforms to Sendable. static bool isSendableFunctionType(EitherFunctionType eitherFnTy) { @@ -288,7 +293,7 @@ static bool isSendableFunctionType(EitherFunctionType eitherFnTy) { representation = *converted; } else { - auto functionType = eitherFnTy.get(); + auto functionType = eitherFnTy.get(); if (functionType->isSendable()) return true; @@ -332,7 +337,7 @@ static bool isBitwiseCopyableFunctionType(EitherFunctionType eitherFnTy) { if (auto silFnTy = eitherFnTy.dyn_cast()) { representation = silFnTy->getRepresentation(); } else { - auto fnTy = eitherFnTy.get(); + auto fnTy = eitherFnTy.get(); representation = convertRepresentation(fnTy->getRepresentation()); } @@ -621,7 +626,7 @@ LookupConformanceInModuleRequest::evaluate( } // Function types can conform to protocols. - if (auto functionType = type->getAs()) { + if (auto functionType = type->getAs()) { return getBuiltinFunctionTypeConformance(type, functionType, protocol); } diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index b156bd629bd56..bcec28fd568c9 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -6123,13 +6123,15 @@ RValue SILGenFunction::emitApply( SILValue executor; switch (*implicitActorHopTarget) { case ActorIsolation::ActorInstance: - if (unsigned paramIndex = - implicitActorHopTarget->getActorInstanceParameter()) { + assert(!implicitActorHopTarget->isActorInstanceForCapture()); + if (implicitActorHopTarget->isActorInstanceForSelfParameter()) { + executor = emitLoadActorExecutor(loc, args.back()); + } else { + unsigned paramIndex = + implicitActorHopTarget->getActorInstanceParameterIndex(); auto isolatedIndex = calleeTypeInfo.origFormalType - ->getLoweredParamIndex(paramIndex - 1); + ->getLoweredParamIndex(paramIndex); executor = emitLoadActorExecutor(loc, args[isolatedIndex]); - } else { - executor = emitLoadActorExecutor(loc, args.back()); } break; diff --git a/lib/SILGen/SILGenConcurrency.cpp b/lib/SILGen/SILGenConcurrency.cpp index bea50739dedcd..ca0f92fa3f823 100644 --- a/lib/SILGen/SILGenConcurrency.cpp +++ b/lib/SILGen/SILGenConcurrency.cpp @@ -195,7 +195,8 @@ void SILGenFunction::emitExpectedExecutorProlog() { case ActorIsolation::GlobalActor: if (F.isAsync() || wantDataRaceChecks) { - setExpectedExecutorForGlobalActor(*this, actorIsolation.getGlobalActor()); + auto globalActorType = F.mapTypeIntoContext(actorIsolation.getGlobalActor()); + setExpectedExecutorForGlobalActor(*this, globalActorType); } break; } @@ -226,7 +227,8 @@ void SILGenFunction::emitExpectedExecutorProlog() { case ActorIsolation::GlobalActor: if (wantExecutor) { - setExpectedExecutorForGlobalActor(*this, actorIsolation.getGlobalActor()); + auto globalActorType = F.mapTypeIntoContext(actorIsolation.getGlobalActor()); + setExpectedExecutorForGlobalActor(*this, globalActorType); break; } } @@ -573,9 +575,10 @@ SILGenFunction::emitFunctionTypeIsolation(SILLocation loc, // Emit global actor isolation by loading .shared from the global actor, // erasing it into `any Actor`, and injecting that into Optional. - case FunctionTypeIsolation::Kind::GlobalActor: + case FunctionTypeIsolation::Kind::GlobalActor: { return emitGlobalActorIsolation(loc, isolation.getGlobalActorType()->getCanonicalType()); + } // Emit @isolated(any) isolation by loading the actor reference from the // function. @@ -646,14 +649,14 @@ SILGenFunction::emitClosureIsolation(SILLocation loc, SILDeclRef constant, case ActorIsolation::Erased: llvm_unreachable("closures cannot directly have erased isolation"); - case ActorIsolation::GlobalActor: - return emitGlobalActorIsolation(loc, - isolation.getGlobalActor()->getCanonicalType()); + case ActorIsolation::GlobalActor: { + auto globalActorType = F.mapTypeIntoContext(isolation.getGlobalActor()) + ->getCanonicalType(); + return emitGlobalActorIsolation(loc, globalActorType); + } case ActorIsolation::ActorInstance: { - // This should always be a capture. That's not expressed super-cleanly - // in ActorIsolation, unfortunately. - assert(isolation.getActorInstanceParameter() == 0); + assert(isolation.isActorInstanceForCapture()); auto capture = isolation.getActorInstance(); assert(capture); return emitLoadOfCaptureIsolation(*this, loc, capture, constant, captures); @@ -705,8 +708,10 @@ SILGenFunction::emitExecutor(SILLocation loc, ActorIsolation isolation, return emitLoadActorExecutor(loc, self); } - case ActorIsolation::GlobalActor: - return emitLoadGlobalActorExecutor(isolation.getGlobalActor()); + case ActorIsolation::GlobalActor: { + auto globalActorType = F.mapTypeIntoContext(isolation.getGlobalActor()); + return emitLoadGlobalActorExecutor(globalActorType); + } } llvm_unreachable("covered switch"); } diff --git a/lib/SILGen/SILGenConstructor.cpp b/lib/SILGen/SILGenConstructor.cpp index 5c0586bd1fc8e..a330439cab44d 100644 --- a/lib/SILGen/SILGenConstructor.cpp +++ b/lib/SILGen/SILGenConstructor.cpp @@ -595,7 +595,7 @@ static bool ctorHopsInjectedByDefiniteInit(ConstructorDecl *ctor, // must be self-isolated switch (isolation) { case ActorIsolation::ActorInstance: - return isolation.getActorInstanceParameter() == 0; + return isolation.isActorInstanceForSelfParameter(); case ActorIsolation::Erased: llvm_unreachable("constructor cannot have erased isolation"); diff --git a/lib/SILGen/SILGenPoly.cpp b/lib/SILGen/SILGenPoly.cpp index aecc3d7814142..c0bcb341b4fbf 100644 --- a/lib/SILGen/SILGenPoly.cpp +++ b/lib/SILGen/SILGenPoly.cpp @@ -7402,7 +7402,7 @@ void SILGenFunction::emitProtocolWitness( // For an instance actor, get the actor 'self'. if (*enterIsolation == ActorIsolation::ActorInstance) { - assert(enterIsolation->getActorInstanceParameter() == 0 && "Not self?"); + assert(enterIsolation->isActorInstanceForSelfParameter() && "Not self?"); auto actorSelfVal = origParams.back(); if (actorSelfVal.getType().isAddress()) { diff --git a/lib/Sema/TypeCheckCaptures.cpp b/lib/Sema/TypeCheckCaptures.cpp index ce403eb2f6248..7ed2d0895cf38 100644 --- a/lib/Sema/TypeCheckCaptures.cpp +++ b/lib/Sema/TypeCheckCaptures.cpp @@ -762,7 +762,7 @@ CaptureInfo CaptureInfoRequest::evaluate(Evaluator &evaluator, finder.checkType(type, AFD->getLoc()); } - if (AFD->isLocalCapture() && AFD->hasAsync()) { + if (AFD->isLocalCapture()) { // If a local function inherits isolation from the enclosing context, // make sure we capture the isolated parameter, if we haven't already. auto actorIsolation = getActorIsolation(AFD); diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 8768f982ed6c0..3a094d55bf8d9 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -3045,13 +3045,13 @@ namespace { // If the closure won't execute concurrently with the context in // which the declaration occurred, it's okay. auto decl = capture.getDecl(); - auto isolation = getActorIsolation(decl); // 'nonisolated' local variables are always okay to capture in // 'Sendable' closures because they can be accessed from anywhere. // Note that only 'nonisolated(unsafe)' can be applied to local // variables. - if (isolation.isNonisolated()) + if (isa(decl) && + getActorIsolation(decl).isNonisolated()) continue; auto *context = localFunc.getAsDeclContext(); @@ -3565,12 +3565,14 @@ namespace { if (isolation == ActorIsolation::ActorInstance) { VarDecl *var = isolation.getActorInstance(); if (!var) { + assert(!isolation.isActorInstanceForCapture() && + "capture isolation without a variable reference?"); auto dc = const_cast(getDeclContext()); - auto paramIdx = isolation.getActorInstanceParameter(); - if (paramIdx == 0) { + if (isolation.isActorInstanceForSelfParameter()) { var = cast(dc)->getImplicitSelfDecl(); } else { - var = const_cast(getParameterAt(dc, paramIdx - 1)); + auto paramIdx = isolation.getActorInstanceParameterIndex(); + var = const_cast(getParameterAt(dc, paramIdx)); } } return var; @@ -4342,11 +4344,10 @@ namespace { const VarDecl *var = isolation.getActorInstance(); if (!var) { auto dc = getDeclContext(); - auto paramIdx = isolation.getActorInstanceParameter(); - if (paramIdx == 0) { + if (isolation.isActorInstanceForSelfParameter()) { var = cast(dc)->getImplicitSelfDecl(); } else { - var = getParameterAt(dc, paramIdx - 1); + var = getParameterAt(dc, isolation.getActorInstanceParameterIndex()); } } actorExpr = new (ctx) DeclRefExpr( @@ -4656,7 +4657,7 @@ namespace { if (!partialApply && (result.isolation.isGlobalActor() || (result.isolation == ActorIsolation::ActorInstance && - result.isolation.getActorInstanceParameter() > 0)) && + !result.isolation.isActorInstanceForSelfParameter())) && isa(decl)) return false; @@ -4832,6 +4833,70 @@ namespace { }; } // end anonymous namespace +/// Compute the isolation of a closure or local function from its parent +/// isolation. +/// +/// Doesn't apply preconcurrency because it's generally easier for the +/// caller to do so. +static ActorIsolation +computeClosureIsolationFromParent(DeclContext *closure, + ActorIsolation parentIsolation, + bool checkIsolatedCapture) { + // We must have parent isolation determined to get here. + switch (parentIsolation) { + case ActorIsolation::CallerIsolationInheriting: + case ActorIsolation::Nonisolated: + case ActorIsolation::NonisolatedUnsafe: + case ActorIsolation::Unspecified: + return ActorIsolation::forNonisolated( + parentIsolation == ActorIsolation::NonisolatedUnsafe); + + case ActorIsolation::Erased: + llvm_unreachable("context cannot have erased isolation"); + + case ActorIsolation::GlobalActor: + // This should already be an interface type, so we don't need to remap + // it between the contexts. + return ActorIsolation::forGlobalActor(parentIsolation.getGlobalActor()); + + case ActorIsolation::ActorInstance: { + // In non-@Sendable local functions, we always inherit the enclosing + // isolation, forcing a capture of it if necessary. + if (isa(closure)) { + // We should always have a VarDecl in this case, where we got the + // ActorIsolation from a context; the non-VarDecl cases are only used + // locally within isolation checking. + auto actor = parentIsolation.getActorInstance(); + assert(actor); + return ActorIsolation::forActorInstanceCapture(actor); + } + + if (checkIsolatedCapture) { + auto closureAsFn = AnyFunctionRef::fromFunctionDeclContext(closure); + if (auto param = closureAsFn.getCaptureInfo().getIsolatedParamCapture()) + return ActorIsolation::forActorInstanceCapture(param); + + auto *explicitClosure = dyn_cast(closure); + // @_inheritActorContext(always) forces the isolation capture. + if (explicitClosure && explicitClosure->alwaysInheritsActorContext()) { + if (parentIsolation.isActorInstanceIsolated()) { + if (auto *param = parentIsolation.getActorInstance()) + return ActorIsolation::forActorInstanceCapture(param); + } + return parentIsolation; + } + } else { + // If we don't have capture information during code completion, assume + // that the closure captures the `isolated` parameter from the parent + // context. + return parentIsolation; + } + + return ActorIsolation::forNonisolated(/*unsafe=*/false); + } + } +} + ActorIsolation ActorIsolationChecker::determineClosureIsolation( AbstractClosureExpr *closure) const { bool preconcurrency = false; @@ -4888,48 +4953,8 @@ ActorIsolation ActorIsolationChecker::determineClosureIsolation( closure->getParent(), getClosureActorIsolation); preconcurrency |= parentIsolation.preconcurrency(); - // We must have parent isolation determined to get here. - switch (parentIsolation) { - case ActorIsolation::CallerIsolationInheriting: - case ActorIsolation::Nonisolated: - case ActorIsolation::NonisolatedUnsafe: - case ActorIsolation::Unspecified: - return ActorIsolation::forNonisolated( - parentIsolation == ActorIsolation::NonisolatedUnsafe); - - case ActorIsolation::Erased: - llvm_unreachable("context cannot have erased isolation"); - - case ActorIsolation::GlobalActor: { - Type globalActor = closure->mapTypeIntoContext( - parentIsolation.getGlobalActor()->mapTypeOutOfContext()); - return ActorIsolation::forGlobalActor(globalActor); - } - - case ActorIsolation::ActorInstance: { - if (checkIsolatedCapture) { - if (auto param = closure->getCaptureInfo().getIsolatedParamCapture()) - return ActorIsolation::forActorInstanceCapture(param); - - auto *explicitClosure = dyn_cast(closure); - // @_inheritActorContext(always) forces the isolation capture. - if (explicitClosure && explicitClosure->alwaysInheritsActorContext()) { - if (parentIsolation.isActorInstanceIsolated()) { - if (auto *param = parentIsolation.getActorInstance()) - return ActorIsolation::forActorInstanceCapture(param); - } - return parentIsolation; - } - } else { - // If we don't have capture information during code completion, assume - // that the closure captures the `isolated` parameter from the parent - // context. - return parentIsolation; - } - - return ActorIsolation::forNonisolated(/*unsafe=*/false); - } - } + return computeClosureIsolationFromParent(closure, parentIsolation, + checkIsolatedCapture); }(); // Apply computed preconcurrency. @@ -5126,7 +5151,8 @@ getIsolationFromAttributes(const Decl *decl, bool shouldDiagnose = true, std::optional result; if (selfTypeDecl) { if (selfTypeDecl->isAnyActor()) { - result = ActorIsolation::forActorInstanceSelf(selfTypeDecl); + result = ActorIsolation::forActorInstanceSelf( + const_cast(cast(decl))); } else { // If the declaration is in an extension that has one of the isolation // attributes, use that. @@ -6315,11 +6341,18 @@ static InferredActorIsolation computeActorIsolation(Evaluator &evaluator, return inferred; }; + // If this is an accessor, use the actor isolation of its storage + // declaration. All of the logic for FuncDecls below only applies to + // non-accessor functions. + if (auto accessor = dyn_cast(value)) { + return getInferredActorIsolation(accessor->getStorage()); + } + // If this is a local function, inherit the actor isolation from its // context if it global or was captured. if (auto func = dyn_cast(value)) { - if (func->isLocalCapture() && !func->isSendable()) { - auto *dc = func->getDeclContext(); + auto *dc = func->getDeclContext(); + if (dc->isLocalContext() && !func->isSendable()) { llvm::PointerUnion inferenceSource; if (auto *closure = dyn_cast(dc)) { inferenceSource = closure; @@ -6327,38 +6360,18 @@ static InferredActorIsolation computeActorIsolation(Evaluator &evaluator, inferenceSource = dc->getAsDecl(); } - switch (auto enclosingIsolation = getActorIsolationOfContext(dc)) { - case ActorIsolation::Nonisolated: - case ActorIsolation::CallerIsolationInheriting: - case ActorIsolation::NonisolatedUnsafe: - case ActorIsolation::Unspecified: - // Do nothing. - break; - - case ActorIsolation::Erased: - llvm_unreachable("context cannot have erased isolation"); - - case ActorIsolation::ActorInstance: - return { - inferredIsolation(enclosingIsolation), - IsolationSource(inferenceSource, IsolationSource::LexicalContext) - }; - - case ActorIsolation::GlobalActor: - return { - inferredIsolation(enclosingIsolation), - IsolationSource(inferenceSource, IsolationSource::LexicalContext) - }; - } + auto enclosingIsolation = getActorIsolationOfContext(dc); + auto isolation = + computeClosureIsolationFromParent(func, enclosingIsolation, + /*checkIsolatedCapture*/true) + .withPreconcurrency(enclosingIsolation.preconcurrency()); + return { + inferredIsolation(isolation), + IsolationSource(inferenceSource, IsolationSource::LexicalContext) + }; } } - // If this is an accessor, use the actor isolation of its storage - // declaration. - if (auto accessor = dyn_cast(value)) { - return getInferredActorIsolation(accessor->getStorage()); - } - if (auto var = dyn_cast(value)) { auto &ctx = var->getASTContext(); if (!ctx.LangOpts.isConcurrencyModelTaskToThread() && @@ -6600,7 +6613,8 @@ bool HasIsolatedSelfRequest::evaluate( } } if (attrIsolation) { - return attrIsolation == ActorIsolation::forActorInstanceSelf(selfTypeDecl); + return attrIsolation->getKind() == ActorIsolation::ActorInstance && + attrIsolation->isActorInstanceForSelfParameter(); } // If this is a variable, check for a property wrapper that alters its @@ -7860,7 +7874,7 @@ ActorIsolation swift::getActorIsolationForReference(ValueDecl *decl, // as needing to enter the actor. if (auto nominal = ctor->getDeclContext()->getSelfNominalTypeDecl()) { if (nominal->isAnyActor()) - return ActorIsolation::forActorInstanceSelf(decl); + return ActorIsolation::forActorInstanceSelf(ctor); } // Fall through to treat initializers like any other declaration. @@ -8107,7 +8121,7 @@ ActorReferenceResult ActorReferenceResult::forReference( // The declaration we are accessing is actor-isolated. First, check whether // we are on the same actor already. if (actorInstance && declIsolation == ActorIsolation::ActorInstance && - declIsolation.getActorInstanceParameter() == 0) { + declIsolation.isActorInstanceForSelfParameter()) { // If this instance is isolated, we're in the same concurrency domain. if (actorInstance->isIsolated()) return forSameConcurrencyDomain(declIsolation, options); diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index 3e285caa8f7e2..ff19b0a8e56ac 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -2165,8 +2165,10 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, std::optional IsolationCrossing; if (bool(ApplyCallerIsolation) || bool(ApplyCalleeIsolation)) { - auto caller = ActorIsolation(ActorIsolation::Kind(ApplyCallerIsolation)); - auto callee = ActorIsolation(ActorIsolation::Kind(ApplyCalleeIsolation)); + auto caller = ActorIsolation(ActorIsolation::Kind(ApplyCallerIsolation), + /*forSIL*/ true); + auto callee = ActorIsolation(ActorIsolation::Kind(ApplyCalleeIsolation), + /*forSIL*/ true); IsolationCrossing = {caller, callee}; } @@ -2209,8 +2211,10 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, std::optional IsolationCrossing; if (bool(ApplyCallerIsolation) || bool(ApplyCalleeIsolation)) { - auto caller = ActorIsolation(ActorIsolation::Kind(ApplyCallerIsolation)); - auto callee = ActorIsolation(ActorIsolation::Kind(ApplyCalleeIsolation)); + auto caller = ActorIsolation(ActorIsolation::Kind(ApplyCallerIsolation), + /*forSIL*/true); + auto callee = ActorIsolation(ActorIsolation::Kind(ApplyCalleeIsolation), + /*forSIL*/true); IsolationCrossing = {caller, callee}; } diff --git a/test/Concurrency/default_isolation.swift b/test/Concurrency/default_isolation.swift index 3a5a101267513..c1dbe2b895b7e 100644 --- a/test/Concurrency/default_isolation.swift +++ b/test/Concurrency/default_isolation.swift @@ -30,6 +30,14 @@ func test() { } } +// Tested below. This used to fail in default-isolation mode because +// the type-checker applied the default isolation to the implicit $defer +// function, causing it to have MainActor isolation despite the enclosing +// context being nonisolated. +nonisolated func test_defer() { + defer {} +} + //--- concurrent.swift using nonisolated @@ -37,7 +45,14 @@ using nonisolated // CHECK: // S.init(value:) // CHECK-NEXT: // Isolation: unspecified struct S { - // CHECK: // S.value.getter - // CHECK-NEXT: // Isolation: unspecified var value: Int } + +// CHECK: // test_defer() +// CHECK-NEXT: // Isolation: nonisolated + +// CHECK: // $defer #1 () in test_defer() +// CHECK-NEXT: // Isolation: nonisolated + +// CHECK: // S.value.getter +// CHECK-NEXT: // Isolation: unspecified diff --git a/test/Concurrency/local_functions.swift b/test/Concurrency/local_functions.swift new file mode 100644 index 0000000000000..c409a7d483fee --- /dev/null +++ b/test/Concurrency/local_functions.swift @@ -0,0 +1,62 @@ +// RUN: %target-swift-frontend -target %target-swift-5.1-abi-triple %s -emit-sil -o - -verify -strict-concurrency=complete -enable-actor-data-race-checks -disable-availability-checking | %FileCheck %s + +// Issue #80772. This used to crash in SILGen because we gave local functions +// the isolation of their enclosing context instead of trying to convert +// parameter isolation to capture isolation. +actor TestActor { + // CHECK-LABEL: // nested #1 () in TestActor.testWithoutCapture() + // CHECK-NEXT: // Isolation: actor_instance. name: 'self' + func testWithoutCapture() { + func nested() -> String { + return "test" + } + + print(nested()) + } + + // CHECK-LABEL: // nested #1 () in TestActor.testWithCapture() + // CHECK-NEXT: // Isolation: actor_instance. name: 'self' + // CHECK: [[SELF_EXECUTOR:%.*]] = extract_executor %0 + // CHECK: [[CHECK_FN:%.*]] = function_ref @swift_task_isCurrentExecutor + // CHECK: apply [[CHECK_FN]]([[SELF_EXECUTOR]]) + func testWithCapture() { + func nested() -> String { + _ = self + return "test" + } + + print(nested()) + } +} + +@globalActor struct GenericGlobalActor { + static var shared: TestActor { + // not a valid implementation + return TestActor() + } +} + +struct Generic { + // CHECK-LABEL: // nested #1 (_:) in Generic.testGenericGlobalActor() + // CHECK-NEXT: // Isolation: global_actor. type: GenericGlobalActor + @GenericGlobalActor func testGenericGlobalActor() { + func nested(_ type: U.Type) -> String { + // CHECK: [[FN:%.*]] = function_ref @$s15local_functions18GenericGlobalActorV6sharedAA04TestE0CvgZ + // CHECK: apply [[FN]]( + return "test" + } + + print(nested(Int.self)) + } +} + +actor MyActor { + // CHECK-LABEL: // nested #1 () in MyActor.deinit + // CHECK-NEXT: // Isolation: actor_instance. name: 'self' + isolated deinit { + func nested() -> String { + return "test" + } + print(nested()) + } +} diff --git a/test/SILGen/local_function_isolation.swift b/test/SILGen/local_function_isolation.swift index 87bea9eff70f8..c8bd8d1188802 100644 --- a/test/SILGen/local_function_isolation.swift +++ b/test/SILGen/local_function_isolation.swift @@ -71,9 +71,9 @@ actor GenericActor { // Make sure defer doesn't capture anything. actor DeferInsideInitActor { init(foo: ()) async throws { - // CHECK-LABEL: sil private [ossa] @$s24local_function_isolation20DeferInsideInitActorC3fooACyt_tYaKcfc6$deferL_yyF : $@convention(thin) () -> () { + // CHECK-LABEL: sil private [ossa] @$s24local_function_isolation20DeferInsideInitActorC3fooACyt_tYaKcfc6$deferL_yyF : $@convention(thin) (@sil_isolated @guaranteed DeferInsideInitActor) -> () { defer {} - try self.init() + self.init() } }