diff --git a/include/swift/AST/ActorIsolation.h b/include/swift/AST/ActorIsolation.h index 023a4d34107cb..305bd06e0cd6c 100644 --- a/include/swift/AST/ActorIsolation.h +++ b/include/swift/AST/ActorIsolation.h @@ -60,6 +60,9 @@ class ActorIsolation { /// meaning that it can be used from any actor but is also unable to /// refer to the isolated state of any given actor. Nonisolated, + /// The declaration is explicitly specified to be not isolated and with the + /// "unsafe" annotation, which means that we do not enforce isolation. + NonisolatedUnsafe, /// The declaration is isolated to a global actor. It can refer to other /// entities with the same global actor. GlobalActor, @@ -97,8 +100,8 @@ class ActorIsolation { return ActorIsolation(Unspecified, nullptr); } - static ActorIsolation forNonisolated() { - return ActorIsolation(Nonisolated, nullptr); + static ActorIsolation forNonisolated(bool unsafe) { + return ActorIsolation(unsafe ? NonisolatedUnsafe : Nonisolated, nullptr); } static ActorIsolation forActorInstanceSelf(NominalTypeDecl *actor) { @@ -124,8 +127,10 @@ class ActorIsolation { operator Kind() const { return getKind(); } bool isUnspecified() const { return kind == Unspecified; } - - bool isNonisolated() const { return kind == Nonisolated; } + + bool isNonisolated() const { + return (kind == Nonisolated) || (kind == NonisolatedUnsafe); + } /// Retrieve the parameter to which actor-instance isolation applies. /// @@ -144,6 +149,7 @@ class ActorIsolation { case Unspecified: case Nonisolated: + case NonisolatedUnsafe: return false; } } @@ -192,6 +198,7 @@ class ActorIsolation { switch (lhs.getKind()) { case Nonisolated: + case NonisolatedUnsafe: case Unspecified: return true; diff --git a/include/swift/AST/Attr.def b/include/swift/AST/Attr.def index 3b78bd5417d9b..b653cd417f385 100644 --- a/include/swift/AST/Attr.def +++ b/include/swift/AST/Attr.def @@ -499,7 +499,7 @@ CONTEXTUAL_SIMPLE_DECL_ATTR(async, Async, SIMPLE_DECL_ATTR(reasync, Reasync, OnFunc | OnConstructor | RejectByParser | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove, 109) -CONTEXTUAL_SIMPLE_DECL_ATTR(nonisolated, Nonisolated, +CONTEXTUAL_DECL_ATTR(nonisolated, Nonisolated, DeclModifier | OnFunc | OnConstructor | OnVar | OnSubscript | ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove, 112) CONTEXTUAL_SIMPLE_DECL_ATTR(distributed, DistributedActor, diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index 075ed757cf42e..a5eff94f9480d 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -187,6 +187,10 @@ class DeclAttribute : public AttributeBase { SWIFT_INLINE_BITFIELD(ObjCImplementationAttr, DeclAttribute, 1, isCategoryNameInvalid : 1 ); + + SWIFT_INLINE_BITFIELD(NonisolatedAttr, DeclAttribute, 1, + isUnsafe : 1 + ); } Bits; // clang-format on @@ -2434,6 +2438,26 @@ class ObjCImplementationAttr final : public DeclAttribute { } }; +/// Represents nonisolated modifier. +class NonisolatedAttr final : public DeclAttribute { +public: + NonisolatedAttr(SourceLoc atLoc, SourceRange range, bool unsafe, + bool implicit) + : DeclAttribute(DAK_Nonisolated, atLoc, range, implicit) { + Bits.NonisolatedAttr.isUnsafe = unsafe; + assert((isUnsafe() == unsafe) && "not enough bits for unsafe state"); + } + + NonisolatedAttr(bool unsafe, bool implicit) + : NonisolatedAttr({}, {}, unsafe, implicit) {} + + bool isUnsafe() const { return Bits.NonisolatedAttr.isUnsafe; } + + static bool classof(const DeclAttribute *DA) { + return DA->getKind() == DAK_Nonisolated; + } +}; + /// A macro role attribute, spelled with either @attached or @freestanding, /// which declares one of the roles that a given macro can inhabit. class MacroRoleAttr final diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 0c99f9076d70f..363fe3741b7c6 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -6128,7 +6128,10 @@ class VarDecl : public AbstractStorageDecl { /// True if this is a top-level global variable from the main source file. bool isTopLevelGlobal() const { return Bits.VarDecl.IsTopLevelGlobal; } void setTopLevelGlobal(bool b) { Bits.VarDecl.IsTopLevelGlobal = b; } - + + /// True if this is any storage of static duration (global scope or static). + bool isGlobalStorage() const; + /// Retrieve the custom attributes that attach property wrappers to this /// property. The returned list contains all of the attached property wrapper /// attributes in source order, which means the outermost wrapper attribute diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 1d2b1bc1e0623..806c1607aa062 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -1456,8 +1456,10 @@ namespace { }); } - printFlag(D->getAttrs().hasAttribute(), "nonisolated", - ExprModifierColor); + if (auto *attr = D->getAttrs().getAttribute()) { + printFlag(attr->isUnsafe() ? "nonisolated(unsafe)" : "nonisolated", + ExprModifierColor); + } printFlag(D->isDistributed(), "distributed", ExprModifierColor); printFlag(D->isDistributedThunk(), "distributed_thunk",ExprModifierColor); } @@ -2735,6 +2737,7 @@ class PrintExpr : public ExprVisitor, switch (auto isolation = E->getActorIsolation()) { case ActorIsolation::Unspecified: case ActorIsolation::Nonisolated: + case ActorIsolation::NonisolatedUnsafe: break; case ActorIsolation::ActorInstance: diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 859e250cb83ba..917736a3de36c 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -1401,6 +1401,14 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, break; } + case DAK_Nonisolated: { + Printer.printAttrName("nonisolated"); + if (cast(this)->isUnsafe()) { + Printer << "(unsafe)"; + } + break; + } + case DAK_MacroRole: { auto Attr = cast(this); @@ -1721,6 +1729,12 @@ StringRef DeclAttribute::getAttrName() const { return "_section"; case DAK_Documentation: return "_documentation"; + case DAK_Nonisolated: + if (cast(this)->isUnsafe()) { + return "nonisolated(unsafe)"; + } else { + return "nonisolated"; + } case DAK_MacroRole: switch (cast(this)->getMacroSyntax()) { case MacroSyntax::Freestanding: diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 6751989c2ac6c..09abc07545184 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -2460,6 +2460,7 @@ static bool deferMatchesEnclosingAccess(const FuncDecl *defer) { switch (getActorIsolation(type)) { case ActorIsolation::Unspecified: + case ActorIsolation::NonisolatedUnsafe: case ActorIsolation::GlobalActorUnsafe: break; @@ -7893,6 +7894,15 @@ VarDecl *VarDecl::getLazyStorageProperty() const { {}); } +bool VarDecl::isGlobalStorage() const { + if (!hasStorage()) { + return false; + } + const auto *dc = getDeclContext(); + return isStatic() || dc->isModuleScopeContext() || + (dc->isTypeContext() && !isInstanceMember()); +} + bool VarDecl::isPropertyMemberwiseInitializedWithWrappedType() const { auto customAttrs = getAttachedPropertyWrappers(); if (customAttrs.empty()) @@ -10500,6 +10510,7 @@ bool VarDecl::isSelfParamCaptureIsolated() const { switch (auto isolation = closure->getActorIsolation()) { case ActorIsolation::Unspecified: case ActorIsolation::Nonisolated: + case ActorIsolation::NonisolatedUnsafe: case ActorIsolation::GlobalActor: case ActorIsolation::GlobalActorUnsafe: return false; diff --git a/lib/AST/DiagnosticEngine.cpp b/lib/AST/DiagnosticEngine.cpp index 744082a664a3e..d444f66075487 100644 --- a/lib/AST/DiagnosticEngine.cpp +++ b/lib/AST/DiagnosticEngine.cpp @@ -921,8 +921,12 @@ static void formatDiagnosticArgument(StringRef Modifier, } case ActorIsolation::Nonisolated: + case ActorIsolation::NonisolatedUnsafe: case ActorIsolation::Unspecified: Out << "nonisolated"; + if (isolation == ActorIsolation::NonisolatedUnsafe) { + Out << "(unsafe)"; + } break; } break; diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index 5d12902fd99f3..5ca2df242e865 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -1741,7 +1741,11 @@ void swift::simple_display( break; case ActorIsolation::Nonisolated: + case ActorIsolation::NonisolatedUnsafe: out << "nonisolated"; + if (state == ActorIsolation::NonisolatedUnsafe) { + out << "(unsafe)"; + } break; case ActorIsolation::Unspecified: diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index fe6c78470e437..972a5bb6fe5d3 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -7844,7 +7844,8 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) { // Hard-code @actorIndependent, until Objective-C clients start // using nonisolated. if (swiftAttr->getAttribute() == "@actorIndependent") { - auto attr = new (SwiftContext) NonisolatedAttr(/*isImplicit=*/true); + auto attr = new (SwiftContext) + NonisolatedAttr(/*unsafe=*/false, /*implicit=*/true); MappedDecl->getAttrs().add(attr); continue; } diff --git a/lib/ClangImporter/SwiftDeclSynthesizer.cpp b/lib/ClangImporter/SwiftDeclSynthesizer.cpp index 25eae66a78721..595edb8dc4ba0 100644 --- a/lib/ClangImporter/SwiftDeclSynthesizer.cpp +++ b/lib/ClangImporter/SwiftDeclSynthesizer.cpp @@ -422,7 +422,8 @@ ValueDecl *SwiftDeclSynthesizer::createConstant( // Mark the function transparent so that we inline it away completely. func->getAttrs().add(new (C) TransparentAttr(/*implicit*/ true)); - auto nonisolatedAttr = new (C) NonisolatedAttr(/*IsImplicit=*/true); + auto nonisolatedAttr = + new (C) NonisolatedAttr(/*unsafe=*/false, /*implicit=*/true); var->getAttrs().add(nonisolatedAttr); // Set the function up as the getter. diff --git a/lib/IDE/CompletionLookup.cpp b/lib/IDE/CompletionLookup.cpp index c3501cf612557..82edff684f246 100644 --- a/lib/IDE/CompletionLookup.cpp +++ b/lib/IDE/CompletionLookup.cpp @@ -789,6 +789,7 @@ void CompletionLookup::analyzeActorIsolation( } case ActorIsolation::Unspecified: case ActorIsolation::Nonisolated: + case ActorIsolation::NonisolatedUnsafe: return; } diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 7216a082dbebf..927aae0a4eb9a 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -3839,6 +3839,20 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, return makeParserSuccess(); break; } + case DAK_Nonisolated: { + auto isUnsafe = + parseSingleAttrOption(*this, Loc, AttrRange, AttrName, DK, + {{Context.Id_unsafe, true}}, false); + if (!isUnsafe) { + return makeParserSuccess(); + } + + if (!DiscardAttribute) { + Attributes.add(new (Context) NonisolatedAttr(AtLoc, AttrRange, *isUnsafe, + /*implicit*/ false)); + } + break; + } case DAK_MacroRole: { auto syntax = (AttrName == "freestanding" ? MacroSyntax::Freestanding : MacroSyntax::Attached); diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index 06b38b8feef00..82f964b63129e 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -5521,6 +5521,7 @@ RValue SILGenFunction::emitApply( case ActorIsolation::Unspecified: case ActorIsolation::Nonisolated: + case ActorIsolation::NonisolatedUnsafe: llvm_unreachable("Not isolated"); break; } diff --git a/lib/SILGen/SILGenConstructor.cpp b/lib/SILGen/SILGenConstructor.cpp index 42c41acde35e9..67e34e0251e81 100644 --- a/lib/SILGen/SILGenConstructor.cpp +++ b/lib/SILGen/SILGenConstructor.cpp @@ -610,6 +610,7 @@ static bool ctorHopsInjectedByDefiniteInit(ConstructorDecl *ctor, case ActorIsolation::Unspecified: case ActorIsolation::Nonisolated: + case ActorIsolation::NonisolatedUnsafe: case ActorIsolation::GlobalActor: case ActorIsolation::GlobalActorUnsafe: return false; @@ -1554,6 +1555,7 @@ void SILGenFunction::emitMemberInitializer(DeclContext *dc, VarDecl *selfDecl, // 'nonisolated' expressions can be evaluated from anywhere case ActorIsolation::Unspecified: case ActorIsolation::Nonisolated: + case ActorIsolation::NonisolatedUnsafe: break; case ActorIsolation::GlobalActor: diff --git a/lib/SILGen/SILGenProlog.cpp b/lib/SILGen/SILGenProlog.cpp index bfaa7d6005ef0..d5f7029064bf2 100644 --- a/lib/SILGen/SILGenProlog.cpp +++ b/lib/SILGen/SILGenProlog.cpp @@ -1281,6 +1281,7 @@ void SILGenFunction::emitProlog( return false; case ActorIsolation::Nonisolated: + case ActorIsolation::NonisolatedUnsafe: case ActorIsolation::Unspecified: return false; } @@ -1332,6 +1333,7 @@ void SILGenFunction::emitProlog( switch (actorIsolation.getKind()) { case ActorIsolation::Unspecified: case ActorIsolation::Nonisolated: + case ActorIsolation::NonisolatedUnsafe: break; case ActorIsolation::ActorInstance: { @@ -1384,6 +1386,7 @@ void SILGenFunction::emitProlog( switch (actorIsolation.getKind()) { case ActorIsolation::Unspecified: case ActorIsolation::Nonisolated: + case ActorIsolation::NonisolatedUnsafe: break; case ActorIsolation::ActorInstance: { @@ -1572,6 +1575,7 @@ SILGenFunction::emitExecutor(SILLocation loc, ActorIsolation isolation, switch (isolation.getKind()) { case ActorIsolation::Unspecified: case ActorIsolation::Nonisolated: + case ActorIsolation::NonisolatedUnsafe: return llvm::None; case ActorIsolation::ActorInstance: { @@ -1597,8 +1601,9 @@ void SILGenFunction::emitHopToActorValue(SILLocation loc, ManagedValue actor) { getActorIsolationOfContext(FunctionDC, [](AbstractClosureExpr *CE) { return CE->getActorIsolation(); }); - if (isolation != ActorIsolation::Nonisolated - && isolation != ActorIsolation::Unspecified) { + if (isolation != ActorIsolation::Nonisolated && + isolation != ActorIsolation::NonisolatedUnsafe && + isolation != ActorIsolation::Unspecified) { // TODO: Explicit hop with no hop-back should only be allowed in nonisolated // async functions. But it needs work for any closure passed to // Task.detached, which currently has unspecified isolation. diff --git a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp index 675763657e2a5..2ba2bfa64a50c 100644 --- a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp +++ b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp @@ -1046,6 +1046,7 @@ void LifetimeChecker::injectActorHops() { case ActorIsolation::Unspecified: case ActorIsolation::Nonisolated: + case ActorIsolation::NonisolatedUnsafe: case ActorIsolation::GlobalActorUnsafe: case ActorIsolation::GlobalActor: return; diff --git a/lib/Sema/CodeSynthesis.cpp b/lib/Sema/CodeSynthesis.cpp index 87193c2b86a7c..a3970376700e5 100644 --- a/lib/Sema/CodeSynthesis.cpp +++ b/lib/Sema/CodeSynthesis.cpp @@ -1634,7 +1634,8 @@ bool swift::addNonIsolatedToSynthesized(NominalTypeDecl *nominal, return false; ASTContext &ctx = nominal->getASTContext(); - value->getAttrs().add(new (ctx) NonisolatedAttr(/*isImplicit=*/true)); + value->getAttrs().add( + new (ctx) NonisolatedAttr(/*unsafe=*/false, /*implicit=*/true)); return true; } diff --git a/lib/Sema/CodeSynthesisDistributedActor.cpp b/lib/Sema/CodeSynthesisDistributedActor.cpp index 9cf9a4b80a672..f5cceeacd846c 100644 --- a/lib/Sema/CodeSynthesisDistributedActor.cpp +++ b/lib/Sema/CodeSynthesisDistributedActor.cpp @@ -108,7 +108,7 @@ static VarDecl *addImplicitDistributedActorIDProperty( // mark as nonisolated, allowing access to it from everywhere propDecl->getAttrs().add( - new (C) NonisolatedAttr(/*IsImplicit=*/true)); + new (C) NonisolatedAttr(/*unsafe=*/false, /*implicit=*/true)); // mark as @_compilerInitialized, since we synthesize the initializing // assignment during SILGen. propDecl->getAttrs().add( @@ -158,7 +158,7 @@ static VarDecl *addImplicitDistributedActorActorSystemProperty( // mark as nonisolated, allowing access to it from everywhere propDecl->getAttrs().add( - new (C) NonisolatedAttr(/*IsImplicit=*/true)); + new (C) NonisolatedAttr(/*unsafe=*/false, /*implicit=*/true)); auto idProperty = nominal->getDistributedActorIDProperty(); // If the id was not yet synthesized, we need to ensure that eventually @@ -735,7 +735,8 @@ static FuncDecl *createDistributedThunkFunction(FuncDecl *func) { thunk->setSynthesized(true); thunk->setDistributedThunk(true); - thunk->getAttrs().add(new (C) NonisolatedAttr(/*isImplicit=*/true)); + thunk->getAttrs().add( + new (C) NonisolatedAttr(/*unsafe=*/false, /*implicit=*/true)); if (isa(DC)) thunk->getAttrs().add(new (C) FinalAttr(/*isImplicit=*/true)); diff --git a/lib/Sema/DerivedConformanceActor.cpp b/lib/Sema/DerivedConformanceActor.cpp index 6def2c24a8d34..6632b3d50a5c8 100644 --- a/lib/Sema/DerivedConformanceActor.cpp +++ b/lib/Sema/DerivedConformanceActor.cpp @@ -147,7 +147,8 @@ static ValueDecl *deriveActor_unownedExecutor(DerivedConformance &derived) { property->getAttrs().add(new (ctx) SemanticsAttr(SEMANTICS_DEFAULT_ACTOR, SourceLoc(), SourceRange(), /*implicit*/ true)); - property->getAttrs().add(new (ctx) NonisolatedAttr(/*IsImplicit=*/true)); + property->getAttrs().add( + new (ctx) NonisolatedAttr(/*unsafe=*/false, /*implicit=*/true)); // Make the property implicitly final. property->getAttrs().add(new (ctx) FinalAttr(/*IsImplicit=*/true)); diff --git a/lib/Sema/DerivedConformanceDistributedActor.cpp b/lib/Sema/DerivedConformanceDistributedActor.cpp index c11ef965343dc..b4574ca0d37eb 100644 --- a/lib/Sema/DerivedConformanceDistributedActor.cpp +++ b/lib/Sema/DerivedConformanceDistributedActor.cpp @@ -455,7 +455,7 @@ static ValueDecl *deriveDistributedActor_id(DerivedConformance &derived) { // mark as nonisolated, allowing access to it from everywhere propDecl->getAttrs().add( - new (C) NonisolatedAttr(/*IsImplicit=*/true)); + new (C) NonisolatedAttr(/*unsafe=*/false, /*implicit=*/true)); derived.addMemberToConformanceContext(pbDecl, /*insertAtHead=*/true); derived.addMemberToConformanceContext(propDecl, /*insertAtHead=*/true); @@ -484,7 +484,7 @@ static ValueDecl *deriveDistributedActor_actorSystem( // mark as nonisolated, allowing access to it from everywhere propDecl->getAttrs().add( - new (C) NonisolatedAttr(/*IsImplicit=*/true)); + new (C) NonisolatedAttr(/*unsafe=*/false, /*implicit=*/true)); // IMPORTANT: `id` MUST be the first field of a distributed actor, and // `actorSystem` MUST be the second field, because for a remote instance @@ -782,7 +782,8 @@ static ValueDecl *deriveDistributedActor_unownedExecutor(DerivedConformance &der property->getAttrs().add(new (ctx) SemanticsAttr(SEMANTICS_DEFAULT_ACTOR, SourceLoc(), SourceRange(), /*implicit*/ true)); - property->getAttrs().add(new (ctx) NonisolatedAttr(/*IsImplicit=*/true)); + property->getAttrs().add( + new (ctx) NonisolatedAttr(/*unsafe=*/false, /*implicit=*/true)); // Make the property implicitly final. property->getAttrs().add(new (ctx) FinalAttr(/*IsImplicit=*/true)); diff --git a/lib/Sema/DerivedConformanceEquatableHashable.cpp b/lib/Sema/DerivedConformanceEquatableHashable.cpp index 282e5ce580f66..9375e4a5afeb6 100644 --- a/lib/Sema/DerivedConformanceEquatableHashable.cpp +++ b/lib/Sema/DerivedConformanceEquatableHashable.cpp @@ -563,7 +563,8 @@ deriveHashable_hashInto( // The derived hash(into:) for an actor must be non-isolated. if (!addNonIsolatedToSynthesized(derived.Nominal, hashDecl) && derived.Nominal->isActor()) - hashDecl->getAttrs().add(new (C) NonisolatedAttr(/*IsImplicit*/ true)); + hashDecl->getAttrs().add( + new (C) NonisolatedAttr(/*unsafe*/ false, /*implicit*/ true)); derived.addMembersToConformanceContext({hashDecl}); @@ -928,7 +929,8 @@ static ValueDecl *deriveHashable_hashValue(DerivedConformance &derived) { // The derived hashValue of an actor must be nonisolated. if (!addNonIsolatedToSynthesized(derived.Nominal, hashValueDecl) && derived.Nominal->isActor()) - hashValueDecl->getAttrs().add(new (C) NonisolatedAttr(/*IsImplicit*/ true)); + hashValueDecl->getAttrs().add( + new (C) NonisolatedAttr(/*unsafe*/ false, /*implicit*/ true)); Pattern *hashValuePat = NamedPattern::createImplicit(C, hashValueDecl, intType); diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 5acba237de7ca..92ac49ab3d73d 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -6777,9 +6777,10 @@ void AttributeChecker::visitNonisolatedAttr(NonisolatedAttr *attr) { if (auto var = dyn_cast(D)) { // stored properties have limitations as to when they can be nonisolated. if (var->hasStorage()) { + const bool isUnsafeGlobal = attr->isUnsafe() && var->isGlobalStorage(); // 'nonisolated' can not be applied to mutable stored properties. - if (var->supportsMutation()) { + if (var->supportsMutation() && !isUnsafeGlobal) { diagnoseAndRemoveAttr(attr, diag::nonisolated_mutable_storage); return; } @@ -6833,8 +6834,8 @@ void AttributeChecker::visitNonisolatedAttr(NonisolatedAttr *attr) { return; } } - - // `nonisolated` on non-async actor initializers is invalid + + // `nonisolated` on non-async actor initializers is invalid. // the reasoning is that there is a "little bit" of isolation, // as afforded by flow-isolation. if (auto ctor = dyn_cast(D)) { diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 61deeb2c372bc..88fed4ca2acd5 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -106,7 +106,8 @@ static bool requiresFlowIsolation(ActorIsolation typeIso, case ActorIsolation::GlobalActorUnsafe: case ActorIsolation::Unspecified: case ActorIsolation::Nonisolated: - return false; + case ActorIsolation::NonisolatedUnsafe: + return false; case ActorIsolation::ActorInstance: return !(ctor->hasAsync()); // need flow-isolation for non-async. @@ -323,13 +324,13 @@ llvm::Optional> GlobalActorAttributeRequest::evaluate( Evaluator &evaluator, llvm::PointerUnion subject) const { - DeclContext *dc; - DeclAttributes *declAttrs; + DeclContext *dc = nullptr; + DeclAttributes *declAttrs = nullptr; SourceLoc loc; if (auto decl = subject.dyn_cast()) { dc = decl->getDeclContext(); declAttrs = &decl->getAttrs(); - // HACK: `getLoc`, when querying the attr from a serialized decl, + // HACK: `getLoc`, when querying the attr from a serialized decl, // depending on deserialization order, may launch into arbitrary // type-checking when querying interface types of such decls. Which, // in turn, may do things like query (to print) USRs. This ends up being @@ -502,6 +503,7 @@ static bool varIsSafeAcrossActors(const ModuleDecl *fromModule, switch (varIsolation) { case ActorIsolation::Nonisolated: + case ActorIsolation::NonisolatedUnsafe: case ActorIsolation::Unspecified: // if nonisolated, it's OK return true; @@ -1583,6 +1585,7 @@ static bool wasLegacyEscapingUseRestriction(AbstractFunctionDecl *fn) { // escaping-use restriction switch (getActorIsolation(fn).getKind()) { case ActorIsolation::Nonisolated: + case ActorIsolation::NonisolatedUnsafe: case ActorIsolation::GlobalActor: case ActorIsolation::GlobalActorUnsafe: // convenience inits did not have the restriction. @@ -1803,6 +1806,7 @@ static ActorIsolation getInnermostIsolatedContext( getActorIsolationOfContext(mutableDC, getClosureActorIsolation)) { case ActorIsolation::ActorInstance: case ActorIsolation::Nonisolated: + case ActorIsolation::NonisolatedUnsafe: case ActorIsolation::Unspecified: return isolation; @@ -1904,6 +1908,7 @@ static void noteGlobalActorOnContext(DeclContext *dc, Type globalActor) { case ActorIsolation::GlobalActor: case ActorIsolation::GlobalActorUnsafe: case ActorIsolation::Nonisolated: + case ActorIsolation::NonisolatedUnsafe: return; case ActorIsolation::Unspecified: @@ -2269,6 +2274,7 @@ namespace { // default arguments cannot have any async calls. case ActorIsolation::Unspecified: case ActorIsolation::Nonisolated: + case ActorIsolation::NonisolatedUnsafe: return; case ActorIsolation::GlobalActor: @@ -2651,6 +2657,7 @@ namespace { switch (isolation) { case ActorIsolation::Unspecified: case ActorIsolation::Nonisolated: + case ActorIsolation::NonisolatedUnsafe: if (isSendableClosure(closure, /*forActorIsolation=*/true)) { return ReferencedActor(var, isPotentiallyIsolated, ReferencedActor::SendableClosure); } @@ -2707,6 +2714,7 @@ namespace { switch (auto isolation = getActorIsolationOfContext(dc, getClosureActorIsolation)) { case ActorIsolation::Nonisolated: + case ActorIsolation::NonisolatedUnsafe: case ActorIsolation::Unspecified: // Local functions can capture an isolated parameter. // FIXME: This really should be modeled by getActorIsolationOfContext. @@ -3055,7 +3063,8 @@ namespace { break; case ActorReferenceResult::ExitsActorToNonisolated: - unsatisfiedIsolation = ActorIsolation::forNonisolated(); + unsatisfiedIsolation = + ActorIsolation::forNonisolated(/*unsafe=*/false); break; case ActorReferenceResult::EntersActor: @@ -3113,7 +3122,7 @@ namespace { // an isolated context, then we're exiting the actor context. if (mayExitToNonisolated && fnType->isAsync() && getContextIsolation().isActorIsolated()) - unsatisfiedIsolation = ActorIsolation::forNonisolated(); + unsatisfiedIsolation = ActorIsolation::forNonisolated(/*unsafe=*/false); // If there was no unsatisfied actor isolation, we're done. if (!unsatisfiedIsolation) @@ -3302,6 +3311,7 @@ namespace { decl, getDeclContext()); switch (isolation) { case ActorIsolation::Nonisolated: + case ActorIsolation::NonisolatedUnsafe: case ActorIsolation::Unspecified: break; @@ -3494,6 +3504,7 @@ namespace { case ActorIsolation::Unspecified: case ActorIsolation::Nonisolated: + case ActorIsolation::NonisolatedUnsafe: refKind = ReferencedActor::NonIsolatedContext; break; } @@ -3566,7 +3577,7 @@ namespace { // Sendable closures are nonisolated unless the closure has // specifically opted into inheriting actor isolation. if (isSendableClosure(closure, /*forActorIsolation=*/true)) - return ActorIsolation::forNonisolated() + return ActorIsolation::forNonisolated(/*unsafe=*/false) .withPreconcurrency(preconcurrency); // A non-Sendable closure gets its isolation from its context. @@ -3577,8 +3588,10 @@ namespace { // We must have parent isolation determined to get here. switch (parentIsolation) { case ActorIsolation::Nonisolated: + case ActorIsolation::NonisolatedUnsafe: case ActorIsolation::Unspecified: - return ActorIsolation::forNonisolated() + return ActorIsolation::forNonisolated(parentIsolation == + ActorIsolation::NonisolatedUnsafe) .withPreconcurrency(preconcurrency); case ActorIsolation::GlobalActor: @@ -3594,7 +3607,7 @@ namespace { return ActorIsolation::forActorInstanceCapture(param) .withPreconcurrency(preconcurrency); - return ActorIsolation::forNonisolated() + return ActorIsolation::forNonisolated(/*unsafe=*/false) .withPreconcurrency(preconcurrency); } } @@ -3751,7 +3764,7 @@ getIsolationFromAttributes(const Decl *decl, bool shouldDiagnose = true, // If the declaration is explicitly marked 'nonisolated', report it as // independent. if (nonisolatedAttr) { - return ActorIsolation::forNonisolated(); + return ActorIsolation::forNonisolated(nonisolatedAttr->isUnsafe()); } // If the declaration is marked with a global actor, report it as being @@ -3824,6 +3837,7 @@ getIsolationFromWitnessedRequirements(ValueDecl *value) { case ActorIsolation::GlobalActor: case ActorIsolation::GlobalActorUnsafe: case ActorIsolation::Nonisolated: + case ActorIsolation::NonisolatedUnsafe: break; } @@ -3848,6 +3862,7 @@ getIsolationFromWitnessedRequirements(ValueDecl *value) { llvm_unreachable("protocol requirements cannot be actor instances"); case ActorIsolation::Nonisolated: + case ActorIsolation::NonisolatedUnsafe: // We only need one nonisolated. if (sawActorIndependent) return true; @@ -3898,6 +3913,7 @@ getIsolationFromConformances(NominalTypeDecl *nominal) { case ActorIsolation::ActorInstance: case ActorIsolation::Unspecified: case ActorIsolation::Nonisolated: + case ActorIsolation::NonisolatedUnsafe: break; case ActorIsolation::GlobalActor: @@ -3955,6 +3971,7 @@ getIsolationFromWrappers(NominalTypeDecl *nominal) { case ActorIsolation::ActorInstance: case ActorIsolation::Unspecified: case ActorIsolation::Nonisolated: + case ActorIsolation::NonisolatedUnsafe: break; case ActorIsolation::GlobalActor: @@ -4146,6 +4163,7 @@ static bool checkClassGlobalActorIsolation( switch (superIsolation) { case ActorIsolation::Unspecified: case ActorIsolation::Nonisolated: + case ActorIsolation::NonisolatedUnsafe: downgradeToWarning = true; break; @@ -4351,21 +4369,25 @@ ActorIsolation ActorIsolationRequest::evaluate( ActorIsolation isolation) { if (var && var->getLoc() && var->getASTContext().LangOpts.hasFeature(Feature::GlobalConcurrency) && - !isolation.isGlobalActor()) { - const bool isGlobalState = - var->isStatic() || var->getDeclContext()->isModuleScopeContext() || - (var->getDeclContext()->isTypeContext() && !var->isInstanceMember()); + !isolation.isGlobalActor() && + (isolation != ActorIsolation::NonisolatedUnsafe)) { auto *classDecl = var->getDeclContext()->getSelfClassDecl(); const bool isActorType = classDecl && classDecl->isAnyActor(); - if (isGlobalState && !isActorType) { + if (var->isGlobalStorage() && !isActorType) { + auto *diagVar = var; + if (auto *originalVar = var->getOriginalWrappedProperty()) { + diagVar = originalVar; + } if (var->isLet()) { if (!isSendableType(var->getModuleContext(), var->getInterfaceType())) { - var->diagnose(diag::shared_immutable_state_decl, var); + diagVar->diagnose(diag::shared_immutable_state_decl, diagVar) + .warnUntilSwiftVersion(6); } } else { - var->diagnose(diag::shared_mutable_state_decl, var); - var->diagnose(diag::shared_mutable_state_decl_note, var); + diagVar->diagnose(diag::shared_mutable_state_decl, diagVar) + .warnUntilSwiftVersion(6); + diagVar->diagnose(diag::shared_mutable_state_decl_note, diagVar); } } } @@ -4408,7 +4430,7 @@ ActorIsolation ActorIsolationRequest::evaluate( if (auto func = dyn_cast(value)) { // A @Sendable function is assumed to be actor-independent. if (func->isSendable()) { - defaultIsolation = ActorIsolation::forNonisolated(); + defaultIsolation = ActorIsolation::forNonisolated(/*unsafe=*/false); } } @@ -4417,7 +4439,7 @@ ActorIsolation ActorIsolationRequest::evaluate( if (nominal->isAnyActor()) if (auto ctor = dyn_cast(value)) if (!ctor->hasAsync()) - defaultIsolation = ActorIsolation::forNonisolated(); + defaultIsolation = ActorIsolation::forNonisolated(/*unsafe=*/false); // Look for and remember the overridden declaration's isolation. llvm::Optional overriddenIso; @@ -4455,6 +4477,7 @@ ActorIsolation ActorIsolationRequest::evaluate( ASTContext &ctx = value->getASTContext(); switch (inferred) { case ActorIsolation::Nonisolated: + case ActorIsolation::NonisolatedUnsafe: // Stored properties cannot be non-isolated, so don't infer it. if (auto var = dyn_cast(value)) { if (!var->isStatic() && var->hasStorage()) @@ -4467,7 +4490,8 @@ ActorIsolation ActorIsolationRequest::evaluate( inferred.preconcurrency()); } - value->getAttrs().add(new (ctx) NonisolatedAttr(/*IsImplicit=*/true)); + value->getAttrs().add(new (ctx) NonisolatedAttr( + inferred == ActorIsolation::NonisolatedUnsafe, /*implicit=*/true)); break; case ActorIsolation::GlobalActorUnsafe: @@ -4514,6 +4538,7 @@ ActorIsolation ActorIsolationRequest::evaluate( switch (auto enclosingIsolation = getActorIsolationOfContext(func->getDeclContext())) { case ActorIsolation::Nonisolated: + case ActorIsolation::NonisolatedUnsafe: case ActorIsolation::Unspecified: // Do nothing. break; @@ -4684,6 +4709,7 @@ bool HasIsolatedSelfRequest::evaluate( if (auto var = dyn_cast(value)) { switch (auto isolation = getActorIsolationFromWrappedProperty(var)) { case ActorIsolation::Nonisolated: + case ActorIsolation::NonisolatedUnsafe: case ActorIsolation::Unspecified: break; @@ -5029,6 +5055,7 @@ bool swift::checkSendableConformance( case ActorIsolation::Unspecified: case ActorIsolation::ActorInstance: case ActorIsolation::Nonisolated: + case ActorIsolation::NonisolatedUnsafe: break; case ActorIsolation::GlobalActor: @@ -5499,6 +5526,7 @@ AnyFunctionType *swift::adjustFunctionTypeForConcurrency( switch (auto isolation = getActorIsolation(decl)) { case ActorIsolation::ActorInstance: case ActorIsolation::Nonisolated: + case ActorIsolation::NonisolatedUnsafe: case ActorIsolation::Unspecified: return fnType; @@ -5784,6 +5812,7 @@ bool swift::isAccessibleAcrossActors( switch (isolation) { case ActorIsolation::ActorInstance: case ActorIsolation::Nonisolated: + case ActorIsolation::NonisolatedUnsafe: case ActorIsolation::Unspecified: return true; diff --git a/lib/Sema/TypeCheckDeclObjC.cpp b/lib/Sema/TypeCheckDeclObjC.cpp index b35a6eafb9b73..31268c0267f1b 100644 --- a/lib/Sema/TypeCheckDeclObjC.cpp +++ b/lib/Sema/TypeCheckDeclObjC.cpp @@ -492,6 +492,7 @@ static bool checkObjCActorIsolation(const ValueDecl *VD, ObjCReason Reason) { return false; case ActorIsolation::Nonisolated: + case ActorIsolation::NonisolatedUnsafe: case ActorIsolation::Unspecified: return false; } diff --git a/lib/Sema/TypeCheckPropertyWrapper.cpp b/lib/Sema/TypeCheckPropertyWrapper.cpp index 2e8be56ab0c1d..19c9c8adc8276 100644 --- a/lib/Sema/TypeCheckPropertyWrapper.cpp +++ b/lib/Sema/TypeCheckPropertyWrapper.cpp @@ -98,6 +98,7 @@ static VarDecl *findValueProperty(ASTContext &ctx, NominalTypeDecl *nominal, case ActorIsolation::GlobalActor: case ActorIsolation::GlobalActorUnsafe: case ActorIsolation::Nonisolated: + case ActorIsolation::NonisolatedUnsafe: case ActorIsolation::Unspecified: break; } diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index fa97bcac98b77..e1df9734b23fa 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -464,6 +464,7 @@ getActualActorIsolationKind(uint8_t raw) { CASE(Unspecified) CASE(ActorInstance) CASE(Nonisolated) + CASE(NonisolatedUnsafe) CASE(GlobalActor) CASE(GlobalActorUnsafe) #undef CASE @@ -3841,6 +3842,7 @@ class DeclDeserializer { switch (isoKind) { case ActorIsolation::Unspecified: case ActorIsolation::Nonisolated: + case ActorIsolation::NonisolatedUnsafe: isolation = ActorIsolation::forUnspecified(); break; @@ -5915,6 +5917,15 @@ llvm::Error DeclDeserializer::deserializeDeclCommon() { break; } + case decls_block::Nonisolated_DECL_ATTR: { + bool isUnsafe{}; + bool isImplicit{}; + serialization::decls_block::NonisolatedDeclAttrLayout::readRecord( + scratch, isUnsafe, isImplicit); + Attr = new (ctx) NonisolatedAttr(isUnsafe, isImplicit); + break; + } + case decls_block::MacroRole_DECL_ATTR: { bool isImplicit; uint8_t rawMacroSyntax; diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index e51ab291545f0..ef23709cf9255 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 814; // @extern(c) +const uint16_t SWIFTMODULE_VERSION_MINOR = 815; // nonisolated(unsafe) /// A standard hash seed used for all string hashes in a serialized module. /// @@ -532,6 +532,7 @@ enum class ActorIsolation : uint8_t { Unspecified = 0, ActorInstance, Nonisolated, + NonisolatedUnsafe, GlobalActor, GlobalActorUnsafe }; @@ -2356,6 +2357,12 @@ namespace decls_block { AccessLevelField // visibility >; + using NonisolatedDeclAttrLayout = + BCRecordLayout, // is the argument (unsafe) + BCFixed<1> // implicit flag + >; + using MacroRoleDeclAttrLayout = BCRecordLayout< MacroRole_DECL_ATTR, BCFixed<1>, // implicit flag diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index a043aaf4dda92..96ca9744c8424 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -1417,6 +1417,7 @@ getRawStableActorIsolationKind(swift::ActorIsolation::Kind kind) { CASE(Unspecified) CASE(ActorInstance) CASE(Nonisolated) + CASE(NonisolatedUnsafe) CASE(GlobalActor) CASE(GlobalActorUnsafe) #undef CASE @@ -3172,6 +3173,15 @@ class Serializer::DeclSerializer : public DeclVisitor { return; } + case DAK_Nonisolated: { + auto *theAttr = cast(DA); + auto abbrCode = S.DeclTypeAbbrCodes[NonisolatedDeclAttrLayout::Code]; + NonisolatedDeclAttrLayout::emitRecord(S.Out, S.ScratchRecord, abbrCode, + theAttr->isUnsafe(), + theAttr->isImplicit()); + return; + } + case DAK_MacroRole: { auto *theAttr = cast(DA); auto abbrCode = S.DeclTypeAbbrCodes[MacroRoleDeclAttrLayout::Code]; diff --git a/test/Concurrency/experimental_feature_strictconcurrency.swift b/test/Concurrency/experimental_feature_strictconcurrency.swift index 348adc3de7bcd..007b384c47987 100644 --- a/test/Concurrency/experimental_feature_strictconcurrency.swift +++ b/test/Concurrency/experimental_feature_strictconcurrency.swift @@ -45,18 +45,31 @@ final class TestNonsendable { init() {} } -struct A { +@propertyWrapper +public struct TestWrapper { + public init() {} + public var wrappedValue: Int { + return 0 + } +} + +struct TestStatics { static let immutableExplicitSendable = TestSendable() - static let immutableNonsendableGlobal = TestNonsendable() // expected-warning{{static property 'immutableNonsendableGlobal' is not concurrency-safe because it is not either conforming to 'Sendable' or isolated to a global actor}} + static let immutableNonsendable = TestNonsendable() // expected-warning{{static property 'immutableNonsendable' is not concurrency-safe because it is not either conforming to 'Sendable' or isolated to a global actor}} + static nonisolated(unsafe) let immutableNonisolatedUnsafe = TestNonsendable() + static nonisolated let immutableNonisolated = TestNonsendable() // expected-warning{{static property 'immutableNonisolated' is not concurrency-safe because it is not either conforming to 'Sendable' or isolated to a global actor}} static let immutableInferredSendable = 0 static var mutable = 0 // expected-warning{{static property 'mutable' is not concurrency-safe because it is non-isolated global shared mutable state}} // expected-note@-1{{isolate 'mutable' to a global actor, or convert it to a 'let' constant and conform it to 'Sendable'}} // expected-note@-2{{static property declared here}} + static var computedProperty: Int { 0 } // computed property that, though static, has no storage so is not a global + @TestWrapper static var wrapped: Int // expected-warning{{static property 'wrapped' is not concurrency-safe because it is non-isolated global shared mutable state}} + // expected-note@-1{{isolate 'wrapped' to a global actor, or convert it to a 'let' constant and conform it to 'Sendable'}} } @TestGlobalActor func f() { - print(A.immutableExplicitSendable) - print(A.immutableInferredSendable) - print(A.mutable) // expected-warning{{reference to static property 'mutable' is not concurrency-safe because it involves shared mutable state}} + print(TestStatics.immutableExplicitSendable) + print(TestStatics.immutableInferredSendable) + print(TestStatics.mutable) // expected-warning{{reference to static property 'mutable' is not concurrency-safe because it involves shared mutable state}} } diff --git a/test/Serialization/attr-nonisolated.swift b/test/Serialization/attr-nonisolated.swift index f274966a5ea89..1ecd9f8861a97 100644 --- a/test/Serialization/attr-nonisolated.swift +++ b/test/Serialization/attr-nonisolated.swift @@ -19,7 +19,7 @@ // and look for nonisolated -// BC-CHECK: +// BC-CHECK: actor UnsafeCounter {