diff --git a/include/swift/AST/ASTBridging.h b/include/swift/AST/ASTBridging.h index cef10ae9a4733..9c15db9050d34 100644 --- a/include/swift/AST/ASTBridging.h +++ b/include/swift/AST/ASTBridging.h @@ -1161,6 +1161,7 @@ enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedTypeAttrKind : size_t { BridgedTypeAttrKind_Sendable, BridgedTypeAttrKind_retroactive, BridgedTypeAttrKind_unchecked, + BridgedTypeAttrKind_preconcurrency, BridgedTypeAttrKind__local, BridgedTypeAttrKind__noMetadata, BridgedTypeAttrKind__opaqueReturnTypeOf, diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index 76fe300545dd8..e7de20703a36e 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -1282,7 +1282,8 @@ class ASTContext final { SourceLoc loc, DeclContext *dc, ProtocolConformanceState state, - bool isUnchecked); + bool isUnchecked, + bool isPreconcurrency); /// Produce a self-conformance for the given protocol. SelfProtocolConformance * diff --git a/include/swift/AST/Attr.def b/include/swift/AST/Attr.def index 4e492979359c9..fdcd1a48935aa 100644 --- a/include/swift/AST/Attr.def +++ b/include/swift/AST/Attr.def @@ -57,6 +57,7 @@ TYPE_ATTR(async) TYPE_ATTR(Sendable) TYPE_ATTR(retroactive) TYPE_ATTR(unchecked) +TYPE_ATTR(preconcurrency) TYPE_ATTR(_local) TYPE_ATTR(_noMetadata) TYPE_ATTR(_opaqueReturnTypeOf) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 0206e5fd36953..c5ff83010a2ef 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -1604,11 +1604,15 @@ struct InheritedEntry : public TypeLoc { /// Whether there was an @retroactive attribute. bool isRetroactive = false; + /// Whether there was an @preconcurrency attribute. + bool isPreconcurrency = false; + InheritedEntry(const TypeLoc &typeLoc); - InheritedEntry(const TypeLoc &typeLoc, bool isUnchecked, bool isRetroactive) - : TypeLoc(typeLoc), isUnchecked(isUnchecked), isRetroactive(isRetroactive) { - } + InheritedEntry(const TypeLoc &typeLoc, bool isUnchecked, bool isRetroactive, + bool isPreconcurrency) + : TypeLoc(typeLoc), isUnchecked(isUnchecked), + isRetroactive(isRetroactive), isPreconcurrency(isPreconcurrency) {} }; /// A wrapper for the collection of inherited types for either a `TypeDecl` or diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 1187d59bc8b97..b503e2320ce75 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -5626,6 +5626,15 @@ ERROR(unchecked_not_inheritance_clause,none, ERROR(unchecked_not_existential,none, "'unchecked' attribute cannot apply to non-protocol type %0", (Type)) +WARNING(preconcurrency_conformance_not_used,none, + "@preconcurrency attribute on conformance to %0 has no effect", (Type)) +ERROR(preconcurrency_not_inheritance_clause,none, + "'preconcurrency' attribute only applies in inheritance clauses", ()) +ERROR(preconcurrency_not_existential,none, + "'preconcurrency' attribute cannot apply to non-protocol type %0", (Type)) +ERROR(preconcurrency_attr_disabled,none, + "attribute requires '-enable-experimental-feature PreconcurrencyConformances'", ()) + ERROR(redundant_any_in_existential,none, "redundant 'any' in type %0", (Type)) diff --git a/include/swift/AST/NameLookup.h b/include/swift/AST/NameLookup.h index fb66e3afd3ebf..a4df7472d44ed 100644 --- a/include/swift/AST/NameLookup.h +++ b/include/swift/AST/NameLookup.h @@ -586,12 +586,15 @@ struct InheritedNominalEntry : Located { /// The location of the "unchecked" attribute, if present. SourceLoc uncheckedLoc; + /// The location of the "preconcurrency" attribute if present. + SourceLoc preconcurrencyLoc; + InheritedNominalEntry() { } - InheritedNominalEntry( - NominalTypeDecl *item, SourceLoc loc, - SourceLoc uncheckedLoc - ) : Located(item, loc), uncheckedLoc(uncheckedLoc) { } + InheritedNominalEntry(NominalTypeDecl *item, SourceLoc loc, + SourceLoc uncheckedLoc, SourceLoc preconcurrencyLoc) + : Located(item, loc), uncheckedLoc(uncheckedLoc), + preconcurrencyLoc(preconcurrencyLoc) {} }; /// Retrieve the set of nominal type declarations that are directly diff --git a/include/swift/AST/ProtocolConformance.h b/include/swift/AST/ProtocolConformance.h index 9f31dcfc57be6..e2f0d31ddf8ff 100644 --- a/include/swift/AST/ProtocolConformance.h +++ b/include/swift/AST/ProtocolConformance.h @@ -444,11 +444,11 @@ class NormalProtocolConformance : public RootProtocolConformance, // Flag bits used in ContextAndBits. enum { - /// The conformance is invalid. - InvalidFlag = 0x01, - /// The conformance was labeled with @unchecked. - UncheckedFlag = 0x02, + UncheckedFlag = 0x01, + + /// The conformance was labeled with @preconcurrency. + PreconcurrencyFlag = 0x02, /// We have allocated the AssociatedConformances array (but not necessarily /// populated any of its elements). @@ -458,10 +458,13 @@ class NormalProtocolConformance : public RootProtocolConformance, /// The declaration context containing the ExtensionDecl or /// NominalTypeDecl that declared the conformance. /// - /// Also stores the "invalid", "unchecked" and "has computed associated + /// Also stores the "unchecked", "preconcurrency" and "has computed associated /// conformances" bits. llvm::PointerIntPair ContextAndBits; + /// Indicates whether the conformance is invalid. + bool Invalid : 1; + /// The reason that this conformance exists. /// /// Either Explicit (e.g. 'struct Foo: Protocol {}' or 'extension Foo: @@ -501,12 +504,14 @@ class NormalProtocolConformance : public RootProtocolConformance, public: NormalProtocolConformance(Type conformingType, ProtocolDecl *protocol, SourceLoc loc, DeclContext *dc, - ProtocolConformanceState state, - bool isUnchecked) + ProtocolConformanceState state, bool isUnchecked, + bool isPreconcurrency) : RootProtocolConformance(ProtocolConformanceKind::Normal, conformingType), ProtocolAndState(protocol, state), Loc(loc), - ContextAndBits(dc, isUnchecked ? UncheckedFlag : 0) { + ContextAndBits(dc, ((isUnchecked ? UncheckedFlag : 0) | + (isPreconcurrency ? PreconcurrencyFlag : 0))), + Invalid(false) { assert(!conformingType->hasArchetype() && "ProtocolConformances should store interface types"); } @@ -543,12 +548,12 @@ class NormalProtocolConformance : public RootProtocolConformance, /// Determine whether this conformance is invalid. bool isInvalid() const { - return ContextAndBits.getInt() & InvalidFlag; + return Invalid; } /// Mark this conformance as invalid. void setInvalid() { - ContextAndBits.setInt(ContextAndBits.getInt() | InvalidFlag); + Invalid = true; } /// Whether this is an "unchecked" conformance. @@ -563,6 +568,9 @@ class NormalProtocolConformance : public RootProtocolConformance, ContextAndBits.setInt(ContextAndBits.getInt() | UncheckedFlag); } + /// Whether this is an preconcurrency conformance. + bool isPreconcurrency() const; + /// Determine whether we've lazily computed the associated conformance array /// already. bool hasComputedAssociatedConformances() const { diff --git a/include/swift/Basic/Features.def b/include/swift/Basic/Features.def index 83f5c469e5e23..92c83d037a01e 100644 --- a/include/swift/Basic/Features.def +++ b/include/swift/Basic/Features.def @@ -270,6 +270,9 @@ EXPERIMENTAL_FEATURE(GroupActorErrors, true) // Allow for the 'transferring' keyword to be applied to arguments and results. EXPERIMENTAL_FEATURE(TransferringArgsAndResults, true) +// Enable `@preconcurrency` attribute on protocol conformances. +EXPERIMENTAL_FEATURE(PreconcurrencyConformances, false) + #undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE #undef EXPERIMENTAL_FEATURE #undef UPCOMING_FEATURE diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 8adb17c8f6023..6658400c287f9 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -2490,7 +2490,8 @@ ASTContext::getNormalConformance(Type conformingType, SourceLoc loc, DeclContext *dc, ProtocolConformanceState state, - bool isUnchecked) { + bool isUnchecked, + bool isPreconcurrency) { assert(dc->isTypeContext()); llvm::FoldingSetNodeID id; @@ -2504,10 +2505,9 @@ ASTContext::getNormalConformance(Type conformingType, return result; // Build a new normal protocol conformance. - auto result - = new (*this, AllocationArena::Permanent) - NormalProtocolConformance( - conformingType, protocol, loc, dc, state,isUnchecked); + auto result = new (*this, AllocationArena::Permanent) + NormalProtocolConformance(conformingType, protocol, loc, dc, state, + isUnchecked, isPreconcurrency); normalConformances.InsertNode(result, insertPos); return result; diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 22b91ebadd572..f1c7582de389b 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -2732,6 +2732,8 @@ void PrintAST::printInherited(const Decl *decl) { if (inherited.isRetroactive && !llvm::is_contained(Options.ExcludeAttrList, TAK_retroactive)) Printer << "@retroactive "; + if (inherited.isPreconcurrency) + Printer << "@preconcurrency "; printTypeLoc(inherited); }, [&]() { @@ -3906,6 +3908,30 @@ static bool usesFeatureBitwiseCopyable(Decl *decl) { return false; } static bool usesFeatureTransferringArgsAndResults(Decl *decl) { return false; } +static bool usesFeaturePreconcurrencyConformances(Decl *decl) { + auto usesPreconcurrencyConformance = [&](const InheritedTypes &inherited) { + return llvm::any_of( + inherited.getEntries(), + [](const InheritedEntry &entry) { return entry.isPreconcurrency; }); + }; + + if (auto *T = dyn_cast(decl)) + return usesPreconcurrencyConformance(T->getInherited()); + + if (auto *E = dyn_cast(decl)) { + // If type has `@preconcurrency` conformance(s) all of its + // extensions have to be guarded by the flag too. + if (auto *T = dyn_cast(E->getExtendedNominal())) { + if (usesPreconcurrencyConformance(T->getInherited())) + return true; + } + + return usesPreconcurrencyConformance(E->getInherited()); + } + + return false; +} + /// Suppress the printing of a particular feature. static void suppressingFeature(PrintOptions &options, Feature feature, llvm::function_ref action) { @@ -8308,7 +8334,8 @@ swift::getInheritedForPrinting( Results.push_back({TypeLoc::withoutLoc(proto->getDeclaredInterfaceType()), isUnchecked, - /*isRetroactive=*/false}); + /*isRetroactive=*/false, + /*isPreconcurrency=*/false}); } } diff --git a/lib/AST/ConformanceLookupTable.cpp b/lib/AST/ConformanceLookupTable.cpp index dd2303435754f..acfbd92534a94 100644 --- a/lib/AST/ConformanceLookupTable.cpp +++ b/lib/AST/ConformanceLookupTable.cpp @@ -137,15 +137,19 @@ void ConformanceLookupTable::destroy() { namespace { struct ConformanceConstructionInfo : public Located { - /// The location of the "unchecked" attribute, if this + /// The location of the "unchecked" attribute, if present. const SourceLoc uncheckedLoc; + /// The location of the "preconcurrency" attribute if present. + const SourceLoc preconcurrencyLoc; + ConformanceConstructionInfo() { } - ConformanceConstructionInfo( - ProtocolDecl *item, SourceLoc loc, - SourceLoc uncheckedLoc - ) : Located(item, loc), uncheckedLoc(uncheckedLoc) { } + ConformanceConstructionInfo(ProtocolDecl *item, SourceLoc loc, + SourceLoc uncheckedLoc, + SourceLoc preconcurrencyLoc) + : Located(item, loc), uncheckedLoc(uncheckedLoc), + preconcurrencyLoc(preconcurrencyLoc) {} }; } @@ -200,7 +204,8 @@ void ConformanceLookupTable::forEachInStage(ConformanceStage stage, loader.first->loadAllConformances(next, loader.second, conformances); loadAllConformances(next, conformances); for (auto conf : conformances) { - protocols.push_back({conf->getProtocol(), SourceLoc(), SourceLoc()}); + protocols.push_back( + {conf->getProtocol(), SourceLoc(), SourceLoc(), SourceLoc()}); } } else if (next->getParentSourceFile() || next->getParentModule()->isBuiltinModule()) { @@ -208,7 +213,8 @@ void ConformanceLookupTable::forEachInStage(ConformanceStage stage, for (const auto &found : getDirectlyInheritedNominalTypeDecls(next, anyObject)) { if (auto proto = dyn_cast(found.Item)) - protocols.push_back({proto, found.Loc, found.uncheckedLoc}); + protocols.push_back( + {proto, found.Loc, found.uncheckedLoc, found.preconcurrencyLoc}); } } @@ -294,15 +300,15 @@ void ConformanceLookupTable::updateLookupTable(NominalTypeDecl *nominal, addMacroGeneratedProtocols( nominal, ConformanceSource::forUnexpandedMacro(nominal)); }, - [&](ExtensionDecl *ext, - ArrayRef protos) { + [&](ExtensionDecl *ext, ArrayRef protos) { // The extension decl may not be validated, so we can't use // its inherited protocols directly. auto source = ConformanceSource::forExplicit(ext); for (auto locAndProto : protos) addProtocol( - locAndProto.Item, locAndProto.Loc, - source.withUncheckedLoc(locAndProto.uncheckedLoc)); + locAndProto.Item, locAndProto.Loc, + source.withUncheckedLoc(locAndProto.uncheckedLoc) + .withPreconcurrencyLoc(locAndProto.preconcurrencyLoc)); }); break; @@ -495,8 +501,9 @@ void ConformanceLookupTable::addInheritedProtocols( for (const auto &found : getDirectlyInheritedNominalTypeDecls(decl, anyObject)) { if (auto proto = dyn_cast(found.Item)) { - addProtocol( - proto, found.Loc, source.withUncheckedLoc(found.uncheckedLoc)); + addProtocol(proto, found.Loc, + source.withUncheckedLoc(found.uncheckedLoc) + .withPreconcurrencyLoc(found.preconcurrencyLoc)); } } } @@ -953,10 +960,11 @@ ConformanceLookupTable::getConformance(NominalTypeDecl *nominal, } // Create or find the normal conformance. - auto normalConf = - ctx.getNormalConformance(conformingType, protocol, conformanceLoc, - conformingDC, ProtocolConformanceState::Incomplete, - entry->Source.getUncheckedLoc().isValid()); + auto normalConf = ctx.getNormalConformance( + conformingType, protocol, conformanceLoc, conformingDC, + ProtocolConformanceState::Incomplete, + entry->Source.getUncheckedLoc().isValid(), + entry->Source.getPreconcurrencyLoc().isValid()); // Invalid code may cause the getConformance call below to loop, so break // the infinite recursion by setting this eagerly to shortcircuit with the // early return at the start of this function. diff --git a/lib/AST/ConformanceLookupTable.h b/lib/AST/ConformanceLookupTable.h index cc41e28d306c7..bb9727f90414f 100644 --- a/lib/AST/ConformanceLookupTable.h +++ b/lib/AST/ConformanceLookupTable.h @@ -91,6 +91,9 @@ class ConformanceLookupTable : public ASTAllocated { /// The location of the "unchecked" attribute, if there is one. SourceLoc uncheckedLoc; + /// The location of the "preconcurrency" attribute, if there is one. + SourceLoc preconcurrencyLoc; + ConformanceSource(void *ptr, ConformanceEntryKind kind) : Storage(ptr), Kind(kind) { } @@ -141,6 +144,15 @@ class ConformanceLookupTable : public ASTAllocated { return result; } + /// Return a new conformance source with the given location of + /// "@preconcurrency". + ConformanceSource withPreconcurrencyLoc(SourceLoc preconcurrencyLoc) { + ConformanceSource result(*this); + if (preconcurrencyLoc.isValid()) + result.preconcurrencyLoc = preconcurrencyLoc; + return result; + } + /// Retrieve the kind of conformance formed from this source. ConformanceEntryKind getKind() const { return Kind; } @@ -184,6 +196,10 @@ class ConformanceLookupTable : public ASTAllocated { return uncheckedLoc; } + SourceLoc getPreconcurrencyLoc() const { + return preconcurrencyLoc; + } + /// For an inherited conformance, retrieve the class declaration /// for the inheriting class. ClassDecl *getInheritingClass() const { diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 97f419e6bd4dd..489829884071c 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -1592,6 +1592,7 @@ InheritedEntry::InheritedEntry(const TypeLoc &typeLoc) if (auto typeRepr = typeLoc.getTypeRepr()) { isUnchecked = typeRepr->findAttrLoc(TAK_unchecked).isValid(); isRetroactive = typeRepr->findAttrLoc(TAK_retroactive).isValid(); + isPreconcurrency = typeRepr->findAttrLoc(TAK_preconcurrency).isValid(); } } diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index 2a2cfa40399c3..d0c110dc5f2c2 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -3687,15 +3687,17 @@ void swift::getDirectlyInheritedNominalTypeDecls( // InheritedDeclsReferencedRequest to make this work. SourceLoc loc; SourceLoc uncheckedLoc; + SourceLoc preconcurrencyLoc; auto inheritedTypes = InheritedTypes(decl); if (TypeRepr *typeRepr = inheritedTypes.getTypeRepr(i)) { loc = typeRepr->getLoc(); uncheckedLoc = typeRepr->findAttrLoc(TAK_unchecked); + preconcurrencyLoc = typeRepr->findAttrLoc(TAK_preconcurrency); } // Form the result. for (auto nominal : nominalTypes) { - result.push_back({nominal, loc, uncheckedLoc}); + result.push_back({nominal, loc, uncheckedLoc, preconcurrencyLoc}); } } @@ -3733,7 +3735,7 @@ swift::getDirectlyInheritedNominalTypeDecls( if (!req.getFirstType()->isEqual(protoSelfTy)) continue; - result.emplace_back(req.getProtocolDecl(), loc, SourceLoc()); + result.emplace_back(req.getProtocolDecl(), loc, SourceLoc(), SourceLoc()); } return result; } @@ -3744,8 +3746,9 @@ swift::getDirectlyInheritedNominalTypeDecls( for (auto attr : protoDecl->getAttrs().getAttributes()) { auto loc = attr->getLocation(); - result.push_back( - {attr->getProtocol(), loc, attr->isUnchecked() ? loc : SourceLoc()}); + result.push_back({attr->getProtocol(), loc, + attr->isUnchecked() ? loc : SourceLoc(), + /*preconcurrencyLoc=*/SourceLoc()}); } // Else we have access to this information on the where clause. @@ -3753,7 +3756,7 @@ swift::getDirectlyInheritedNominalTypeDecls( anyObject |= selfBounds.anyObject; for (auto inheritedNominal : selfBounds.decls) - result.emplace_back(inheritedNominal, loc, SourceLoc()); + result.emplace_back(inheritedNominal, loc, SourceLoc(), SourceLoc()); return result; } diff --git a/lib/AST/ProtocolConformance.cpp b/lib/AST/ProtocolConformance.cpp index cb0343dac85f9..69c8a4f94928f 100644 --- a/lib/AST/ProtocolConformance.cpp +++ b/lib/AST/ProtocolConformance.cpp @@ -338,6 +338,11 @@ bool NormalProtocolConformance::isResilient() const { return getDeclContext()->getParentModule()->isResilient(); } +bool NormalProtocolConformance::isPreconcurrency() const { + // The conformance is explicitly marked as `@preconcurrency`. + return ContextAndBits.getInt() & PreconcurrencyFlag; +} + llvm::Optional> ProtocolConformance::getConditionalRequirementsIfAvailable() const { CONFORMANCE_SUBCLASS_DISPATCH(getConditionalRequirementsIfAvailable, ()); diff --git a/lib/ASTGen/Sources/ASTGen/Types.swift b/lib/ASTGen/Sources/ASTGen/Types.swift index 1928a0c6a5a15..647593af84501 100644 --- a/lib/ASTGen/Sources/ASTGen/Types.swift +++ b/lib/ASTGen/Sources/ASTGen/Types.swift @@ -419,8 +419,8 @@ extension ASTGenVisitor { fallthrough case .autoclosure, .escaping, .noescape, .noDerivative, .async, - .sendable, .retroactive, .unchecked, ._local, ._noMetadata, - .pack_owned, .pack_guaranteed, .pack_inout, .pack_out, + .sendable, .retroactive, .unchecked, .preconcurrency, ._local, + ._noMetadata, .pack_owned, .pack_guaranteed, .pack_inout, .pack_out, .pseudogeneric, .yields, .yield_once, .yield_many, .thin, .thick, .count, .unimplementable: typeAttributes.addSimpleAttr(kind: typeAttrKind, atLoc: atLoc, attrLoc: attrLoc) diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 8b782a8750e79..a0157ccae8fb1 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -7273,9 +7273,9 @@ void SwiftDeclConverter::importObjCProtocols( Impl.importDecl(*cp, getActiveSwiftVersion()))) { addProtocols(proto, protocols, knownProtocols); inheritedTypes.push_back( - InheritedEntry( - TypeLoc::withoutLoc(proto->getDeclaredInterfaceType()), - /*isUnchecked=*/false, /*isRetroactive=*/false)); + InheritedEntry(TypeLoc::withoutLoc(proto->getDeclaredInterfaceType()), + /*isUnchecked=*/false, /*isRetroactive=*/false, + /*isPreconcurrency=*/false)); } } @@ -9453,7 +9453,8 @@ void ClangImporter::Implementation::loadAllConformances( dc->getDeclaredInterfaceType(), protocol, SourceLoc(), dc, ProtocolConformanceState::Incomplete, - protocol->isSpecificProtocol(KnownProtocolKind::Sendable)); + protocol->isSpecificProtocol(KnownProtocolKind::Sendable), + /*isPreconcurrency=*/false); conformance->setLazyLoader(this, /*context*/0); conformance->setState(ProtocolConformanceState::Complete); Conformances.push_back(conformance); diff --git a/lib/Frontend/ModuleInterfaceSupport.cpp b/lib/Frontend/ModuleInterfaceSupport.cpp index d65629c193325..497b4b8e46d79 100644 --- a/lib/Frontend/ModuleInterfaceSupport.cpp +++ b/lib/Frontend/ModuleInterfaceSupport.cpp @@ -746,7 +746,8 @@ class InheritedProtocolCollector { ASTContext &ctx = M->getASTContext(); auto inherits = ctx.AllocateCopy(llvm::makeArrayRef(InheritedEntry( TypeLoc::withoutLoc(proto->getDeclaredInterfaceType()), isUnchecked, - /*isRetroactive=*/false))); + /*isRetroactive=*/false, + /*isPreconcurrency=*/false))); auto extension = ExtensionDecl::create(ctx, SourceLoc(), nullptr, inherits, nominal->getModuleScopeContext(), nullptr); diff --git a/lib/SILGen/SILGenBridging.cpp b/lib/SILGen/SILGenBridging.cpp index 9ea5d3cc05411..14f6079f160f8 100644 --- a/lib/SILGen/SILGenBridging.cpp +++ b/lib/SILGen/SILGenBridging.cpp @@ -1592,22 +1592,29 @@ void SILGenFunction::emitNativeToForeignThunk(SILDeclRef thunk) { auto loc = thunk.getAsRegularLocation(); loc.markAutoGenerated(); Scope scope(Cleanups, CleanupLocation(loc)); - - // Hop to the actor for the method's actor constraint, if any. - // Note that, since an async native-to-foreign thunk only ever runs in a - // task purpose-built for running the Swift async code triggering the - // completion handler, there is no need for us to hop back to the existing - // executor, since the task will end after we invoke the completion handler. - if (F.isAsync()) { - llvm::Optional isolation; - if (thunk.hasDecl()) { - isolation = getActorIsolation(thunk.getDecl()); - } - // A hop is only needed in the thunk if it is global-actor isolated. - // Native, instance-isolated async methods will hop in the prologue. - if (isolation && isolation->isGlobalActor()) { + bool emitExecutorPrecondition = + getASTContext().LangOpts.hasFeature(Feature::PreconcurrencyConformances); + + llvm::Optional isolation; + if ((F.isAsync() || emitExecutorPrecondition) && thunk.hasDecl()) { + isolation = getActorIsolation(thunk.getDecl()); + } + + // A hop/check is only needed in the thunk if it is global-actor isolated. + // Native, instance-isolated async methods will hop in the prologue. + if (isolation && isolation->isGlobalActor()) { + if (F.isAsync()) { + // Hop to the actor for the method's actor constraint. + // Note that, since an async native-to-foreign thunk only ever runs in a + // task purpose-built for running the Swift async code triggering the + // completion handler, there is no need for us to hop back to the existing + // executor, since the task will end after we invoke the completion handler. emitPrologGlobalActorHop(loc, isolation->getGlobalActor()); + } else { + auto executor = + emitLoadGlobalActorExecutor(isolation->getGlobalActor()); + emitPreconditionCheckExpectedExecutor(loc, executor); } } diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index 1ee8b72c4be72..c9eda3f00bf73 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -946,12 +946,16 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction /// /// This is used for both concrete witness thunks and default witness /// thunks. + /// + /// \param isPreconcurrency If the conformance is marked as `@preconcurrency` + /// instead of a hop (when entering isolation) emit a dynamic check to make + /// sure that witness has been unsed in the expected context. void emitProtocolWitness(AbstractionPattern reqtOrigTy, CanAnyFunctionType reqtSubstTy, SILDeclRef requirement, SubstitutionMap reqtSubs, SILDeclRef witness, SubstitutionMap witnessSubs, IsFreeFunctionWitness_t isFree, - bool isSelfConformance, + bool isSelfConformance, bool isPreconcurrency, llvm::Optional enterIsolation); /// Generates subscript arguments for keypath. This function handles lowering diff --git a/lib/SILGen/SILGenPoly.cpp b/lib/SILGen/SILGenPoly.cpp index ab92257a4aa01..91a1f104a7838 100644 --- a/lib/SILGen/SILGenPoly.cpp +++ b/lib/SILGen/SILGenPoly.cpp @@ -6861,7 +6861,8 @@ void SILGenFunction::emitProtocolWitness( AbstractionPattern reqtOrigTy, CanAnyFunctionType reqtSubstTy, SILDeclRef requirement, SubstitutionMap reqtSubs, SILDeclRef witness, SubstitutionMap witnessSubs, IsFreeFunctionWitness_t isFree, - bool isSelfConformance, llvm::Optional enterIsolation) { + bool isSelfConformance, bool isPreconcurrency, + llvm::Optional enterIsolation) { // FIXME: Disable checks that the protocol witness carries debug info. // Should we carry debug info for witnesses? F.setBare(IsBare); @@ -6905,7 +6906,13 @@ void SILGenFunction::emitProtocolWitness( actorSelf = actorSelfVal; } - emitHopToTargetActor(loc, enterIsolation, actorSelf); + if (!F.isAsync()) { + assert(isPreconcurrency); + auto executor = emitExecutor(loc, *enterIsolation, actorSelf); + emitPreconditionCheckExpectedExecutor(loc, *executor); + } else { + emitHopToTargetActor(loc, enterIsolation, actorSelf); + } } // Get the type of the witness. diff --git a/lib/SILGen/SILGenType.cpp b/lib/SILGen/SILGenType.cpp index c77a5b8da4b91..497c47932f2b0 100644 --- a/lib/SILGen/SILGenType.cpp +++ b/lib/SILGen/SILGenType.cpp @@ -834,9 +834,21 @@ SILFunction *SILGenModule::emitProtocolWitness( // archetypes of the witness thunk generic environment. auto witnessSubs = witness.getSubstitutions(); + // If the conformance is marked as `@preconcurrency` instead of + // emitting a hop to the executor (when needed) emit a dynamic check + // to make sure that witness has been unsed in the expected context. + bool isPreconcurrency = false; + if (conformance.isConcrete()) { + if (auto *C = + dyn_cast(conformance.getConcrete())) + isPreconcurrency = C->isPreconcurrency(); + } + SGF.emitProtocolWitness(AbstractionPattern(reqtOrigTy), reqtSubstTy, requirement, reqtSubMap, witnessRef, - witnessSubs, isFree, /*isSelfConformance*/ false, + witnessSubs, isFree, + /*isSelfConformance*/ false, + isPreconcurrency, witness.getEnterIsolation()); emitLazyConformancesForFunction(f); @@ -910,7 +922,8 @@ static SILFunction *emitSelfConformanceWitness(SILGenModule &SGM, SGF.emitProtocolWitness(AbstractionPattern(reqtOrigTy), reqtSubstTy, requirement, reqtSubs, requirement, witnessSubs, - isFree, /*isSelfConformance*/ true, llvm::None); + isFree, /*isSelfConformance*/ true, + /*isPreconcurrency*/ false, llvm::None); SGM.emitLazyConformancesForFunction(f); diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index e6ed30b8bebfa..49e0069d137e4 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -3456,7 +3456,8 @@ bool ContextualFailure::tryProtocolConformanceFixIt( for (auto protocol : missingProtocols) { auto conformance = NormalProtocolConformance( nominal->getDeclaredType(), protocol, SourceLoc(), nominal, - ProtocolConformanceState::Incomplete, /*isUnchecked=*/false); + ProtocolConformanceState::Incomplete, /*isUnchecked=*/false, + /*isPreconcurrency=*/false); ConformanceChecker checker(getASTContext(), &conformance, missingWitnesses); // Type witnesses must be resolved first. diff --git a/lib/Sema/CodeSynthesisDistributedActor.cpp b/lib/Sema/CodeSynthesisDistributedActor.cpp index c466d30c3ac67..ebf15e362ca37 100644 --- a/lib/Sema/CodeSynthesisDistributedActor.cpp +++ b/lib/Sema/CodeSynthesisDistributedActor.cpp @@ -811,7 +811,8 @@ addDistributedActorCodableConformance( actor->getDeclaredInterfaceType(), proto, actor->getLoc(), /*dc=*/actor, ProtocolConformanceState::Incomplete, - /*isUnchecked=*/false); + /*isUnchecked=*/false, + /*isPreconcurrency=*/false); conformance->setSourceKindAndImplyingConformance( ConformanceEntryKind::Synthesized, nullptr); actor->registerProtocolConformance(conformance, /*synthesized=*/true); diff --git a/lib/Sema/TypeCheckBitwise.cpp b/lib/Sema/TypeCheckBitwise.cpp index 2326af58ad40d..0f9dd13a6f1cd 100644 --- a/lib/Sema/TypeCheckBitwise.cpp +++ b/lib/Sema/TypeCheckBitwise.cpp @@ -322,7 +322,8 @@ DeriveImplicitBitwiseCopyableConformance::synthesizeConformance( auto conformance = context.getNormalConformance( nominal->getDeclaredInterfaceType(), protocol, nominal->getLoc(), dc, ProtocolConformanceState::Complete, - /*isUnchecked=*/false); + /*isUnchecked=*/false, + /*isPreconcurrency=*/false); conformance->setSourceKindAndImplyingConformance( ConformanceEntryKind::Synthesized, nullptr); diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 4e745776be3cf..86612c067f579 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -5556,7 +5556,8 @@ ProtocolConformance *swift::deriveImplicitSendableConformance( // FIXME: This is a hack--we should give conformances real availability. auto inherits = ctx.AllocateCopy(makeArrayRef( InheritedEntry(TypeLoc::withoutLoc(proto->getDeclaredInterfaceType()), - /*isUnchecked*/true, /*isRetroactive=*/false))); + /*isUnchecked*/ true, /*isRetroactive=*/false, + /*isPreconcurrency=*/false))); // If you change the use of AtLoc in the ExtensionDecl, make sure you // update isNonSendableExtension() in ASTPrinter. auto extension = ExtensionDecl::create(ctx, attrMakingUnavailable->AtLoc, @@ -5582,7 +5583,8 @@ ProtocolConformance *swift::deriveImplicitSendableConformance( auto conformance = ctx.getNormalConformance( nominal->getDeclaredInterfaceType(), proto, nominal->getLoc(), conformanceDC, ProtocolConformanceState::Complete, - /*isUnchecked=*/attrMakingUnavailable != nullptr); + /*isUnchecked=*/attrMakingUnavailable != nullptr, + /*isPreconcurrency=*/false); conformance->setSourceKindAndImplyingConformance( ConformanceEntryKind::Synthesized, nullptr); diff --git a/lib/Sema/TypeCheckInvertible.cpp b/lib/Sema/TypeCheckInvertible.cpp index f220d8801a08e..c343b2c4d461c 100644 --- a/lib/Sema/TypeCheckInvertible.cpp +++ b/lib/Sema/TypeCheckInvertible.cpp @@ -396,7 +396,7 @@ ProtocolConformance *deriveConformanceForInvertible(Evaluator &evaluator, auto conformance = ctx.getNormalConformance( nominal->getDeclaredInterfaceType(), proto, nominal->getLoc(), conformanceDC, ProtocolConformanceState::Complete, - /*isUnchecked=*/false); + /*isUnchecked=*/false, /*isPreconcurrency=*/false); conformance->setSourceKindAndImplyingConformance( ConformanceEntryKind::Synthesized, nullptr); diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 5ad2b2ee69655..98268315bdac8 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -3094,6 +3094,14 @@ ConformanceChecker::checkActorIsolation(ValueDecl *requirement, getDeclRefInContext(witness), witness->getLoc(), DC, llvm::None, llvm::None, llvm::None, requirementIsolation); bool sameConcurrencyDomain = false; + // If the requirement is isolated (explicitly or implicitly) or + // explicitly marked as `nonisolated` it means that the protocol + // has adopted concurrency and `@preconcurrency` doesn't apply. + bool isPreconcurrency = + Conformance->isPreconcurrency() && + !(requirementIsolation.isActorIsolated() || + requirement->getAttrs().hasAttribute()); + switch (refResult) { case ActorReferenceResult::SameConcurrencyDomain: // If the witness has distributed-actor isolation, we have extra @@ -3182,8 +3190,9 @@ ConformanceChecker::checkActorIsolation(ValueDecl *requirement, } } - // If we aren't missing anything, do a Sendable check and move on. - if (!missingOptions) { + // If we aren't missing anything or this is a witness to a `@preconcurrency` + // conformance, do a Sendable check and move on. + if (!missingOptions || isPreconcurrency) { // FIXME: Disable Sendable checking when the witness is an initializer // that is explicitly marked nonisolated. if (isa(witness) && @@ -3210,9 +3219,15 @@ ConformanceChecker::checkActorIsolation(ValueDecl *requirement, if (isAccessibleAcrossActors(witness, refResult.isolation, DC)) return llvm::None; - if (refResult.isolation.isActorIsolated() && isAsyncDecl(requirement) && - !isAsyncDecl(witness)) - return refResult.isolation; + if (refResult.isolation.isActorIsolated()) { + if (isAsyncDecl(requirement) && !isAsyncDecl(witness)) + return refResult.isolation; + + // Always treat `@preconcurrency` witnesses as isolated. + if (isPreconcurrency && + missingOptions.contains(MissingFlags::RequirementAsync)) + return refResult.isolation; + } return llvm::None; } @@ -5266,6 +5281,7 @@ hasInvalidTypeInConformanceContext(const ValueDecl *requirement, } void ConformanceChecker::resolveValueWitnesses() { + bool usesPreconcurrencyConformance = false; for (auto *requirement : Proto->getProtocolRequirements()) { // Associated type requirements handled elsewhere. if (isa(requirement)) @@ -5284,6 +5300,15 @@ void ConformanceChecker::resolveValueWitnesses() { // Check actor isolation. If we need to enter into the actor's // isolation within the witness thunk, record that. if (auto enteringIsolation = checkActorIsolation(requirement, witness)) { + // Only @preconcurrency conformances allow entering isolation + // when neither requirement nor witness are async by adding a + // runtime precondition that witness is always called on the + // expected executor. + if (Conformance->isPreconcurrency()) { + usesPreconcurrencyConformance = + !isAsyncDecl(requirement) && !isAsyncDecl(witness); + } + Conformance->overrideWitness( requirement, Conformance->getWitnessUncached(requirement) @@ -5417,6 +5442,12 @@ void ConformanceChecker::resolveValueWitnesses() { } } + if (Conformance->isPreconcurrency() && !usesPreconcurrencyConformance) { + DC->getASTContext().Diags.diagnose( + Conformance->getLoc(), diag::preconcurrency_conformance_not_used, + Proto->getDeclaredInterfaceType()); + } + // Finally, check some ad-hoc protocol requirements. // // These protocol requirements are not expressible in Swift today, but as diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 495fb5bcac016..a424239b7d221 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -3250,6 +3250,33 @@ TypeResolver::resolveAttributedType(TypeAttributes &attrs, TypeRepr *repr, attrs.clearAttribute(TAK_unchecked); } + if (attrs.has(TAK_preconcurrency)) { + auto &ctx = getASTContext(); + if (ctx.LangOpts.hasFeature(Feature::PreconcurrencyConformances)) { + ty = resolveType(repr, options); + if (!ty || ty->hasError()) + return ty; + + if (!options.is(TypeResolverContext::Inherited) || + getDeclContext()->getSelfProtocolDecl()) { + diagnoseInvalid(repr, attrs.getLoc(TAK_preconcurrency), + diag::preconcurrency_not_inheritance_clause); + ty = ErrorType::get(getASTContext()); + } else if (!ty->isConstraintType()) { + diagnoseInvalid(repr, attrs.getLoc(TAK_preconcurrency), + diag::preconcurrency_not_existential, ty); + ty = ErrorType::get(getASTContext()); + } + + // Nothing to record in the type. Just clear the attribute. + attrs.clearAttribute(TAK_preconcurrency); + } else { + diagnoseInvalid(repr, attrs.getLoc(TAK_preconcurrency), + diag::preconcurrency_attr_disabled); + ty = ErrorType::get(getASTContext()); + } + } + if (attrs.has(TAK_retroactive)) { ty = resolveType(repr, options); if (!ty || ty->hasError()) return ty; diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index dd708b4b5d926..37656a576d60c 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -928,13 +928,15 @@ ProtocolConformanceDeserializer::readNormalProtocolConformance( DeclID protoID; DeclContextID contextID; - unsigned valueCount, typeCount, conformanceCount, isUnchecked; + unsigned valueCount, typeCount, conformanceCount, isUnchecked, + isPreconcurrency; ArrayRef rawIDs; NormalProtocolConformanceLayout::readRecord(scratch, protoID, contextID, typeCount, valueCount, conformanceCount, isUnchecked, + isPreconcurrency, rawIDs); auto doOrError = MF.getDeclContextChecked(contextID); @@ -957,7 +959,7 @@ ProtocolConformanceDeserializer::readNormalProtocolConformance( auto conformance = ctx.getNormalConformance( conformingType, proto, SourceLoc(), dc, ProtocolConformanceState::Incomplete, - isUnchecked); + isUnchecked, isPreconcurrency); // Record this conformance. if (conformanceEntry.isComplete()) { assert(conformanceEntry.get() == conformance); @@ -3144,10 +3146,15 @@ class DeclDeserializer { ArrayRef rawInheritedIDs) { SmallVector inheritedTypes; for (auto rawID : rawInheritedIDs) { - // The low bit indicates "@unchecked". + // The first low bit indicates "@preconcurrency". + bool isPreconcurrency = rawID & 0x01; + rawID = rawID >> 1; + + // The second low bit indicates "@unchecked". bool isUnchecked = rawID & 0x01; - TypeID typeID = rawID >> 1; + rawID = rawID >> 1; + TypeID typeID = rawID; auto maybeType = MF.getTypeChecked(typeID); if (!maybeType) { MF.diagnoseAndConsumeError(maybeType.takeError()); @@ -3155,7 +3162,7 @@ class DeclDeserializer { } inheritedTypes.push_back( InheritedEntry(TypeLoc::withoutLoc(maybeType.get()), isUnchecked, - /*isRetroactive=*/false)); + /*isRetroactive=*/false, isPreconcurrency)); } auto inherited = ctx.AllocateCopy(inheritedTypes); @@ -7984,7 +7991,8 @@ void ModuleFile::finishNormalConformance(NormalProtocolConformance *conformance, DeclID protoID; DeclContextID contextID; - unsigned valueCount, typeCount, conformanceCount, isUnchecked; + unsigned valueCount, typeCount, conformanceCount, isUnchecked, + isPreconcurrency; ArrayRef rawIDs; SmallVector scratch; @@ -7994,10 +8002,9 @@ void ModuleFile::finishNormalConformance(NormalProtocolConformance *conformance, fatal(llvm::make_error(kind, "registered lazy loader incorrectly")); - NormalProtocolConformanceLayout::readRecord(scratch, protoID, - contextID, typeCount, - valueCount, conformanceCount, - isUnchecked, rawIDs); + NormalProtocolConformanceLayout::readRecord( + scratch, protoID, contextID, typeCount, valueCount, conformanceCount, + isUnchecked, isPreconcurrency, rawIDs); // Read requirement signature conformances. SmallVector reqConformances; diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index d14aab432b38c..169fef9976b51 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 = 835; // remove Swift3Inferred +const uint16_t SWIFTMODULE_VERSION_MINOR = 837; // @preconcurrency conformances /// A standard hash seed used for all string hashes in a serialized module. /// @@ -1960,6 +1960,7 @@ namespace decls_block { BCVBR<5>, // value mapping count BCVBR<5>, // requirement signature conformance count BCFixed<1>, // unchecked + BCFixed<1>, // preconcurrency BCArray // The array contains requirement signature conformances, then // type witnesses, then value witnesses. diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index a246ef4bd906b..76061713ef92d 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -1785,6 +1785,7 @@ void Serializer::writeLocalNormalProtocolConformance( numValueWitnesses, numSignatureConformances, conformance->isUnchecked(), + conformance->isPreconcurrency(), data); } @@ -3829,6 +3830,8 @@ class Serializer::DeclSerializer : public DeclVisitor { // Encode "unchecked" in the low bit. typeRef = (typeRef << 1) | (inherited.isUnchecked ? 0x01 : 0x00); + // Encode "preconcurrency" in the low bit. + typeRef = (typeRef << 1) | (inherited.isPreconcurrency ? 0x01 : 0x00); result.push_back(typeRef); } diff --git a/test/ClangImporter/preconcurrency_conformances.swift b/test/ClangImporter/preconcurrency_conformances.swift new file mode 100644 index 0000000000000..6dc1255cc1c52 --- /dev/null +++ b/test/ClangImporter/preconcurrency_conformances.swift @@ -0,0 +1,76 @@ +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -disable-availability-checking -enable-experimental-feature PreconcurrencyConformances -emit-silgen %s | %FileCheck %s + +// REQUIRES: asserts +// REQUIRES: concurrency +// REQUIRES: objc_interop + +import Foundation + +actor MyActor { +} + +@globalActor +struct GlobalActor { + static var shared: MyActor = MyActor() +} + +@objc protocol P { + var data: String? { get set } + + init() + func test() -> Any? +} + +@MainActor +final class K : @preconcurrency P { + var data: String? { + get { nil } + set {} + } + + init() {} + @GlobalActor func test() -> Any? { nil } +} + +// @objc K.data.getter +// CHECK-LABEL: sil private [thunk] [ossa] @$s27preconcurrency_conformances1KC4dataSSSgvgTo : $@convention(objc_method) (K) -> @autoreleased Optional +// CHECK: [[MAIN_ACTOR_METATYPE:%.*]] = metatype $@thick MainActor.Type +// CHECK: [[SHARED_FIELD:%.*]] = function_ref @$sScM6sharedScMvgZ : $@convention(method) (@thick MainActor.Type) -> @owned MainActor +// CHECK-NEXT: [[SHARED_ACTOR:%.*]] = apply [[SHARED_FIELD]]([[MAIN_ACTOR_METATYPE]]) : $@convention(method) (@thick MainActor.Type) -> @owned MainActor +// CHECK-NEXT: [[MAIN_ACTOR:%.*]] = begin_borrow [[SHARED_ACTOR]] : $MainActor +// CHECK-NEXT: [[EXEC:%.*]] = extract_executor [[MAIN_ACTOR]] : $MainActor +// CHECK: [[PRECONDITION:%.*]] = function_ref @$ss22_checkExpectedExecutor14_filenameStart01_D6Length01_D7IsASCII5_line9_executoryBp_BwBi1_BwBetF : $@convention(thin) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, Builtin.Word, Builtin.Executor) -> () +// CHECK-NEXT: {{.*}} = apply [[PRECONDITION]]({{.*}}, [[EXEC]]) : $@convention(thin) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, Builtin.Word, Builtin.Executor) -> () + +// @objc K.data.setter +// CHECK-LABEL: sil private [thunk] [ossa] @$s27preconcurrency_conformances1KC4dataSSSgvsTo : $@convention(objc_method) (Optional, K) -> () +// CHECK: [[MAIN_ACTOR_METATYPE:%.*]] = metatype $@thick MainActor.Type +// CHECK: [[SHARED_FIELD:%.*]] = function_ref @$sScM6sharedScMvgZ : $@convention(method) (@thick MainActor.Type) -> @owned MainActor +// CHECK-NEXT: [[SHARED_ACTOR:%.*]] = apply [[SHARED_FIELD]]([[MAIN_ACTOR_METATYPE]]) : $@convention(method) (@thick MainActor.Type) -> @owned MainActor +// CHECK-NEXT: [[MAIN_ACTOR:%.*]] = begin_borrow [[SHARED_ACTOR]] : $MainActor +// CHECK-NEXT: [[EXEC:%.*]] = extract_executor [[MAIN_ACTOR]] : $MainActor +// CHECK: [[PRECONDITION:%.*]] = function_ref @$ss22_checkExpectedExecutor14_filenameStart01_D6Length01_D7IsASCII5_line9_executoryBp_BwBi1_BwBetF : $@convention(thin) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, Builtin.Word, Builtin.Executor) -> () +// CHECK-NEXT: {{.*}} = apply [[PRECONDITION]]({{.*}}, [[EXEC]]) : $@convention(thin) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, Builtin.Word, Builtin.Executor) -> () + +// @objc K.init() +// CHECK-LABEL: sil private [thunk] [ossa] @$s27preconcurrency_conformances1KCACycfcTo : $@convention(objc_method) (@owned K) -> @owned K +// CHECK: [[MAIN_ACTOR_METATYPE:%.*]] = metatype $@thick MainActor.Type +// CHECK: [[SHARED_FIELD:%.*]] = function_ref @$sScM6sharedScMvgZ : $@convention(method) (@thick MainActor.Type) -> @owned MainActor +// CHECK-NEXT: [[SHARED_ACTOR:%.*]] = apply [[SHARED_FIELD]]([[MAIN_ACTOR_METATYPE]]) : $@convention(method) (@thick MainActor.Type) -> @owned MainActor +// CHECK-NEXT: [[MAIN_ACTOR:%.*]] = begin_borrow [[SHARED_ACTOR]] : $MainActor +// CHECK-NEXT: [[EXEC:%.*]] = extract_executor [[MAIN_ACTOR]] : $MainActor +// CHECK: [[PRECONDITION:%.*]] = function_ref @$ss22_checkExpectedExecutor14_filenameStart01_D6Length01_D7IsASCII5_line9_executoryBp_BwBi1_BwBetF : $@convention(thin) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, Builtin.Word, Builtin.Executor) -> () +// CHECK-NEXT: {{.*}} = apply [[PRECONDITION]]({{.*}}, [[EXEC]]) : $@convention(thin) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, Builtin.Word, Builtin.Executor) -> () + +// @objc K.test() +// CHECK-LABEL: sil private [thunk] [ossa] @$s27preconcurrency_conformances1KC4testypSgyFTo : $@convention(objc_method) (K) -> @autoreleased Optional +// CHECK: [[GLOBAL_ACTOR_METATYPE:%.*]] = metatype $@thin GlobalActor.Type +// CHECK: [[SHARED_ACTOR_GETTER:%.*]] = function_ref @$s27preconcurrency_conformances11GlobalActorV6sharedAA02MyD0Cvau : $@convention(thin) () -> Builtin.RawPointer +// CHECK-NEXT: [[ACTOR_PTR:%.*]] = apply [[SHARED_ACTOR_GETTER]]() : $@convention(thin) () -> Builtin.RawPointer +// CHECK-NEXT: [[MY_ACTOR_ADDR:%.*]] = pointer_to_address [[ACTOR_PTR]] : $Builtin.RawPointer to [strict] $*MyActor +// CHECK-NEXT: [[MY_ACTOR_ACCESS:%.*]] = begin_access [read] [dynamic] [[MY_ACTOR_ADDR]] : $*MyActor +// CHECK-NEXT: [[MY_ACTOR:%.*]] = load [copy] [[MY_ACTOR_ACCESS]] : $*MyActor +// CHECK: [[MY_ACTOR_REF:%.*]] = begin_borrow [[MY_ACTOR]] : $MyActor +// CHECK-NEXT: [[EXEC:%.*]] = extract_executor [[MY_ACTOR_REF]] : $MyActor +// CHECK: [[PRECONDITION:%.*]] = function_ref @$ss22_checkExpectedExecutor14_filenameStart01_D6Length01_D7IsASCII5_line9_executoryBp_BwBi1_BwBetF : $@convention(thin) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, Builtin.Word, Builtin.Executor) -> () +// CHECK-NEXT: {{.*}} = apply [[PRECONDITION]]({{.*}}, [[EXEC]]) : $@convention(thin) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, Builtin.Word, Builtin.Executor) -> () diff --git a/test/Concurrency/preconcurrency_conformances.swift b/test/Concurrency/preconcurrency_conformances.swift new file mode 100644 index 0000000000000..615102a8e4674 --- /dev/null +++ b/test/Concurrency/preconcurrency_conformances.swift @@ -0,0 +1,171 @@ +// RUN: %target-swift-frontend -swift-version 5 -disable-availability-checking %s -emit-sil -o /dev/null -verify -enable-experimental-feature PreconcurrencyConformances -strict-concurrency=complete -verify-additional-prefix complete-tns- + +// REQUIRES: concurrency +// REQUIRES: asserts + +protocol Q { +} + +do { + class K {} + + struct A : @preconcurrency Q {} // Ok + // expected-warning@-1 {{@preconcurrency attribute on conformance to 'Q' has no effect}} + + struct B : @preconcurrency K { + // expected-error@-1 {{'preconcurrency' attribute cannot apply to non-protocol type 'K'}} + var x: @preconcurrency Int + // expected-error@-1 {{'preconcurrency' attribute only applies in inheritance clauses}} + } + + typealias T = @preconcurrency Q + // expected-error@-1 {{'preconcurrency' attribute only applies in inheritance clauses}} + + func test(_: @preconcurrency K) {} + // expected-error@-1 {{'preconcurrency' attribute only applies in inheritance clauses}} +} + +protocol InvalidUseOfPreconcurrencyAttr : @preconcurrency Q { + // expected-error@-1 {{'preconcurrency' attribute only applies in inheritance clauses}} +} + +struct TestPreconcurrencyAttr {} +extension TestPreconcurrencyAttr : @preconcurrency Q { // Ok + // expected-warning@-1 {{@preconcurrency attribute on conformance to 'Q' has no effect}} +} + +class NonSendable {} +// expected-note@-1 6 {{class 'NonSendable' does not conform to the 'Sendable' protocol}} + +protocol TestSendability { + var x: NonSendable { get } + func test(_: NonSendable?) -> [NonSendable] +} + +// Make sure that preconcurrency conformances don't suppress Sendable diagnostics +@MainActor +struct Value : @preconcurrency TestSendability { + var x: NonSendable { NonSendable() } + // expected-warning@-1 {{non-sendable type 'NonSendable' in conformance of main actor-isolated property 'x' to protocol requirement cannot cross actor boundary}} + // expected-note@-2 2 {{property declared here}} + + // expected-warning@+2 {{non-sendable type '[NonSendable]' returned by main actor-isolated instance method 'test' satisfying protocol requirement cannot cross actor boundary}} + // expected-warning@+1 {{non-sendable type 'NonSendable?' in parameter of the protocol requirement satisfied by main actor-isolated instance method 'test' cannot cross actor boundary}} + func test(_: NonSendable?) -> [NonSendable] { + // expected-note@-1 2 {{calls to instance method 'test' from outside of its actor context are implicitly asynchronous}} + [] + } +} + +// Make sure that references to actor isolated witness is diagnosed + +// expected-note@+1 2 {{add '@MainActor' to make global function 'test(value:)' part of global actor 'MainActor'}} +func test(value: Value) { + _ = value.x + // expected-error@-1 {{main actor-isolated property 'x' can not be referenced from a non-isolated context}} + _ = value.test(nil) + // expected-error@-1 {{call to main actor-isolated instance method 'test' in a synchronous nonisolated context}} +} + +actor MyActor { + var value: Value? = nil +} + +extension MyActor : @preconcurrency TestSendability { + var x: NonSendable { NonSendable() } + // expected-warning@-1 {{non-sendable type 'NonSendable' in conformance of actor-isolated property 'x' to protocol requirement cannot cross actor boundary}} + + // expected-warning@+2 {{non-sendable type '[NonSendable]' returned by actor-isolated instance method 'test' satisfying protocol requirement cannot cross actor boundary}} + // expected-warning@+1 {{non-sendable type 'NonSendable?' in parameter of the protocol requirement satisfied by actor-isolated instance method 'test' cannot cross actor boundary}} + func test(_: NonSendable?) -> [NonSendable] { + [] + } + + func test_ref_diagnostics() { + _ = value?.x + // expected-error@-1 {{main actor-isolated property 'x' can not be referenced on a non-isolated actor instance}} + _ = value?.test(nil) + // expected-error@-1 {{call to main actor-isolated instance method 'test' in a synchronous actor-isolated context}} + } +} + +protocol Initializable { + init() +} + +final class K : @preconcurrency Initializable { + // expected-warning@-1 {{@preconcurrency attribute on conformance to 'Initializable' has no effect}} + init() {} // Ok +} + +protocol WithAssoc { + associatedtype T + func test() -> T +} + +struct TestConditional {} + +extension TestConditional : @preconcurrency WithAssoc where T == Int { + @MainActor func test() -> T { 42 } // Ok +} + +@globalActor +struct GlobalActor { + static let shared: MyActor = MyActor() +} + +protocol WithIndividuallyIsolatedRequirements { + @MainActor var a: Int { get set } + @GlobalActor var b: Int { get set } + // expected-note@-1 {{'b' declared here}} + + @GlobalActor func test() + // expected-note@-1 {{mark the protocol requirement 'test()' 'async' to allow actor-isolated conformances}} +} + +do { + @MainActor + struct TestExplicitGlobalActorAttrs : @preconcurrency WithIndividuallyIsolatedRequirements { + // expected-warning@-1 {{@preconcurrency attribute on conformance to 'WithIndividuallyIsolatedRequirements' has no effect}} + + var a: Int = 42 + + @MainActor var b: Int { + // expected-warning@-1 {{main actor-isolated property 'b' cannot be used to satisfy global actor 'GlobalActor'-isolated protocol requirement}} + get { 0 } + set {} + } + + @MainActor func test() { + // expected-warning@-1 {{main actor-isolated instance method 'test()' cannot be used to satisfy global actor 'GlobalActor'-isolated protocol requirement}} + } + } +} + +@MainActor +protocol WithNonIsolated { + var prop: Int { get set } + // expected-note@-1 {{'prop' declared here}} + nonisolated func test() + // expected-note@-1 {{mark the protocol requirement 'test()' 'async' to allow actor-isolated conformances}} +} + +do { + class TestExplicitOtherIsolation : @preconcurrency WithNonIsolated { + // expected-warning@-1 {{@preconcurrency attribute on conformance to 'WithNonIsolated' has no effect}} + + @GlobalActor var prop: Int = 42 + // expected-warning@-1 {{global actor 'GlobalActor'-isolated property 'prop' cannot be used to satisfy main actor-isolated protocol requirement}} + + @MainActor func test() {} + // expected-warning@-1 {{main actor-isolated instance method 'test()' cannot be used to satisfy nonisolated protocol requirement}} + } +} + +do { + class InferredGlobalActorAttrs : @preconcurrency WithNonIsolated { + // expected-warning@-1 {{@preconcurrency attribute on conformance to 'WithNonIsolated' has no effect}} + var prop: Int = 42 + func test() {} + } +} diff --git a/test/Interpreter/preconcurrency_conformances.swift b/test/Interpreter/preconcurrency_conformances.swift new file mode 100644 index 0000000000000..c9ff6913da7d3 --- /dev/null +++ b/test/Interpreter/preconcurrency_conformances.swift @@ -0,0 +1,100 @@ +// RUN: %empty-directory(%t/src) +// RUN: split-file %s %t/src + +// RUN: %target-build-swift %t/src/Interface.swift -swift-version 5 -emit-module -emit-library \ +// RUN: -target %target-cpu-apple-macosx10.15 -swift-version 5 \ +// RUN: -enable-library-evolution \ +// RUN: -module-name Interface \ +// RUN: -o %t/%target-library-name(Interface) \ +// RUN: -emit-module-interface-path %t/Interface.swiftinterface + +// RUN: %target-build-swift %t/src/Types.swift -swift-version 5 -emit-module -emit-library -enable-library-evolution -module-name Types -o %t/%target-library-name(Types) \ +// RUN: -target %target-cpu-apple-macosx10.15 \ +// RUN: -I %t -L %t -l Interface \ +// RUN: -emit-module-interface-path %t/Types.swiftinterface \ +// RUN: -Xfrontend -enable-experimental-feature -Xfrontend PreconcurrencyConformances + +// RUN: %target-build-swift -Xfrontend -enable-experimental-feature -Xfrontend PreconcurrencyConformances -I %t -L %t -l Types %t/src/Crash1.swift -o %t/crash1.out +// RUN: %target-codesign %t/crash1.out +// RUN: not --crash env SWIFT_UNEXPECTED_EXECUTOR_LOG_LEVEL=2 %target-run %t/crash1.out 2>&1 | %FileCheck %t/src/Crash1.swift + +// RUN: %target-build-swift -Xfrontend -enable-experimental-feature -Xfrontend PreconcurrencyConformances -I %t -L %t -l Types %t/src/Crash2.swift -o %t/crash2.out +// RUN: %target-codesign %t/crash2.out +// RUN: not --crash env SWIFT_UNEXPECTED_EXECUTOR_LOG_LEVEL=2 %target-run %t/crash2.out 2>&1 | %FileCheck %t/src/Crash2.swift + +// RUN: %target-build-swift -Xfrontend -enable-experimental-feature -Xfrontend PreconcurrencyConformances -I %t -L %t -l Types %t/src/Crash3.swift -o %t/crash3.out +// RUN: %target-codesign %t/crash3.out +// RUN: not --crash env SWIFT_UNEXPECTED_EXECUTOR_LOG_LEVEL=2 %target-run %t/crash3.out 2>&1 | %FileCheck %t/src/Crash3.swift + +// RUN: %target-build-swift -Xfrontend -enable-experimental-feature -Xfrontend PreconcurrencyConformances -I %t -L %t -l Types %t/src/Crash4.swift -o %t/crash4.out +// RUN: %target-codesign %t/crash4.out +// RUN: not --crash env SWIFT_UNEXPECTED_EXECUTOR_LOG_LEVEL=2 %target-run %t/crash4.out 2>&1 | %FileCheck %t/src/Crash4.swift + +// REQUIRES: asserts +// REQUIRES: concurrency +// REQUIRES: executable_test +// REQUIRES: OS=macosx + +//--- Interface.swift +public protocol P { + init() + + var prop: [String] { get set } + func test() -> Int +} + +//--- Types.swift +import Interface + +public func runTest(_ type: T.Type) async -> Int { + let v = type.init() + return v.test() +} + +public func runAccessors(_ type: T.Type) async -> [String] { + var v = type.init() + v.prop = ["a", "b", "c"] + return v.prop +} + +public final class Test : @preconcurrency P { + @MainActor public var prop: [String] = [] + @MainActor public func test() -> Int { 42 } + + public init() {} +} + +public actor ActorTest { + var x: Int = 0 + + public init() {} +} + +extension ActorTest : @preconcurrency P { + public var prop: [String] { + get { [] } + set { } + } + + public func test() -> Int { x } +} + +//--- Crash1.swift +import Types +print(await runTest(Test.self)) +// CHECK: error: data race detected: @MainActor function at Types/Types.swift:16 was not called on the main thread + +//--- Crash2.swift +import Types +print(await runAccessors(Test.self)) +// CHECK: error: data race detected: @MainActor function at Types/Types.swift:15 was not called on the main thread + +//--- Crash3.swift +import Types +print(await runTest(ActorTest.self)) +// CHECK: error: data race detected: actor-isolated function at Types/Types.swift:33 was not called on the same actor + +//--- Crash4.swift +import Types +print(await runAccessors(ActorTest.self)) +// CHECK: error: data race detected: actor-isolated function at Types/Types.swift:30 was not called on the same actor diff --git a/test/ModuleInterface/preconcurrency_conformances.swift b/test/ModuleInterface/preconcurrency_conformances.swift new file mode 100644 index 0000000000000..a375b5adfe668 --- /dev/null +++ b/test/ModuleInterface/preconcurrency_conformances.swift @@ -0,0 +1,92 @@ +// RUN: %empty-directory(%t/src) +// RUN: split-file %s %t/src + +/// Build the library A +// RUN: %target-swift-frontend -emit-module %t/src/A.swift \ +// RUN: -module-name A -swift-version 5 -enable-library-evolution \ +// RUN: -emit-module-path %t/A.swiftmodule \ +// RUN: -emit-module-interface-path %t/A.swiftinterface + +// Build the client and check the interface +// RUN: %target-swift-frontend -emit-module %t/src/Client.swift \ +// RUN: -module-name Client -I %t -swift-version 5 -enable-library-evolution \ +// RUN: -emit-module-path %t/Client.swiftmodule \ +// RUN: -emit-module-interface-path %t/Client.swiftinterface \ +// RUN: -enable-experimental-feature PreconcurrencyConformances \ +// RUN: -disable-availability-checking \ +// RUN: -verify + +// RUN: %FileCheck %s < %t/Client.swiftinterface + +// RUN: %target-swift-emit-module-interface(%t/Client.swiftinterface) -I %t %t/src/Client.swift -module-name Client \ +// RUN: -disable-availability-checking -enable-experimental-feature PreconcurrencyConformances -verify + +// RUN: %target-swift-typecheck-module-from-interface(%t/Client.swiftinterface) -I %t -module-name Client \ +// RUN: -disable-availability-checking -enable-experimental-feature PreconcurrencyConformances -verify + +// REQUIRES: asserts +// REQUIRES: concurrency + +//--- A.swift +public protocol P { + func test() -> Int +} + +public protocol Q { + var x: Int { get } +} + +public protocol WithAssoc { + associatedtype T + func test() -> T +} + +//--- Client.swift +import A + +// CHECK: #if {{.*}} $PreconcurrencyConformances +// CHECK-NEXT: @_Concurrency.MainActor public struct GlobalActorTest : @preconcurrency A.P + +@MainActor +public struct GlobalActorTest : @preconcurrency P { + public func test() -> Int { 0 } +} + +@MainActor +public class ExtTest { +} + +// CHECK: #if {{.*}} $PreconcurrencyConformances +// CHECK-NEXT: extension Client.ExtTest : @preconcurrency A.P +extension ExtTest : @preconcurrency P { + public func test() -> Int { 1 } +} + +// CHECK: #if {{.*}} && $PreconcurrencyConformances +// CHECK-NEXT: public actor ActorTest : @preconcurrency A.P +public actor ActorTest : @preconcurrency P { + public func test() -> Int { 2 } +} + +public actor ActorExtTest { +} + +// CHECK: #if {{.*}} $PreconcurrencyConformances +// CHECK-NEXT: extension Client.ActorExtTest : @preconcurrency A.Q +extension ActorExtTest : @preconcurrency Q { + public var x: Int { 42 } +} + +public struct TestConditional {} + +// CHECK: #if {{.*}} $PreconcurrencyConformances +// CHECK-NEXT: extension Client.TestConditional : @preconcurrency A.WithAssoc where T == Swift.Int { +// CHECK-NEXT: @_Concurrency.MainActor public func test() -> T +// CHECK-NEXT: } +extension TestConditional : @preconcurrency WithAssoc where T == Int { + @MainActor public func test() -> T { 42 } // Ok +} + +// CHECK: #if {{.*}} $PreconcurrencyConformances +// CHECK-NEXT: extension Client.GlobalActorTest : Swift.Sendable {} +// CHECK-NEXT: #endif diff --git a/test/SILGen/preconcurrency_conformances.swift b/test/SILGen/preconcurrency_conformances.swift new file mode 100644 index 0000000000000..9ccbbb8f8479f --- /dev/null +++ b/test/SILGen/preconcurrency_conformances.swift @@ -0,0 +1,272 @@ +// RUN: %target-swift-emit-silgen -disable-availability-checking -module-name preconcurrency_conformances -enable-experimental-feature PreconcurrencyConformances %s -verify | %FileCheck %s + +// REQUIRES: asserts +// REQUIRES: concurrency + +protocol P { + associatedtype T + + var prop: T { get set } + func fn() -> T? +} + +protocol Q { + static var data: [Int]? { get set } + static func staticFn() -> String? +} + +actor MyActor { +} + +@globalActor +struct GlobalActor { + static var shared: MyActor = MyActor() +} + +@MainActor +struct IsolatedType : @preconcurrency P { + var prop: T + func fn() -> T? { nil } +} + +// protocol witness for P.prop.getter in conformance IsolatedType +// CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s27preconcurrency_conformances12IsolatedTypeVyxGAA1PA2aEP4prop1TQzvgTW : $@convention(witness_method: P) <τ_0_0> (@in_guaranteed IsolatedType<τ_0_0>) -> @out τ_0_0 +// CHECK: [[MAIN_ACTOR:%.*]] = begin_borrow {{.*}} : $MainActor +// CHECK-NEXT: [[EXEC:%.*]] = extract_executor [[MAIN_ACTOR]] : $MainActor +// CHECK: [[CHECK_EXEC_REF:%.*]] = function_ref @$ss22_checkExpectedExecutor14_filenameStart01_D6Length01_D7IsASCII5_line9_executoryBp_BwBi1_BwBetF +// CHECK-NEXT: {{.*}} = apply [[CHECK_EXEC_REF]]({{.*}}, [[EXEC]]) + +// protocol witness for P.prop.setter in conformance IsolatedType +// CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s27preconcurrency_conformances12IsolatedTypeVyxGAA1PA2aEP4prop1TQzvsTW : $@convention(witness_method: P) <τ_0_0> (@in τ_0_0, @inout IsolatedType<τ_0_0>) -> () +// CHECK: [[MAIN_ACTOR:%.*]] = begin_borrow {{.*}} : $MainActor +// CHECK-NEXT: [[EXEC:%.*]] = extract_executor [[MAIN_ACTOR]] : $MainActor +// CHECK: [[CHECK_EXEC_REF:%.*]] = function_ref @$ss22_checkExpectedExecutor14_filenameStart01_D6Length01_D7IsASCII5_line9_executoryBp_BwBi1_BwBetF +// CHECK-NEXT: {{.*}} = apply [[CHECK_EXEC_REF]]({{.*}}, [[EXEC]]) + +// protocol witness for P.prop.modify in conformance IsolatedType +// CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s27preconcurrency_conformances12IsolatedTypeVyxGAA1PA2aEP4prop1TQzvMTW : $@yield_once @convention(witness_method: P) <τ_0_0> @substituted <τ_0_0, τ_0_1> (@inout τ_0_0) -> @yields @inout τ_0_1 for , τ_0_0> +// CHECK: [[MAIN_ACTOR:%.*]] = begin_borrow {{.*}} : $MainActor +// CHECK-NEXT: [[EXEC:%.*]] = extract_executor [[MAIN_ACTOR]] : $MainActor +// CHECK: [[CHECK_EXEC_REF:%.*]] = function_ref @$ss22_checkExpectedExecutor14_filenameStart01_D6Length01_D7IsASCII5_line9_executoryBp_BwBi1_BwBetF +// CHECK-NEXT: {{.*}} = apply [[CHECK_EXEC_REF]]({{.*}}, [[EXEC]]) + +// protocol witness for P.fn() in conformance IsolatedType +// CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s27preconcurrency_conformances12IsolatedTypeVyxGAA1PA2aEP2fn1TQzSgyFTW : $@convention(witness_method: P) <τ_0_0> (@in_guaranteed IsolatedType<τ_0_0>) -> @out Optional<τ_0_0> +// CHECK: [[MAIN_ACTOR:%.*]] = begin_borrow {{.*}} : $MainActor +// CHECK-NEXT: [[EXEC:%.*]] = extract_executor [[MAIN_ACTOR]] : $MainActor +// CHECK: [[CHECK_EXEC_REF:%.*]] = function_ref @$ss22_checkExpectedExecutor14_filenameStart01_D6Length01_D7IsASCII5_line9_executoryBp_BwBi1_BwBetF +// CHECK-NEXT: {{.*}} = apply [[CHECK_EXEC_REF]]({{.*}}, [[EXEC]]) + +extension IsolatedType : @preconcurrency Q { + static var data: [Int]? { + get { nil } + set {} + } + + static func staticFn() -> String? { nil } +} + +// protocol witness for static Q.data.getter in conformance IsolatedType +// CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s27preconcurrency_conformances12IsolatedTypeVyxGAA1QA2aEP4dataSaySiGSgvgZTW : $@convention(witness_method: Q) <τ_0_0> (@thick IsolatedType<τ_0_0>.Type) -> @owned Optional> +// CHECK: [[MAIN_ACTOR_METATYPE:%.*]] = metatype $@thick MainActor.Type +// CHECK: [[SHARED_FIELD_GETTER:%.*]] = function_ref @$sScM6sharedScMvgZ : $@convention(method) (@thick MainActor.Type) -> @owned MainActor +// CHECK-NEXT: [[MAIN_ACTOR:%.*]] = apply [[SHARED_FIELD_GETTER]]([[MAIN_ACTOR_METATYPE]]) : $@convention(method) (@thick MainActor.Type) -> @owned MainActor +// CHECK-NEXT: [[MAIN_ACTOR_ACCESS:%.*]] = begin_borrow [[MAIN_ACTOR]] : $MainActor +// CHECK-NEXT: [[EXEC:%.*]] = extract_executor [[MAIN_ACTOR_ACCESS]] : $MainActor +// CHECK: [[CHECK_EXEC_REF:%.*]] = function_ref @$ss22_checkExpectedExecutor14_filenameStart01_D6Length01_D7IsASCII5_line9_executoryBp_BwBi1_BwBetF +// CHECK-NEXT: {{.*}} = apply [[CHECK_EXEC_REF]]({{.*}}, [[EXEC]]) + +// protocol witness for static Q.data.setter in conformance IsolatedType +// CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s27preconcurrency_conformances12IsolatedTypeVyxGAA1QA2aEP4dataSaySiGSgvsZTW : $@convention(witness_method: Q) <τ_0_0> (@owned Optional>, @thick IsolatedType<τ_0_0>.Type) -> () +// CHECK: [[MAIN_ACTOR_METATYPE:%.*]] = metatype $@thick MainActor.Type +// CHECK: [[SHARED_FIELD_GETTER:%.*]] = function_ref @$sScM6sharedScMvgZ : $@convention(method) (@thick MainActor.Type) -> @owned MainActor +// CHECK-NEXT: [[MAIN_ACTOR:%.*]] = apply [[SHARED_FIELD_GETTER]]([[MAIN_ACTOR_METATYPE]]) : $@convention(method) (@thick MainActor.Type) -> @owned MainActor +// CHECK-NEXT: [[MAIN_ACTOR_ACCESS:%.*]] = begin_borrow [[MAIN_ACTOR]] : $MainActor +// CHECK-NEXT: [[EXEC:%.*]] = extract_executor [[MAIN_ACTOR_ACCESS]] : $MainActor +// CHECK: [[CHECK_EXEC_REF:%.*]] = function_ref @$ss22_checkExpectedExecutor14_filenameStart01_D6Length01_D7IsASCII5_line9_executoryBp_BwBi1_BwBetF +// CHECK-NEXT: {{.*}} = apply [[CHECK_EXEC_REF]]({{.*}}, [[EXEC]]) + +// protocol witness for static Q.data.modify in conformance IsolatedType +// CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s27preconcurrency_conformances12IsolatedTypeVyxGAA1QA2aEP4dataSaySiGSgvMZTW : $@yield_once @convention(witness_method: Q) <τ_0_0> @substituted <τ_0_0> (@thick τ_0_0.Type) -> @yields @inout Optional> for > +// CHECK: [[MAIN_ACTOR_METATYPE:%.*]] = metatype $@thick MainActor.Type +// CHECK: [[SHARED_FIELD_GETTER:%.*]] = function_ref @$sScM6sharedScMvgZ : $@convention(method) (@thick MainActor.Type) -> @owned MainActor +// CHECK-NEXT: [[MAIN_ACTOR:%.*]] = apply [[SHARED_FIELD_GETTER]]([[MAIN_ACTOR_METATYPE]]) : $@convention(method) (@thick MainActor.Type) -> @owned MainActor +// CHECK-NEXT: [[MAIN_ACTOR_ACCESS:%.*]] = begin_borrow [[MAIN_ACTOR]] : $MainActor +// CHECK-NEXT: [[EXEC:%.*]] = extract_executor [[MAIN_ACTOR_ACCESS]] : $MainActor +// CHECK: [[CHECK_EXEC_REF:%.*]] = function_ref @$ss22_checkExpectedExecutor14_filenameStart01_D6Length01_D7IsASCII5_line9_executoryBp_BwBi1_BwBetF +// CHECK-NEXT: {{.*}} = apply [[CHECK_EXEC_REF]]({{.*}}, [[EXEC]]) + +// protocol witness for static Q.staticFn() in conformance IsolatedType +// CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s27preconcurrency_conformances12IsolatedTypeVyxGAA1QA2aEP8staticFnSSSgyFZTW : $@convention(witness_method: Q) <τ_0_0> (@thick IsolatedType<τ_0_0>.Type) -> @owned Optional +// CHECK: [[MAIN_ACTOR_METATYPE:%.*]] = metatype $@thick MainActor.Type +// CHECK: [[SHARED_FIELD_GETTER:%.*]] = function_ref @$sScM6sharedScMvgZ : $@convention(method) (@thick MainActor.Type) -> @owned MainActor +// CHECK-NEXT: [[MAIN_ACTOR:%.*]] = apply [[SHARED_FIELD_GETTER]]([[MAIN_ACTOR_METATYPE]]) : $@convention(method) (@thick MainActor.Type) -> @owned MainActor +// CHECK-NEXT: [[MAIN_ACTOR_ACCESS:%.*]] = begin_borrow [[MAIN_ACTOR]] : $MainActor +// CHECK-NEXT: [[EXEC:%.*]] = extract_executor [[MAIN_ACTOR_ACCESS]] : $MainActor +// CHECK: [[CHECK_EXEC_REF:%.*]] = function_ref @$ss22_checkExpectedExecutor14_filenameStart01_D6Length01_D7IsASCII5_line9_executoryBp_BwBi1_BwBetF +// CHECK-NEXT: {{.*}} = apply [[CHECK_EXEC_REF]]({{.*}}, [[EXEC]]) + +class IsolatedMembers : @preconcurrency P { + @MainActor var prop: T { + get { fatalError() } + set {} + } + + @GlobalActor func fn() -> T? { nil } +} + +// protocol witness for P.prop.getter in conformance IsolatedMembers +// CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s27preconcurrency_conformances15IsolatedMembersCyxGAA1PA2aEP4prop1TQzvgTW : $@convention(witness_method: P) <τ_0_0> (@in_guaranteed IsolatedMembers<τ_0_0>) -> @out τ_0_0 +// CHECK: [[MAIN_ACTOR:%.*]] = begin_borrow {{.*}} : $MainActor +// CHECK-NEXT: [[EXEC:%.*]] = extract_executor [[MAIN_ACTOR]] : $MainActor +// CHECK: [[CHECK_EXEC_REF:%.*]] = function_ref @$ss22_checkExpectedExecutor14_filenameStart01_D6Length01_D7IsASCII5_line9_executoryBp_BwBi1_BwBetF +// CHECK-NEXT: {{.*}} = apply [[CHECK_EXEC_REF]]({{.*}}, [[EXEC]]) + +// protocol witness for P.prop.setter in conformance IsolatedMembers +// CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s27preconcurrency_conformances15IsolatedMembersCyxGAA1PA2aEP4prop1TQzvsTW : $@convention(witness_method: P) <τ_0_0> (@in τ_0_0, @inout IsolatedMembers<τ_0_0>) -> () +// CHECK: [[MAIN_ACTOR:%.*]] = begin_borrow {{.*}} : $MainActor +// CHECK-NEXT: [[EXEC:%.*]] = extract_executor [[MAIN_ACTOR]] : $MainActor +// CHECK: [[CHECK_EXEC_REF:%.*]] = function_ref @$ss22_checkExpectedExecutor14_filenameStart01_D6Length01_D7IsASCII5_line9_executoryBp_BwBi1_BwBetF +// CHECK-NEXT: {{.*}} = apply [[CHECK_EXEC_REF]]({{.*}}, [[EXEC]]) + +// protocol witness for P.prop.modify in conformance IsolatedMembers +// CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s27preconcurrency_conformances15IsolatedMembersCyxGAA1PA2aEP4prop1TQzvMTW : $@yield_once @convention(witness_method: P) <τ_0_0> @substituted <τ_0_0, τ_0_1> (@inout τ_0_0) -> @yields @inout τ_0_1 for , τ_0_0> +// CHECK: [[MAIN_ACTOR:%.*]] = begin_borrow {{.*}} : $MainActor +// CHECK-NEXT: [[EXEC:%.*]] = extract_executor [[MAIN_ACTOR]] : $MainActor +// CHECK: [[CHECK_EXEC_REF:%.*]] = function_ref @$ss22_checkExpectedExecutor14_filenameStart01_D6Length01_D7IsASCII5_line9_executoryBp_BwBi1_BwBetF +// CHECK-NEXT: {{.*}} = apply [[CHECK_EXEC_REF]]({{.*}}, [[EXEC]]) + +// protocol witness for P.fn() in conformance IsolatedMembers +// CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s27preconcurrency_conformances15IsolatedMembersCyxGAA1PA2aEP2fn1TQzSgyFTW : $@convention(witness_method: P) <τ_0_0> (@in_guaranteed IsolatedMembers<τ_0_0>) -> @out Optional<τ_0_0> +// CHECK: [[SHARED_REF:%.*]] = function_ref @$s27preconcurrency_conformances11GlobalActorV6sharedAA02MyD0Cvau : $@convention(thin) () -> Builtin.RawPointer +// CHECK-NEXT: [[SHARED_PTR:%.*]] = apply [[SHARED_REF]]() : $@convention(thin) () -> Builtin.RawPointer +// CHECK-NEXT: [[MY_ACTOR_ADDR:%.*]] = pointer_to_address [[SHARED_PTR]] : $Builtin.RawPointer to [strict] $*MyActor +// CHECK-NEXT: [[MY_ACTOR_REF:%.*]] = begin_access [read] [dynamic] [[MY_ACTOR_ADDR]] : $*MyActor +// CHECK-NEXT: [[MY_ACTOR:%.*]] = load [copy] [[MY_ACTOR_REF]] : $*MyActor +// CHECK-NEXT: end_access [[MY_ACTOR_REF]] : $*MyActor +// CHECK-NEXT: [[MY_ACTOR_REF:%.*]] = begin_borrow [[MY_ACTOR]] : $MyActor +// CHECK-NEXT: [[EXEC:%.*]] = extract_executor [[MY_ACTOR_REF]] : $MyActor +// CHECK: [[CHECK_EXEC_REF:%.*]] = function_ref @$ss22_checkExpectedExecutor14_filenameStart01_D6Length01_D7IsASCII5_line9_executoryBp_BwBi1_BwBetF +// CHECK-NEXT: {{.*}} = apply [[CHECK_EXEC_REF]]({{.*}}, [[EXEC]]) + +extension IsolatedMembers : @preconcurrency Q { + @GlobalActor static var data: [Int]? { + get { nil } + set {} + } + + @MainActor static func staticFn() -> String? { nil } +} + +// protocol witness for static Q.data.getter in conformance IsolatedMembers +// CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s27preconcurrency_conformances15IsolatedMembersCyxGAA1QA2aEP4dataSaySiGSgvgZTW : $@convention(witness_method: Q) <τ_0_0> (@thick IsolatedMembers<τ_0_0>.Type) -> @owned Optional> +// CHECK: [[SHARED_REF:%.*]] = function_ref @$s27preconcurrency_conformances11GlobalActorV6sharedAA02MyD0Cvau : $@convention(thin) () -> Builtin.RawPointer +// CHECK-NEXT: [[SHARED_PTR:%.*]] = apply [[SHARED_REF]]() : $@convention(thin) () -> Builtin.RawPointer +// CHECK-NEXT: [[MY_ACTOR_ADDR:%.*]] = pointer_to_address [[SHARED_PTR]] : $Builtin.RawPointer to [strict] $*MyActor +// CHECK-NEXT: [[MY_ACTOR_REF:%.*]] = begin_access [read] [dynamic] [[MY_ACTOR_ADDR]] : $*MyActor +// CHECK-NEXT: [[MY_ACTOR:%.*]] = load [copy] [[MY_ACTOR_REF]] : $*MyActor +// CHECK-NEXT: end_access [[MY_ACTOR_REF]] : $*MyActor +// CHECK-NEXT: [[MY_ACTOR_REF:%.*]] = begin_borrow [[MY_ACTOR]] : $MyActor +// CHECK-NEXT: [[EXEC:%.*]] = extract_executor [[MY_ACTOR_REF]] : $MyActor +// CHECK: [[CHECK_EXEC_REF:%.*]] = function_ref @$ss22_checkExpectedExecutor14_filenameStart01_D6Length01_D7IsASCII5_line9_executoryBp_BwBi1_BwBetF +// CHECK-NEXT: {{.*}} = apply [[CHECK_EXEC_REF]]({{.*}}, [[EXEC]]) + +// protocol witness for static Q.data.setter in conformance IsolatedMembers +// CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s27preconcurrency_conformances15IsolatedMembersCyxGAA1QA2aEP4dataSaySiGSgvsZTW : $@convention(witness_method: Q) <τ_0_0> (@owned Optional>, @thick IsolatedMembers<τ_0_0>.Type) -> () +// CHECK: [[SHARED_REF:%.*]] = function_ref @$s27preconcurrency_conformances11GlobalActorV6sharedAA02MyD0Cvau : $@convention(thin) () -> Builtin.RawPointer +// CHECK-NEXT: [[SHARED_PTR:%.*]] = apply [[SHARED_REF]]() : $@convention(thin) () -> Builtin.RawPointer +// CHECK-NEXT: [[MY_ACTOR_ADDR:%.*]] = pointer_to_address [[SHARED_PTR]] : $Builtin.RawPointer to [strict] $*MyActor +// CHECK-NEXT: [[MY_ACTOR_REF:%.*]] = begin_access [read] [dynamic] [[MY_ACTOR_ADDR]] : $*MyActor +// CHECK-NEXT: [[MY_ACTOR:%.*]] = load [copy] [[MY_ACTOR_REF]] : $*MyActor +// CHECK-NEXT: end_access [[MY_ACTOR_REF]] : $*MyActor +// CHECK-NEXT: [[MY_ACTOR_REF:%.*]] = begin_borrow [[MY_ACTOR]] : $MyActor +// CHECK-NEXT: [[EXEC:%.*]] = extract_executor [[MY_ACTOR_REF]] : $MyActor +// CHECK: [[CHECK_EXEC_REF:%.*]] = function_ref @$ss22_checkExpectedExecutor14_filenameStart01_D6Length01_D7IsASCII5_line9_executoryBp_BwBi1_BwBetF +// CHECK-NEXT: {{.*}} = apply [[CHECK_EXEC_REF]]({{.*}}, [[EXEC]]) + +// protocol witness for static Q.data.modify in conformance IsolatedMembers +// CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s27preconcurrency_conformances15IsolatedMembersCyxGAA1QA2aEP4dataSaySiGSgvMZTW : $@yield_once @convention(witness_method: Q) <τ_0_0> @substituted <τ_0_0> (@thick τ_0_0.Type) -> @yields @inout Optional> for > +// CHECK: [[SHARED_REF:%.*]] = function_ref @$s27preconcurrency_conformances11GlobalActorV6sharedAA02MyD0Cvau : $@convention(thin) () -> Builtin.RawPointer +// CHECK-NEXT: [[SHARED_PTR:%.*]] = apply [[SHARED_REF]]() : $@convention(thin) () -> Builtin.RawPointer +// CHECK-NEXT: [[MY_ACTOR_ADDR:%.*]] = pointer_to_address [[SHARED_PTR]] : $Builtin.RawPointer to [strict] $*MyActor +// CHECK-NEXT: [[MY_ACTOR_REF:%.*]] = begin_access [read] [dynamic] [[MY_ACTOR_ADDR]] : $*MyActor +// CHECK-NEXT: [[MY_ACTOR:%.*]] = load [copy] [[MY_ACTOR_REF]] : $*MyActor +// CHECK-NEXT: end_access [[MY_ACTOR_REF]] : $*MyActor +// CHECK-NEXT: [[MY_ACTOR_REF:%.*]] = begin_borrow [[MY_ACTOR]] : $MyActor +// CHECK-NEXT: [[EXEC:%.*]] = extract_executor [[MY_ACTOR_REF]] : $MyActor +// CHECK: [[CHECK_EXEC_REF:%.*]] = function_ref @$ss22_checkExpectedExecutor14_filenameStart01_D6Length01_D7IsASCII5_line9_executoryBp_BwBi1_BwBetF +// CHECK-NEXT: {{.*}} = apply [[CHECK_EXEC_REF]]({{.*}}, [[EXEC]]) + +// protocol witness for static Q.staticFn() in conformance IsolatedMembers +// CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s27preconcurrency_conformances15IsolatedMembersCyxGAA1QA2aEP8staticFnSSSgyFZTW : $@convention(witness_method: Q) <τ_0_0> (@thick IsolatedMembers<τ_0_0>.Type) -> @owned Optional +// CHECK: [[MAIN_ACTOR:%.*]] = begin_borrow {{.*}} : $MainActor +// CHECK-NEXT: [[EXEC:%.*]] = extract_executor [[MAIN_ACTOR]] : $MainActor +// CHECK: [[CHECK_EXEC_REF:%.*]] = function_ref @$ss22_checkExpectedExecutor14_filenameStart01_D6Length01_D7IsASCII5_line9_executoryBp_BwBi1_BwBetF +// CHECK-NEXT: {{.*}} = apply [[CHECK_EXEC_REF]]({{.*}}, [[EXEC]]) + +extension MyActor : @preconcurrency P { + var prop: Int { + get { 42 } + set {} + } + + func fn() -> Int? { nil } +} + +// protocol witness for P.prop.getter in conformance MyActor +// CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s27preconcurrency_conformances7MyActorCAA1PA2aDP4prop1TQzvgTW : $@convention(witness_method: P) (@in_guaranteed MyActor) -> @out Int +// CHECK: [[SELF_REF:%.*]] = load_borrow %1 : $*MyActor +// CHECK-NEXT: [[EXEC:%.*]] = extract_executor [[SELF_REF]] : $MyActor +// CHECK: [[CHECK_EXEC_REF:%.*]] = function_ref @$ss22_checkExpectedExecutor14_filenameStart01_D6Length01_D7IsASCII5_line9_executoryBp_BwBi1_BwBetF +// CHECK-NEXT: {{.*}} = apply [[CHECK_EXEC_REF]]({{.*}}, [[EXEC]]) + +// protocol witness for P.prop.setter in conformance MyActor +// CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s27preconcurrency_conformances7MyActorCAA1PA2aDP4prop1TQzvsTW : $@convention(witness_method: P) (@in Int, @inout MyActor) -> () +// CHECK: [[SELF_REF:%.*]] = load_borrow %1 : $*MyActor +// CHECK-NEXT: [[EXEC:%.*]] = extract_executor [[SELF_REF]] : $MyActor +// CHECK: [[CHECK_EXEC_REF:%.*]] = function_ref @$ss22_checkExpectedExecutor14_filenameStart01_D6Length01_D7IsASCII5_line9_executoryBp_BwBi1_BwBetF +// CHECK-NEXT: {{.*}} = apply [[CHECK_EXEC_REF]]({{.*}}, [[EXEC]]) + +// protocol witness for P.prop.modify in conformance MyActor +// CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s27preconcurrency_conformances7MyActorCAA1PA2aDP4prop1TQzvMTW : $@yield_once @convention(witness_method: P) @substituted <τ_0_0, τ_0_1> (@inout τ_0_0) -> @yields @inout τ_0_1 for +// CHECK: [[SELF_REF:%.*]] = load_borrow %0 : $*MyActor +// CHECK-NEXT: [[EXEC:%.*]] = extract_executor [[SELF_REF]] : $MyActor +// CHECK: [[CHECK_EXEC_REF:%.*]] = function_ref @$ss22_checkExpectedExecutor14_filenameStart01_D6Length01_D7IsASCII5_line9_executoryBp_BwBi1_BwBetF +// CHECK-NEXT: {{.*}} = apply [[CHECK_EXEC_REF]]({{.*}}, [[EXEC]]) + +// protocol witness for P.fn() in conformance MyActor +// CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s27preconcurrency_conformances7MyActorCAA1PA2aDP2fn1TQzSgyFTW : $@convention(witness_method: P) (@in_guaranteed MyActor) -> @out Optional +// CHECK: [[SELF_REF:%.*]] = load_borrow %1 : $*MyActor +// CHECK-NEXT: [[EXEC:%.*]] = extract_executor [[SELF_REF]] : $MyActor +// CHECK: [[CHECK_EXEC_REF:%.*]] = function_ref @$ss22_checkExpectedExecutor14_filenameStart01_D6Length01_D7IsASCII5_line9_executoryBp_BwBi1_BwBetF +// CHECK-NEXT: {{.*}} = apply [[CHECK_EXEC_REF]]({{.*}}, [[EXEC]]) + +extension MyActor : @preconcurrency Q { + @MainActor static var data: [Int]? { + get { nil } + set {} + } + + @MainActor static func staticFn() -> String? { nil } +} + +// protocol witness for static Q.data.getter in conformance MyActor +// CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s27preconcurrency_conformances7MyActorCAA1QA2aDP4dataSaySiGSgvgZTW : $@convention(witness_method: Q) (@thick MyActor.Type) -> @owned Optional> +// CHECK: [[MAIN_ACTOR:%.*]] = begin_borrow {{.*}} : $MainActor +// CHECK-NEXT: [[EXEC:%.*]] = extract_executor [[MAIN_ACTOR]] : $MainActor +// CHECK: [[CHECK_EXEC_REF:%.*]] = function_ref @$ss22_checkExpectedExecutor14_filenameStart01_D6Length01_D7IsASCII5_line9_executoryBp_BwBi1_BwBetF +// CHECK-NEXT: {{.*}} = apply [[CHECK_EXEC_REF]]({{.*}}, [[EXEC]]) + +// protocol witness for static Q.data.setter in conformance MyActor +// CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s27preconcurrency_conformances7MyActorCAA1QA2aDP4dataSaySiGSgvsZTW : $@convention(witness_method: Q) (@owned Optional>, @thick MyActor.Type) -> () +// CHECK: [[MAIN_ACTOR:%.*]] = begin_borrow {{.*}} : $MainActor +// CHECK-NEXT: [[EXEC:%.*]] = extract_executor [[MAIN_ACTOR]] : $MainActor +// CHECK: [[CHECK_EXEC_REF:%.*]] = function_ref @$ss22_checkExpectedExecutor14_filenameStart01_D6Length01_D7IsASCII5_line9_executoryBp_BwBi1_BwBetF +// CHECK-NEXT: {{.*}} = apply [[CHECK_EXEC_REF]]({{.*}}, [[EXEC]]) + +// protocol witness for static Q.data.modify in conformance MyActor +// CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s27preconcurrency_conformances7MyActorCAA1QA2aDP4dataSaySiGSgvMZTW : $@yield_once @convention(witness_method: Q) @substituted <τ_0_0> (@thick τ_0_0.Type) -> @yields @inout Optional> for +// CHECK: [[MAIN_ACTOR:%.*]] = begin_borrow {{.*}} : $MainActor +// CHECK-NEXT: [[EXEC:%.*]] = extract_executor [[MAIN_ACTOR]] : $MainActor +// CHECK: [[CHECK_EXEC_REF:%.*]] = function_ref @$ss22_checkExpectedExecutor14_filenameStart01_D6Length01_D7IsASCII5_line9_executoryBp_BwBi1_BwBetF +// CHECK-NEXT: {{.*}} = apply [[CHECK_EXEC_REF]]({{.*}}, [[EXEC]])