From a0088327d455bbea32a41c9c5bbd38bc89d61a0d Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Wed, 11 Sep 2024 16:04:37 -0700 Subject: [PATCH 1/3] [concurrency] Represent a SILFunction without isolation as std::optional instead of ActorIsolation::Unspecified. The reason why is that we want to distinguish inbetween SILFunction's that are marked as unspecified by SILGen and those that are parsed from textual SIL that do not have any specified isolation. This will make it easier to write nice FileCheck tests against SILGen output on what is the inferred isolation for various items. NFCI. --- include/swift/SIL/SILFunction.h | 6 +- .../swift/SILOptimizer/Utils/PartitionUtils.h | 4 +- lib/SIL/IR/SILPrinter.cpp | 3 +- lib/SILGen/SILGen.cpp | 12 +- lib/SILOptimizer/Utils/SILIsolationInfo.cpp | 122 +++++++++--------- .../actor_isolation_filecheck.swift | 34 +++++ test/SILGen/default_constructor.swift | 1 + test/SILGen/functions.swift | 1 + test/SILGen/global_init_attribute.swift | 3 + test/SILGen/package_sil_linkage.swift | 33 ++++- test/SILGen/stored_property_default_arg.swift | 8 ++ .../synthesized_conformance_class.swift | 3 + .../SILGen/synthesized_conformance_enum.swift | 8 ++ .../synthesized_conformance_struct.swift | 6 + test/SILOptimizer/moveonly_raw_layout.swift | 2 + test/SILOptimizer/specialize_self.swift | 2 + test/Serialization/nested-protocols.swift | 4 +- 17 files changed, 174 insertions(+), 78 deletions(-) create mode 100644 test/Concurrency/actor_isolation_filecheck.swift diff --git a/include/swift/SIL/SILFunction.h b/include/swift/SIL/SILFunction.h index b56a794a1f4..f000182ab91 100644 --- a/include/swift/SIL/SILFunction.h +++ b/include/swift/SIL/SILFunction.h @@ -353,7 +353,7 @@ private: unsigned BlockListChangeIdx = 0; /// The isolation of this function. - ActorIsolation actorIsolation = ActorIsolation::forUnspecified(); + std::optional actorIsolation; /// The function's bare attribute. Bare means that the function is SIL-only /// and does not require debug info. @@ -1453,7 +1453,9 @@ public: actorIsolation = newActorIsolation; } - ActorIsolation getActorIsolation() const { return actorIsolation; } + std::optional getActorIsolation() const { + return actorIsolation; + } /// Return the source file that this SILFunction belongs to if it exists. SourceFile *getSourceFile() const; diff --git a/include/swift/SILOptimizer/Utils/PartitionUtils.h b/include/swift/SILOptimizer/Utils/PartitionUtils.h index d971d5d5af9..9e334dd9e90 100644 --- a/include/swift/SILOptimizer/Utils/PartitionUtils.h +++ b/include/swift/SILOptimizer/Utils/PartitionUtils.h @@ -1425,9 +1425,9 @@ private: // our transferring operand. If so, we can squelch this. if (auto functionIsolation = transferringOp->getUser()->getFunction()->getActorIsolation()) { - if (functionIsolation.isActorIsolated() && + if (functionIsolation->isActorIsolated() && SILIsolationInfo::get(transferringOp->getUser()) - .hasSameIsolation(functionIsolation)) + .hasSameIsolation(*functionIsolation)) return; } } diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index 98d09771944..024b4d9fa0f 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -3376,9 +3376,10 @@ void SILFunction::print(SILPrintContext &PrintCtx) const { if (auto functionIsolation = getActorIsolation()) { OS << "// Isolation: "; - functionIsolation.print(OS); + functionIsolation->print(OS); OS << '\n'; } + printClangQualifiedNameCommentIfPresent(OS, getClangDecl()); OS << "sil "; diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index 9262b0daf97..7aaa4962043 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -754,10 +754,7 @@ SILFunction *SILGenModule::getFunction(SILDeclRef constant, // If we have global actor isolation for our constant, put the isolation onto // the function. - if (auto isolation = - getActorIsolationOfContext(constant.getInnermostDeclContext())) { - F->setActorIsolation(isolation); - } + F->setActorIsolation(getActorIsolationOfContext(constant.getInnermostDeclContext())); assert(F && "SILFunction should have been defined"); @@ -1248,12 +1245,7 @@ void SILGenModule::preEmitFunction(SILDeclRef constant, SILFunction *F, F->setGenericEnvironment(genericEnv, capturedEnvs, forwardingSubs); } - // If we have global actor isolation for our constant, put the isolation onto - // the function. - if (auto isolation = - getActorIsolationOfContext(constant.getInnermostDeclContext())) { - F->setActorIsolation(isolation); - } + F->setActorIsolation(getActorIsolationOfContext(constant.getInnermostDeclContext())); // Create a debug scope for the function using astNode as source location. F->setDebugScope(new (M) SILDebugScope(Loc, F)); diff --git a/lib/SILOptimizer/Utils/SILIsolationInfo.cpp b/lib/SILOptimizer/Utils/SILIsolationInfo.cpp index a438cb412c2..617c912a90a 100644 --- a/lib/SILOptimizer/Utils/SILIsolationInfo.cpp +++ b/lib/SILOptimizer/Utils/SILIsolationInfo.cpp @@ -509,59 +509,62 @@ SILIsolationInfo SILIsolationInfo::get(SILInstruction *inst) { // Treat function ref as either actor isolated or sendable. if (auto *fri = dyn_cast(inst)) { - auto isolation = fri->getReferencedFunction()->getActorIsolation(); + if (auto optIsolation = fri->getReferencedFunction()->getActorIsolation()) { + auto isolation = *optIsolation; - // First check if we are actor isolated at the AST level... if we are, then - // create the relevant actor isolated. - if (isolation.isActorIsolated()) { - if (isolation.isGlobalActor()) { - return SILIsolationInfo::getGlobalActorIsolated( - fri, isolation.getGlobalActor()); + // First check if we are actor isolated at the AST level... if we are, + // then create the relevant actor isolated. + if (isolation.isActorIsolated()) { + if (isolation.isGlobalActor()) { + return SILIsolationInfo::getGlobalActorIsolated( + fri, isolation.getGlobalActor()); + } + + // TODO: We need to be able to support flow sensitive actor instances + // like we do for partial apply. Until we do so, just store SILValue() + // for this. This could cause a problem if we can construct a function + // ref and invoke it with two different actor instances of the same type + // and pass in the same parameters to both. We should error and we would + // not with this impl since we could not distinguish the two. + if (isolation.getKind() == ActorIsolation::ActorInstance) { + return SILIsolationInfo::getFlowSensitiveActorIsolated(fri, + isolation); + } + + assert(isolation.getKind() != ActorIsolation::Erased && + "Implement this!"); } - // TODO: We need to be able to support flow sensitive actor instances like - // we do for partial apply. Until we do so, just store SILValue() for - // this. This could cause a problem if we can construct a function ref and - // invoke it with two different actor instances of the same type and pass - // in the same parameters to both. We should error and we would not with - // this impl since we could not distinguish the two. - if (isolation.getKind() == ActorIsolation::ActorInstance) { - return SILIsolationInfo::getFlowSensitiveActorIsolated(fri, isolation); - } - - assert(isolation.getKind() != ActorIsolation::Erased && - "Implement this!"); - } - - // Then check if we have something that is nonisolated unsafe. - if (isolation.isNonisolatedUnsafe()) { - // First check if our function_ref is a method of a global actor isolated - // type. In such a case, we create a global actor isolated - // nonisolated(unsafe) so that if we assign the value to another variable, - // the variable still says that it is the appropriate global actor - // isolated thing. - // - // E.x.: - // - // @MainActor - // struct X { nonisolated(unsafe) var x: NonSendableThing { ... } } - // - // We want X.x to be safe to use... but to have that 'z' in the following - // is considered MainActor isolated. - // - // let z = X.x - // - auto *func = fri->getReferencedFunction(); - auto funcType = func->getLoweredFunctionType(); - if (funcType->hasSelfParam()) { - auto selfParam = funcType->getSelfInstanceType( - fri->getModule(), func->getTypeExpansionContext()); - if (auto *nomDecl = selfParam->getNominalOrBoundGenericNominal()) { - auto isolation = swift::getActorIsolation(nomDecl); - if (isolation.isGlobalActor()) { - return SILIsolationInfo::getGlobalActorIsolated( - fri, isolation.getGlobalActor()) - .withUnsafeNonIsolated(true); + // Then check if we have something that is nonisolated unsafe. + if (isolation.isNonisolatedUnsafe()) { + // First check if our function_ref is a method of a global actor + // isolated type. In such a case, we create a global actor isolated + // nonisolated(unsafe) so that if we assign the value to another + // variable, the variable still says that it is the appropriate global + // actor isolated thing. + // + // E.x.: + // + // @MainActor + // struct X { nonisolated(unsafe) var x: NonSendableThing { ... } } + // + // We want X.x to be safe to use... but to have that 'z' in the + // following is considered MainActor isolated. + // + // let z = X.x + // + auto *func = fri->getReferencedFunction(); + auto funcType = func->getLoweredFunctionType(); + if (funcType->hasSelfParam()) { + auto selfParam = funcType->getSelfInstanceType( + fri->getModule(), func->getTypeExpansionContext()); + if (auto *nomDecl = selfParam->getNominalOrBoundGenericNominal()) { + auto nomDeclIsolation = swift::getActorIsolation(nomDecl); + if (nomDeclIsolation.isGlobalActor()) { + return SILIsolationInfo::getGlobalActorIsolated( + fri, nomDeclIsolation.getGlobalActor()) + .withUnsafeNonIsolated(true); + } } } } @@ -868,8 +871,11 @@ SILIsolationInfo SILIsolationInfo::get(SILArgument *arg) { // code. In the case of a non-actor, we can only have an allocator that is // global actor isolated, so we will never hit this code path. if (declRef.kind == SILDeclRef::Kind::Allocator) { - if (fArg->getFunction()->getActorIsolation().isActorInstanceIsolated()) { - return SILIsolationInfo::getDisconnected(false /*nonisolated(unsafe)*/); + if (auto isolation = fArg->getFunction()->getActorIsolation()) { + if (isolation->isActorInstanceIsolated()) { + return SILIsolationInfo::getDisconnected( + false /*nonisolated(unsafe)*/); + } } } @@ -878,13 +884,13 @@ SILIsolationInfo SILIsolationInfo::get(SILArgument *arg) { // we need to pass in a "fake" ActorInstance that users know is a sentinel // for the self value. if (auto functionIsolation = fArg->getFunction()->getActorIsolation()) { - if (functionIsolation.isActorInstanceIsolated() && declRef.getDecl()) { + if (functionIsolation->isActorInstanceIsolated() && declRef.getDecl()) { if (auto *accessor = dyn_cast_or_null(declRef.getFuncDecl())) { if (accessor->isInitAccessor()) { return SILIsolationInfo::getActorInstanceIsolated( fArg, ActorInstance::getForActorAccessorInit(), - functionIsolation.getActor()); + functionIsolation->getActor()); } } } @@ -894,15 +900,15 @@ SILIsolationInfo SILIsolationInfo::get(SILArgument *arg) { // Otherwise, if we do not have an isolated argument and are not in an // allocator, then we might be isolated via global isolation. if (auto functionIsolation = fArg->getFunction()->getActorIsolation()) { - if (functionIsolation.isActorIsolated()) { - if (functionIsolation.isGlobalActor()) { + if (functionIsolation->isActorIsolated()) { + if (functionIsolation->isGlobalActor()) { return SILIsolationInfo::getGlobalActorIsolated( - fArg, functionIsolation.getGlobalActor()); + fArg, functionIsolation->getGlobalActor()); } return SILIsolationInfo::getActorInstanceIsolated( fArg, ActorInstance::getForActorAccessorInit(), - functionIsolation.getActor()); + functionIsolation->getActor()); } } diff --git a/test/Concurrency/actor_isolation_filecheck.swift b/test/Concurrency/actor_isolation_filecheck.swift new file mode 100644 index 00000000000..55b61d2b642 --- /dev/null +++ b/test/Concurrency/actor_isolation_filecheck.swift @@ -0,0 +1,34 @@ +// RUN: %target-swift-frontend -swift-version 6 -disable-availability-checking %s -emit-silgen -o - | %FileCheck %s +// RUN: %target-swift-frontend -swift-version 6 -disable-availability-checking %s -emit-sil -o /dev/null -verify + +// README: This file contains FileCheck tests that validate that specific Swift +// entities have their respective SILFunctions assigned the correct actor +// isolation by FileChecking against SILGen. + +//////////////////////// +// MARK: Declarations // +//////////////////////// + +func useValueAsync(_ t: T) async {} + +///////////////// +// MARK: Tests // +///////////////// + +// CHECK: // synchronousActorIsolatedFinalClassMethodError() +// CHECK-NEXT: // Isolation: global_actor. type: MainActor +// CHECK-NEXT: sil hidden [ossa] @$s25actor_isolation_filecheck45synchronousActorIsolatedFinalClassMethodErroryyYaF : $@convention(thin) @async () -> () { +@MainActor func synchronousActorIsolatedFinalClassMethodError() async { + @MainActor final class Test { + // CHECK: // foo() in Test #1 in synchronousActorIsolatedFinalClassMethodError() + // CHECK-NEXT: // Isolation: global_actor. type: MainActor + // CHECK-NEXT: sil private [ossa] @$s25actor_isolation_filecheck45synchronousActorIsolatedFinalClassMethodErroryyYaF4TestL_C3fooyyF : $@convention(method) (@guaranteed Test) -> () { + func foo() {} + } + + let t = Test() + let erased: () -> Void = t.foo + + await useValueAsync(erased) // expected-error {{sending 'erased' risks causing data races}} + // expected-note @-1 {{sending main actor-isolated 'erased' to nonisolated global function 'useValueAsync' risks causing data races between nonisolated and main actor-isolated uses}} +} diff --git a/test/SILGen/default_constructor.swift b/test/SILGen/default_constructor.swift index a7f826cbc1b..f78be6f2327 100644 --- a/test/SILGen/default_constructor.swift +++ b/test/SILGen/default_constructor.swift @@ -97,6 +97,7 @@ struct G { // CHECK-NOT: default_constructor.G.init() // CHECK-LABEL: default_constructor.G.init(bar: Swift.Optional) +// CHECK-NEXT: // Isolation: // CHECK-NEXT: sil hidden [ossa] @$s19default_constructor1GV{{[_0-9a-zA-Z]*}}fC // CHECK-NOT: default_constructor.G.init() diff --git a/test/SILGen/functions.swift b/test/SILGen/functions.swift index 6f0cb1befc7..cf8460ffa34 100644 --- a/test/SILGen/functions.swift +++ b/test/SILGen/functions.swift @@ -481,6 +481,7 @@ func testNoescape() { } // CHECK-LABEL: functions.testNoescape() -> () +// CHECK-NEXT: // Isolation: // CHECK-NEXT: sil hidden [ossa] @$s9functions12testNoescapeyyF : $@convention(thin) () -> () // CHECK: function_ref closure #1 () -> () in functions.testNoescape() -> () // CHECK-NEXT: function_ref @$s9functions12testNoescapeyyFyyXEfU_ : $@convention(thin) (@guaranteed { var Int }) -> () diff --git a/test/SILGen/global_init_attribute.swift b/test/SILGen/global_init_attribute.swift index 95167c71270..2a3e12f8160 100644 --- a/test/SILGen/global_init_attribute.swift +++ b/test/SILGen/global_init_attribute.swift @@ -10,6 +10,7 @@ import def_global let InternalConst = 42 // CHECK-NOT: [global_init] // CHECK: // global_init_attribute.InternalConst.unsafeMutableAddressor : Swift.Int +// CHECK-NEXT: // Isolation: // CHECK-NEXT: sil hidden [global_init] [ossa] @$s21global_init_attribute13InternalConstSivau func foo() -> Int { @@ -22,10 +23,12 @@ func bar(i: Int) { // CHECK-NOT: [global_init] // CHECK: // def_global.ExportedVar.unsafeMutableAddressor : Swift.Int +// CHECK-NEXT: // Isolation: // CHECK-NEXT: sil [global_init] @$s10def_global11ExportedVarSivau var InternalFoo = foo() // CHECK-NOT: [global_init] // CHECK: // global_init_attribute.InternalFoo.unsafeMutableAddressor : Swift.Int +// CHECK-NEXT: // Isolation: // CHECK-NEXT: sil hidden [global_init] [ossa] @$s21global_init_attribute11InternalFooSivau diff --git a/test/SILGen/package_sil_linkage.swift b/test/SILGen/package_sil_linkage.swift index 0b0cead4117..04eb678517a 100644 --- a/test/SILGen/package_sil_linkage.swift +++ b/test/SILGen/package_sil_linkage.swift @@ -12,11 +12,11 @@ /// Check serialization in SILGEN with resilience enabled. // RUN: %target-swift-emit-silgen -emit-verbose-sil -sil-verify-all -enable-library-evolution -module-name Utils %t/Utils.swift -package-name mypkg -I %t > %t/Utils-Res.sil -// RUN: %FileCheck %s --check-prefixes=UTILS-RES,UTILS-COMMON < %t/Utils-Res.sil +// RUN: %FileCheck %s --check-prefixes=UTILS-RES,UTILS-COMMON -input-file=%t/Utils-Res.sil /// Check for indirect access with a resiliently built module dependency. // RUN: %target-swift-emit-silgen -sil-verify-all %t/Client.swift -package-name mypkg -I %t > %t/Client-Res.sil -// RUN: %FileCheck %s --check-prefixes=CLIENT-RES,CLIENT-COMMON < %t/Client-Res.sil +// RUN: %FileCheck %s --check-prefixes=CLIENT-RES,CLIENT-COMMON -input-file=%t/Client-Res.sil // RUN: rm -rf %t/Utils.swiftmodule @@ -30,11 +30,11 @@ /// Check serialization in SILGEN with resilience not enabled. // RUN: %target-swift-emit-silgen -emit-verbose-sil -sil-verify-all -module-name Utils %t/Utils.swift -package-name mypkg -I %t > %t/Utils-NonRes.sil -// RUN: %FileCheck %s --check-prefixes=UTILS-NONRES,UTILS-COMMON < %t/Utils-NonRes.sil +// RUN: %FileCheck %s --check-prefixes=UTILS-NONRES,UTILS-COMMON -input-file %t/Utils-NonRes.sil /// Check for indirect access with a non-resiliently built module dependency. // RUN: %target-swift-emit-silgen -sil-verify-all %t/Client.swift -package-name mypkg -I %t > %t/Client-NonRes.sil -// RUN: %FileCheck %s --check-prefixes=CLIENT-NONRES,CLIENT-COMMON < %t/Client-NonRes.sil +// RUN: %FileCheck %s --check-prefixes=CLIENT-NONRES,CLIENT-COMMON -input-file %t/Client-NonRes.sil //--- Utils.swift @@ -538,11 +538,14 @@ package func f(_ arg: PublicStruct) -> Int { } // CLIENT-RES-LABEL: // f(_:) +// CLIENT-RES-NEXT: // Isolation: unspecified // CLIENT-RES-NEXT: sil package [ossa] @$s6Client1fySi5Utils12PublicStructVF : $@convention(thin) (@in_guaranteed PublicStruct) -> Int // CLIENT-RES-LABEL: // PublicStruct.data.getter +// CLIENT-RES-NEXT: // Isolation: unspecified // CLIENT-RES-NEXT: sil @$s5Utils12PublicStructV4dataSivg : $@convention(method) (@in_guaranteed PublicStruct) -> Int // CLIENT-NONRES-LABEL: // f(_:) +// CLIENT-NONRES-NEXT: // Isolation: unspecified // CLIENT-NONRES-NEXT: sil package [ossa] @$s6Client1fySi5Utils12PublicStructVF : $@convention(thin) (PublicStruct) -> Int public func ff(_ arg: PublicStruct) -> Int { @@ -550,9 +553,11 @@ public func ff(_ arg: PublicStruct) -> Int { } // CLIENT-RES-LABEL: // ff(_:) +// CLIENT-RES-NEXT: // Isolation: unspecified // CLIENT-RES-NEXT: sil [ossa] @$s6Client2ffySi5Utils12PublicStructVF : $@convention(thin) (@in_guaranteed PublicStruct) -> Int // CLIENT-NONRES-LABEL: // ff(_:) +// CLIENT-NONRES-NEXT: // Isolation: unspecified // CLIENT-NONRES-NEXT: sil [ossa] @$s6Client2ffySi5Utils12PublicStructVF : $@convention(thin) (PublicStruct) -> Int @@ -587,11 +592,14 @@ package func g(_ arg: PkgStruct) -> Int { } // CLIENT-RES-LABEL: // g(_:) +// CLIENT-RES-NEXT: // Isolation: unspecified // CLIENT-RES-NEXT: sil package [ossa] @$s6Client1gySi5Utils9PkgStructVF : $@convention(thin) (@in_guaranteed PkgStruct) -> Int // CLIENT-RES-LABEL: // PkgStruct.data.getter +// CLIENT-RES-NEXT: // Isolation: unspecified // CLIENT-RES-NEXT: sil package_external @$s5Utils9PkgStructV4dataSivg : $@convention(method) (@in_guaranteed PkgStruct) -> Int // CLIENT-NONRES-LABEL: // g(_:) +// CLIENT-NONRES-NEXT: // Isolation: unspecified // CLIENT-NONRES-NEXT: sil package [ossa] @$s6Client1gySi5Utils9PkgStructVF : $@convention(thin) (PkgStruct) -> Int package func gx(_ arg: UfiPkgClass) -> Int { @@ -599,6 +607,7 @@ package func gx(_ arg: UfiPkgClass) -> Int { } // CLIENT-COMMON-LABEL: // gx(_:) +// CLIENT-COMMON-NEXT: // Isolation: unspecified // CLIENT-COMMON-NEXT: sil package [ossa] @$s6Client2gxySi5Utils11UfiPkgClassCF : $@convention(thin) (@guaranteed UfiPkgClass) -> Int { package func m(_ arg: PkgStructGeneric) -> T { @@ -606,12 +615,15 @@ package func m(_ arg: PkgStructGeneric) -> T { } // CLIENT-RES-LABEL: // m(_:) +// CLIENT-RES-NEXT: // Isolation: unspecified // CLIENT-RES-NEXT: sil package [ossa] @$s6Client1myx5Utils16PkgStructGenericVyxGlF : $@convention(thin) (@in_guaranteed PkgStructGeneric) -> @out T { // CLIENT-RES-LABEL: // PkgStructGeneric.data.getter +// CLIENT-RES-NEXT: // Isolation: unspecified // CLIENT-RES-NEXT: sil package_external @$s5Utils16PkgStructGenericV4dataxvg : $@convention(method) <τ_0_0> (@in_guaranteed PkgStructGeneric<τ_0_0>) -> @out τ_0_0 // CLIENT-NONRES-LABEL: // m(_:) +// CLIENT-NONRES-NEXT: // Isolation: unspecified // CLIENT-NONRES-NEXT: sil package [ossa] @$s6Client1myx5Utils16PkgStructGenericVyxGlF : $@convention(thin) (@in_guaranteed PkgStructGeneric) -> @out T { @@ -625,12 +637,16 @@ package func n(_ arg: PkgStructWithPublicMember) -> Int { } // CLIENT-RES-LABEL: // n(_:) +// CLIENT-RES-NEXT: // Isolation: unspecified // CLIENT-RES-NEXT: sil package [ossa] @$s6Client1nySi5Utils25PkgStructWithPublicMemberVF : $@convention(thin) (@in_guaranteed PkgStructWithPublicMember) -> Int + // CLIENT-RES-LABEL: // PkgStructWithPublicMember.member.getter +// CLIENT-RES-NEXT: // Isolation: unspecified // CLIENT-RES-NEXT: sil package_external @$s5Utils25PkgStructWithPublicMemberV6memberAA0eC0Vvg : $@convention(method) (@in_guaranteed PkgStructWithPublicMember) -> @out PublicStruct // CLIENT-NONRES-LABEL: // n(_:) +// CLIENT-NONRES-NEXT: // Isolation: unspecified // CLIENT-NONRES-NEXT: sil package [ossa] @$s6Client1nySi5Utils25PkgStructWithPublicMemberVF : $@convention(thin) (PkgStructWithPublicMember) -> Int package func p(_ arg: PkgStructWithPublicExistential) -> any PublicProto { @@ -638,13 +654,16 @@ package func p(_ arg: PkgStructWithPublicExistential) -> any PublicProto { } // CLIENT-RES-LABEL: // p(_:) +// CLIENT-RES-NEXT: // Isolation: unspecified // CLIENT-RES-NEXT: sil package [ossa] @$s6Client1py5Utils11PublicProto_pAC013PkgStructWithC11ExistentialVF : $@convention(thin) (@in_guaranteed PkgStructWithPublicExistential) -> @out any PublicProto { // CLIENT-RES-LABEL: // PkgStructWithPublicExistential.member.getter +// CLIENT-RES-NEXT: // Isolation: unspecified // CLIENT-RES-NEXT: sil package_external @$s5Utils30PkgStructWithPublicExistentialV6memberAA0E5Proto_pvg : $@convention(method) (@in_guaranteed PkgStructWithPublicExistential) -> @out any PublicProto // CLIENT-NONRES-LABEL: // p(_:) +// CLIENT-NONRES-NEXT: // Isolation: unspecified // CLIENT-NONRES-NEXT: sil package [ossa] @$s6Client1py5Utils11PublicProto_pAC013PkgStructWithC11ExistentialVF : $@convention(thin) (@in_guaranteed PkgStructWithPublicExistential) -> @out any PublicProto { package func q(_ arg: PkgStructWithPkgExistential) -> any PkgProto { @@ -652,13 +671,16 @@ package func q(_ arg: PkgStructWithPkgExistential) -> any PkgProto { } // CLIENT-RES-LABEL: // q(_:) +// CLIENT-RES-NEXT: // Isolation: unspecified // CLIENT-RES-NEXT: sil package [ossa] @$s6Client1qy5Utils8PkgProto_pAC0c10StructWithC11ExistentialVF : $@convention(thin) (@in_guaranteed PkgStructWithPkgExistential) -> @out any PkgProto { // CLIENT-RES-LABEL: // PkgStructWithPkgExistential.member.getter +// CLIENT-RES-NEXT: // Isolation: unspecified // CLIENT-RES-NEXT: sil package_external @$s5Utils013PkgStructWithB11ExistentialV6memberAA0B5Proto_pvg : $@convention(method) (@in_guaranteed PkgStructWithPkgExistential) -> @out any PkgProto // CLIENT-NONRES-LABEL: // q(_:) +// CLIENT-NONRES-NEXT: // Isolation: unspecified // CLIENT-NONRES-NEXT: sil package [ossa] @$s6Client1qy5Utils8PkgProto_pAC0c10StructWithC11ExistentialVF : $@convention(thin) (@in_guaranteed PkgStructWithPkgExistential) -> @out any PkgProto { package func r(_ arg: PublicProto) -> Int { @@ -666,6 +688,7 @@ package func r(_ arg: PublicProto) -> Int { } // CLIENT-COMMON-LABEL: // r(_:) +// CLIENT-COMMON-NEXT: // Isolation: unspecified // CLIENT-COMMON-NEXT: sil package [ossa] @$s6Client1rySi5Utils11PublicProto_pF : $@convention(thin) (@in_guaranteed any PublicProto) -> Int { package func s(_ arg: PkgProto) -> Int { @@ -673,6 +696,7 @@ package func s(_ arg: PkgProto) -> Int { } // CLIENT-COMMON-LABEL: // s(_:) +// CLIENT-COMMON-NEXT: // Isolation: unspecified // CLIENT-COMMON-NEXT: sil package [ossa] @$s6Client1sySi5Utils8PkgProto_pF : $@convention(thin) (@in_guaranteed any PkgProto) -> Int { public func t(_ arg: any PublicProto) -> Int { @@ -700,4 +724,5 @@ package func w(_ arg: PkgKlass) -> Int { } // CLIENT-COMMON-LABEL: // w(_:) +// CLIENT-COMMON-NEXT: // Isolation: unspecified // CLIENT-COMMON-NEXT: sil package [ossa] @$s6Client1wySi5Utils8PkgKlassCF : $@convention(thin) (@guaranteed PkgKlass) -> Int diff --git a/test/SILGen/stored_property_default_arg.swift b/test/SILGen/stored_property_default_arg.swift index 8c9948ff537..4d7ecfa1b4e 100644 --- a/test/SILGen/stored_property_default_arg.swift +++ b/test/SILGen/stored_property_default_arg.swift @@ -3,6 +3,7 @@ // Currently, this only appears for memberwise initializers. // CHECK: default argument 0 of A.init(b:c:) +// CHECK-NEXT: // Isolation: unspecified // CHECK-NEXT: sil hidden [ossa] @$s27stored_property_default_arg1AV1b1cACSi_SbtcfcfA_ : $@convention(thin) () -> Int { // CHECK-NEXT: bb0: // CHECK-NEXT: function_ref variable initialization expression of A.b @@ -12,6 +13,7 @@ // CHECK-NEXT: } // CHECK: default argument 1 of A.init(b:c:) +// CHECK-NEXT: // Isolation: unspecified // CHECK-NEXT: sil hidden [ossa] @$s27stored_property_default_arg1AV1b1cACSi_SbtcfcfA0_ : $@convention(thin) () -> Bool { // CHECK-NEXT: bb0: // CHECK-NEXT: function_ref variable initialization expression of A.c @@ -45,6 +47,7 @@ func checkConcreteType() { } // CHECK: default argument 1 of F.init(g:h:) +// CHECK-NEXT: // Isolation: unspecified // CHECK-NEXT: sil hidden [ossa] @$s27stored_property_default_arg1FV1g1hACyxGx_SitcfcfA0_ : $@convention(thin) () -> Int { // CHECK-NEXT: bb0: // CHECK-NEXT: function_ref variable initialization expression of F.h @@ -90,6 +93,7 @@ func checkOptionalNil() { } // CHECK: default argument 0 of O.init(p:q:) +// CHECK-NEXT: // Isolation: unspecified // CHECK-NEXT: sil hidden [ossa] @$s27stored_property_default_arg1OV1p1qACyxGx_x_SittcfcfA_ : $@convention(thin) () -> @out T { // CHECK: bb0([[PARAM_0:.*]] : $*T): // CHECK-NEXT: function_ref variable initialization expression of O.p @@ -100,6 +104,7 @@ func checkOptionalNil() { // CHECK-NEXT: } // CHECK: default argument 1 of O.init(p:q:) +// CHECK-NEXT: // Isolation: unspecified // CHECK-NEXT: sil hidden [ossa] @$s27stored_property_default_arg1OV1p1qACyxGx_x_SittcfcfA0_ : $@convention(thin) () -> (@out T, Int) { // CHECK: bb0([[PARAM_0:.*]] : $*T): // CHECK-NEXT: function_ref variable initialization expression of O.q @@ -135,6 +140,7 @@ func checkIndirect() { } // CHECK: default argument 0 of U.init(v:w:) +// CHECK-NEXT: // Isolation: unspecified // CHECK-NEXT: sil hidden [ossa] @$s27stored_property_default_arg1UV1v1wAcA1TCSg_AGtcfcfA_ : $@convention(thin) () -> @owned Optional { // CHECK-NEXT: bb0: // CHECK-NEXT: function_ref variable initialization expression of U.v @@ -144,6 +150,7 @@ func checkIndirect() { // CHECK-NEXT: } // CHECK: default argument 1 of U.init(v:w:) +// CHECK-NEXT: // Isolation: unspecified // CHECK-NEXT: sil hidden [ossa] @$s27stored_property_default_arg1UV1v1wAcA1TCSg_AGtcfcfA0_ : $@convention(thin) () -> @owned T { // CHECK-NEXT: bb0: // CHECK-NEXT: function_ref variable initialization expression of U.w @@ -179,6 +186,7 @@ func checkReferenceTypes() { } // CHECK: default argument 0 of AA.init(ab:) +// CHECK-NEXT: // Isolation: unspecified // CHECK-NEXT: sil hidden [ossa] @$s27stored_property_default_arg2AAV2abAcA1ZCSg2ac_AG2adt_tcfcfA_ : $@convention(thin) () -> (@owned Optional, @owned Optional) { // CHECK-NEXT: bb0: // CHECK-NEXT: function_ref variable initialization expression of AA.ab diff --git a/test/SILGen/synthesized_conformance_class.swift b/test/SILGen/synthesized_conformance_class.swift index a19db139c78..5db67fe9dbf 100644 --- a/test/SILGen/synthesized_conformance_class.swift +++ b/test/SILGen/synthesized_conformance_class.swift @@ -64,14 +64,17 @@ class Nonfinal { extension Final: Encodable where T: Encodable {} // CHECK-LABEL: // Final.encode(to:) +// CHECK-NEXT: // Isolation: unspecified // CHECK-NEXT: sil hidden [ossa] @$s29synthesized_conformance_class5FinalCAASERzlE6encode2toys7Encoder_p_tKF : $@convention(method) (@in_guaranteed any Encoder, @guaranteed Final) -> @error any Error { extension Final: Decodable where T: Decodable {} // CHECK-LABEL: // Final.init(from:) +// CHECK-NEXT: // Isolation: unspecified // CHECK-NEXT: sil hidden [exact_self_class] [ossa] @$s29synthesized_conformance_class5FinalCAASeRzlE4fromACyxGs7Decoder_p_tKcfC : $@convention(method) (@in any Decoder, @thick Final.Type) -> (@owned Final, @error any Error) { extension Nonfinal: Encodable where T: Encodable {} // CHECK-LABEL: // Nonfinal.encode(to:) +// CHECK-NEXT: // Isolation: unspecified // CHECK-NEXT: sil hidden [ossa] @$s29synthesized_conformance_class8NonfinalCAASERzlE6encode2toys7Encoder_p_tKF : $@convention(method) (@in_guaranteed any Encoder, @guaranteed Nonfinal) -> @error any Error { final class FinalHashableClass : Hashable { diff --git a/test/SILGen/synthesized_conformance_enum.swift b/test/SILGen/synthesized_conformance_enum.swift index c168af1dea9..9d0f4fcf644 100644 --- a/test/SILGen/synthesized_conformance_enum.swift +++ b/test/SILGen/synthesized_conformance_enum.swift @@ -36,22 +36,28 @@ enum NoValues { extension Enum: Equatable where T: Equatable {} // CHECK-FRAGILE-LABEL: // static Enum.__derived_enum_equals(_:_:) +// CHECK-FRAGILE-NEXT: // Isolation: unspecified // CHECK-FRAGILE-NEXT: sil hidden [ossa] @$s28synthesized_conformance_enum4EnumOAASQRzlE010__derived_C7_equalsySbACyxG_AEtFZ : $@convention(method) (@in_guaranteed Enum, @in_guaranteed Enum, @thin Enum.Type) -> Bool { // CHECK-RESILIENT-LABEL: // static Enum.== infix(_:_:) +// CHECK-RESILIENT-NEXT: // Isolation: unspecified // CHECK-RESILIENT-NEXT: sil hidden [ossa] @$s28synthesized_conformance_enum4EnumOAASQRzlE2eeoiySbACyxG_AEtFZ : $@convention(method) (@in_guaranteed Enum, @in_guaranteed Enum, @thin Enum.Type) -> Bool { extension Enum: Hashable where T: Hashable {} // CHECK-LABEL: // Enum.hash(into:) +// CHECK-NEXT: // Isolation: unspecified // CHECK-NEXT: sil hidden [ossa] @$s28synthesized_conformance_enum4EnumOAASHRzlE4hash4intoys6HasherVz_tF : $@convention(method) (@inout Hasher, @in_guaranteed Enum) -> () { // CHECK-LABEL: // Enum.hashValue.getter +// CHECK-NEXT: // Isolation: unspecified // CHECK-NEXT: sil hidden [ossa] @$s28synthesized_conformance_enum4EnumOAASHRzlE9hashValueSivg : $@convention(method) (@in_guaranteed Enum) -> Int { extension Enum: Codable where T: Codable {} // CHECK-LABEL: // Enum.encode(to:) +// CHECK-NEXT: // Isolation: unspecified // CHECK-NEXT: sil hidden [ossa] @$s28synthesized_conformance_enum4EnumOAASeRzSERzlE6encode2toys7Encoder_p_tKF : $@convention(method) (@in_guaranteed any Encoder, @in_guaranteed Enum) -> @error any Error { // CHECK-LABEL: // Enum.init(from:) +// CHECK-NEXT: // Isolation: unspecified // CHECK-NEXT: sil hidden [ossa] @$s28synthesized_conformance_enum4EnumOAASeRzSERzlE4fromACyxGs7Decoder_p_tKcfC : $@convention(method) (@in any Decoder, @thin Enum.Type) -> (@out Enum, @error any Error) extension NoValues: CaseIterable {} @@ -61,9 +67,11 @@ extension NoValues: CaseIterable {} extension NoValues: Codable {} // CHECK-LABEL: // NoValues.encode(to:) +// CHECK-NEXT: // Isolation: unspecified // CHECK-NEXT: sil hidden [ossa] @$s28synthesized_conformance_enum8NoValuesO6encode2toys7Encoder_p_tKF : $@convention(method) (@in_guaranteed any Encoder, NoValues) -> @error any Error { // CHECK-LABEL: // NoValues.init(from:) +// CHECK-NEXT: // Isolation: unspecified // CHECK-NEXT: sil hidden [ossa] @$s28synthesized_conformance_enum8NoValuesO4fromACs7Decoder_p_tKcfC : $@convention(method) (@in any Decoder, @thin NoValues.Type) -> (NoValues, @error any Error) // Witness tables for Enum diff --git a/test/SILGen/synthesized_conformance_struct.swift b/test/SILGen/synthesized_conformance_struct.swift index e5e58cfe317..d173b002565 100644 --- a/test/SILGen/synthesized_conformance_struct.swift +++ b/test/SILGen/synthesized_conformance_struct.swift @@ -35,22 +35,28 @@ struct Struct { extension Struct: Equatable where T: Equatable {} // CHECK-FRAGILE-LABEL: // static Struct.__derived_struct_equals(_:_:) +// CHECK-FRAGILE-NEXT: // Isolation: unspecified // CHECK-FRAGILE-NEXT: sil hidden [ossa] @$s30synthesized_conformance_struct6StructVAASQRzlE010__derived_C7_equalsySbACyxG_AEtFZ : $@convention(method) (@in_guaranteed Struct, @in_guaranteed Struct, @thin Struct.Type) -> Bool { // CHECK-RESILIENT-LABEL: // static Struct.== infix(_:_:) +// CHECK-RESILIENT-NEXT: // Isolation: unspecified // CHECK-RESILIENT-NEXT: sil hidden [ossa] @$s30synthesized_conformance_struct6StructVAASQRzlE2eeoiySbACyxG_AEtFZ : $@convention(method) (@in_guaranteed Struct, @in_guaranteed Struct, @thin Struct.Type) -> Bool { extension Struct: Hashable where T: Hashable {} // CHECK-LABEL: // Struct.hash(into:) +// CHECK-NEXT: // Isolation: unspecified // CHECK-NEXT: sil hidden [ossa] @$s30synthesized_conformance_struct6StructVAASHRzlE4hash4intoys6HasherVz_tF : $@convention(method) (@inout Hasher, @in_guaranteed Struct) -> () { // CHECK-LABEL: // Struct.hashValue.getter +// CHECK-NEXT: // Isolation: unspecified // CHECK-NEXT: sil hidden [ossa] @$s30synthesized_conformance_struct6StructVAASHRzlE9hashValueSivg : $@convention(method) (@in_guaranteed Struct) -> Int { extension Struct: Codable where T: Codable {} // CHECK-LABEL: // Struct.encode(to:) +// CHECK-NEXT: // Isolation: unspecified // CHECK-NEXT: sil hidden [ossa] @$s30synthesized_conformance_struct6StructVAASeRzSERzlE6encode2toys7Encoder_p_tKF : $@convention(method) (@in_guaranteed any Encoder, @in_guaranteed Struct) -> @error any Error { // CHECK-LABEL: // Struct.init(from:) +// CHECK-NEXT: // Isolation: unspecified // CHECK-NEXT: sil hidden [ossa] @$s30synthesized_conformance_struct6StructVAASeRzSERzlE4fromACyxGs7Decoder_p_tKcfC : $@convention(method) (@in any Decoder, @thin Struct.Type) -> (@out Struct, @error any Error) diff --git a/test/SILOptimizer/moveonly_raw_layout.swift b/test/SILOptimizer/moveonly_raw_layout.swift index b5a05836e72..c080d3d8f35 100644 --- a/test/SILOptimizer/moveonly_raw_layout.swift +++ b/test/SILOptimizer/moveonly_raw_layout.swift @@ -13,6 +13,7 @@ struct Lock: ~Copyable { var _address: Builtin.RawPointer { return Builtin.addressOfBorrow(self) } // CHECK-LABEL: // Lock.init() + // CHECK-NEXT: Isolation: unspecified // CHECK-NEXT: sil{{.*}} @[[INIT:\$.*4LockV.*fC]] : init() { // CHECK-NOT: destroy_addr @@ -26,6 +27,7 @@ struct Lock: ~Copyable { } // CHECK-LABEL: // Lock.deinit + // CHECK-NEXT: Isolation: unspecified // CHECK-NEXT: sil{{.*}} @[[DEINIT:\$.*4LockV.*fD]] : deinit { // CHECK-NOT: destroy_addr diff --git a/test/SILOptimizer/specialize_self.swift b/test/SILOptimizer/specialize_self.swift index 4cd5f81b69e..7d186a35774 100644 --- a/test/SILOptimizer/specialize_self.swift +++ b/test/SILOptimizer/specialize_self.swift @@ -4,12 +4,14 @@ // CHECK-NOT: generic specialization of specialize_self.cast (A) -> Swift.Optional // CHECK-LABEL: specialize_self.cast(A) -> Swift.Optional +// CHECK-NEXT: Isolation: unspecified // CHECK-NEXT: sil hidden @$s15specialize_self4cast{{[_0-9a-zA-Z]*}}F : $@convention(thin) (@in_guaranteed T) -> @out Optional func cast(_ x: T) -> R? { return x as? R } // CHECK-LABEL: static specialize_self.Base.returnIfSelf(Swift.AnyObject) -> Swift.Optional +// CHECK-NEXT: Isolation: unspecified // CHECK-NEXT: sil hidden @$s15specialize_self4BaseC12returnIfSelf{{[_0-9a-zA-Z]*}}FZ : $@convention(method) (@guaranteed AnyObject, @thick Base.Type) -> @owned Optional // CHECK: [[CAST:%[0-9]+]] = function_ref @$s15specialize_self4cast{{[_0-9a-zA-Z]*}}F // CHECK: apply [[CAST]] diff --git a/test/Serialization/nested-protocols.swift b/test/Serialization/nested-protocols.swift index cd3173332bf..204756c96d7 100644 --- a/test/Serialization/nested-protocols.swift +++ b/test/Serialization/nested-protocols.swift @@ -4,9 +4,11 @@ import nestedprotocolsource // CHECK: usesNested(_:) +// CHECK-NEXT: Isolation: unspecified // CHECK-NEXT: sil @$s4main10usesNestedyyx20nestedprotocolsource5OuterV5InnerRzlF : public func usesNested(_ x: some Outer.Inner) {} // CHECK: usesNestedInExtension(_:) +// CHECK-NEXT: Isolation: unspecified // CHECK-NEXT: sil @$s4main21usesNestedInExtensionyyx20nestedprotocolsource5OuterV05InnerdE0RzlF : -public func usesNestedInExtension(_ x: some Outer.InnerInExtension) {} \ No newline at end of file +public func usesNestedInExtension(_ x: some Outer.InnerInExtension) {} From 3843899c1908cc98be1eb45955f5ad2bbf1d0100 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Wed, 11 Sep 2024 10:33:35 -0700 Subject: [PATCH 2/3] [concurrency] Behind the flag UnspecifiedMeansMainActorIsolated, try inferring by default main actor isolation instead of nonisolated for unspecified. Just to play with. --- include/swift/AST/ActorIsolation.h | 2 + include/swift/Basic/Features.def | 3 + lib/AST/ActorIsolation.cpp | 21 ++ lib/AST/CMakeLists.txt | 1 + lib/AST/FeatureSet.cpp | 1 + lib/Sema/TypeCheckConcurrency.cpp | 35 +++ test/Concurrency/assume_mainactor.swift | 199 ++++++++++++++++++ .../assume_mainactor_typechecker_errors.swift | 71 +++++++ 8 files changed, 333 insertions(+) create mode 100644 lib/AST/ActorIsolation.cpp create mode 100644 test/Concurrency/assume_mainactor.swift create mode 100644 test/Concurrency/assume_mainactor_typechecker_errors.swift diff --git a/include/swift/AST/ActorIsolation.h b/include/swift/AST/ActorIsolation.h index b0ab364d4b9..ae64e65416b 100644 --- a/include/swift/AST/ActorIsolation.h +++ b/include/swift/AST/ActorIsolation.h @@ -133,6 +133,8 @@ public: return ActorIsolation(GlobalActor, globalActor); } + static ActorIsolation forMainActor(ASTContext &ctx); + static ActorIsolation forErased() { return ActorIsolation(Erased); } diff --git a/include/swift/Basic/Features.def b/include/swift/Basic/Features.def index 5a12114a5cf..d031fdcf0bf 100644 --- a/include/swift/Basic/Features.def +++ b/include/swift/Basic/Features.def @@ -411,6 +411,9 @@ EXPERIMENTAL_FEATURE(WarnUnsafe, true) // Enable values in generic signatures, e.g. EXPERIMENTAL_FEATURE(ValueGenerics, true) +// When a parameter has unspecified isolation, infer it as main actor isolated. +EXPERIMENTAL_FEATURE(UnspecifiedMeansMainActorIsolated, false) + #undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE #undef EXPERIMENTAL_FEATURE #undef UPCOMING_FEATURE diff --git a/lib/AST/ActorIsolation.cpp b/lib/AST/ActorIsolation.cpp new file mode 100644 index 00000000000..ad73caf6f60 --- /dev/null +++ b/lib/AST/ActorIsolation.cpp @@ -0,0 +1,21 @@ +//===--- ActorIsolation.cpp -----------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/AST/ActorIsolation.h" +#include "swift/AST/ASTContext.h" + +using namespace swift; + +ActorIsolation ActorIsolation::forMainActor(ASTContext &ctx) { + return ActorIsolation::forGlobalActor( + ctx.getMainActorType()->mapTypeOutOfContext()); +} diff --git a/lib/AST/CMakeLists.txt b/lib/AST/CMakeLists.txt index e90406e8095..bc8e5f03d1f 100644 --- a/lib/AST/CMakeLists.txt +++ b/lib/AST/CMakeLists.txt @@ -9,6 +9,7 @@ add_swift_host_library(swiftAST STATIC AbstractSourceFileDepGraphFactory.cpp AccessNotes.cpp AccessRequests.cpp + ActorIsolation.cpp ArgumentList.cpp ASTBridging.cpp ASTContext.cpp diff --git a/lib/AST/FeatureSet.cpp b/lib/AST/FeatureSet.cpp index a5f2e2ecf79..9758975cfa6 100644 --- a/lib/AST/FeatureSet.cpp +++ b/lib/AST/FeatureSet.cpp @@ -136,6 +136,7 @@ UNINTERESTING_FEATURE(ExtractConstantsFromMembers) UNINTERESTING_FEATURE(FixedArrays) UNINTERESTING_FEATURE(GroupActorErrors) UNINTERESTING_FEATURE(SameElementRequirements) +UNINTERESTING_FEATURE(UnspecifiedMeansMainActorIsolated) static bool usesFeatureSendingArgsAndResults(Decl *decl) { auto isFunctionTypeWithSending = [](Type type) { diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 4c0821ec3a3..7633dd62969 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -5489,6 +5489,12 @@ InferredActorIsolation ActorIsolationRequest::evaluate( ActorIsolation defaultIsolation = ActorIsolation::forUnspecified(); IsolationSource defaultIsolationSource; + // If we are supposed to infer main actor isolation by default, make our + // default isolation main actor. + if (ctx.LangOpts.hasFeature(Feature::UnspecifiedMeansMainActorIsolated)) { + defaultIsolation = ActorIsolation::forMainActor(ctx); + } + if (auto func = dyn_cast(value)) { // A @Sendable function is assumed to be actor-independent. if (func->isSendable()) { @@ -5514,6 +5520,9 @@ InferredActorIsolation ActorIsolationRequest::evaluate( overriddenIso = defaultIsolation; } + // NOTE: After this point, the default has been set. Only touch the default + // isolation above this point since code below assumes it is now constant. + // Function used when returning an inferred isolation. auto inferredIsolation = [&](ActorIsolation inferred, bool onlyGlobal = false) { @@ -5627,6 +5636,32 @@ InferredActorIsolation ActorIsolationRequest::evaluate( } } + // If this is an actor, use the actor isolation of the actor. + if (ctx.LangOpts.hasFeature(Feature::UnspecifiedMeansMainActorIsolated)) { + // non-async inits and deinits need to be always nonisolated since we can + // run the deinit anywhere. + // + // TODO: We should add a check for if they are marked with global actor + // isolation. + if (auto *func = dyn_cast(value)) { + if (isa(func) && !func->isAsyncContext()) + return {ActorIsolation::forNonisolated(false /*unsafe*/), + IsolationSource(func, IsolationSource::LexicalContext)}; + + if (isa(func) && !func->isAsyncContext()) + return {ActorIsolation::forNonisolated(false /*unsafe*/), + IsolationSource(func, IsolationSource::LexicalContext)}; + } + + if (auto nominal = dyn_cast(value)) { + if (nominal->isActor() && !nominal->isGlobalActor()) { + auto isolation = ActorIsolation::forActorInstanceSelf(value); + return {inferredIsolation(isolation), + IsolationSource(nominal, IsolationSource::LexicalContext)}; + } + } + } + // If this is an accessor, use the actor isolation of its storage // declaration. if (auto accessor = dyn_cast(value)) { diff --git a/test/Concurrency/assume_mainactor.swift b/test/Concurrency/assume_mainactor.swift new file mode 100644 index 00000000000..17672121178 --- /dev/null +++ b/test/Concurrency/assume_mainactor.swift @@ -0,0 +1,199 @@ +// RUN: %target-swift-frontend -swift-version 6 -emit-silgen -enable-experimental-feature UnspecifiedMeansMainActorIsolated %s | %FileCheck %s +// RUN: %target-swift-frontend -swift-version 6 -emit-sil -enable-experimental-feature UnspecifiedMeansMainActorIsolated %s -verify + +// READ THIS! This test is meant to FileCheck the specific isolation when +// UnspecifiedMeansMainActorIsolated is enabled. Please do not put other types +// of tests in here. + +class Klass { + // Implicit deinit + // CHECK: // Klass.deinit + // CHECK-NEXT: // Isolation: nonisolated + // CHECK-NEXT: sil hidden [ossa] @$s16assume_mainactor5KlassCfd : $@convention(method) (@guaranteed Klass) -> @owned Builtin.NativeObject { + + // Implicit deallocating deinit + // CHECK: // Klass.__deallocating_deinit + // CHECK-NEXT: // Isolation: nonisolated + // CHECK-NEXT: sil hidden [ossa] @$s16assume_mainactor5KlassCfD : $@convention(method) (@owned Klass) -> () { + + // Implicit allocating init + // CHECK: // Klass.__allocating_init() + // CHECK-NEXT: // Isolation: nonisolated + // CHECK-NEXT: sil hidden [exact_self_class] [ossa] @$s16assume_mainactor5KlassCACycfC : $@convention(method) (@thick Klass.Type) -> @owned Klass { + + // Implicit designated init + // CHECK: // Klass.init() + // CHECK-NEXT: // Isolation: nonisolated + // CHECK-NEXT: sil hidden [ossa] @$s16assume_mainactor5KlassCACycfc : $@convention(method) (@owned Klass) -> @owned Klass { +} + +struct StructContainingKlass { + // CHECK: // variable initialization expression of StructContainingKlass.k + // CHECK-NEXT: // Isolation: global_actor. type: MainActor + // CHECK-NEXT: sil hidden [transparent] [ossa] @$s16assume_mainactor21StructContainingKlassV1kAA0E0Cvpfi : $@convention(thin) () -> @owned Klass { + + // CHECK: // StructContainingKlass.k.getter + // CHECK-NEXT: // Isolation: global_actor. type: MainActor + // CHECK-NEXT: sil hidden [transparent] [ossa] @$s16assume_mainactor21StructContainingKlassV1kAA0E0Cvg : $@convention(method) (@guaranteed StructContainingKlass) -> @owned Klass { + + // CHECK: // StructContainingKlass.k.setter + // CHECK-NEXT: // Isolation: global_actor. type: MainActor + // CHECK-NEXT: sil hidden [transparent] [ossa] @$s16assume_mainactor21StructContainingKlassV1kAA0E0Cvs : $@convention(method) (@owned Klass, @inout StructContainingKlass) -> () { + var k = Klass() + + // CHECK: // StructContainingKlass.init() + // CHECK-NEXT: // Isolation: nonisolated + // CHECK-NEXT: sil hidden [ossa] @$s16assume_mainactor21StructContainingKlassVACycfC : $@convention(method) (@thin StructContainingKlass.Type) -> @owned StructContainingKlass { +} + +// TODO: Allow for nonisolated to be applied to structs. +struct NonIsolatedStructContainingKlass { + // CHECK: // variable initialization expression of NonIsolatedStructContainingKlass.k + // CHECK-NEXT: // Isolation: global_actor. type: MainActor + // CHECK-NEXT: sil hidden [transparent] [ossa] @$s16assume_mainactor32NonIsolatedStructContainingKlassV1kAA0G0Cvpfi : $@convention(thin) () -> @owned Klass { + + // CHECK: // NonIsolatedStructContainingKlass.k.getter + // CHECK-NEXT: // Isolation: global_actor. type: MainActor + // CHECK-NEXT: sil hidden [transparent] [ossa] @$s16assume_mainactor32NonIsolatedStructContainingKlassV1kAA0G0Cvg : $@convention(method) (@guaranteed NonIsolatedStructContainingKlass) -> @owned Klass { + + // CHECK: // NonIsolatedStructContainingKlass.k.setter + // CHECK-NEXT: // Isolation: global_actor. type: MainActor + // CHECK-NEXT: sil hidden [transparent] [ossa] @$s16assume_mainactor32NonIsolatedStructContainingKlassV1kAA0G0Cvs : $@convention(method) (@owned Klass, @inout NonIsolatedStructContainingKlass) -> () { + var k = Klass() + + // CHECK: // NonIsolatedStructContainingKlass.init() + // CHECK-NEXT: // Isolation: nonisolated + // CHECK-NEXT: sil hidden [ossa] @$s16assume_mainactor32NonIsolatedStructContainingKlassVACycfC : $@convention(method) (@thin NonIsolatedStructContainingKlass.Type) -> @owned NonIsolatedStructContainingKlass { +} + +@globalActor +actor CustomActor { + static let shared = CustomActor() +} + +// CHECK: // unspecifiedAsync(_:) +// CHECK-NEXT: // Isolation: global_actor. type: MainActor +// CHECK-NEXT: sil hidden [ossa] @$s16assume_mainactor16unspecifiedAsyncyyxYalF : $@convention(thin) @async (@in_guaranteed T) -> () { +func unspecifiedAsync(_ t: T) async {} + +// CHECK: // nonisolatedAsync(_:) +// CHECK-NEXT: // Isolation: nonisolated +// CHECK-NEXT: sil hidden [ossa] @$s16assume_mainactor16nonisolatedAsyncyyxYalF : $@convention(thin) @async (@in_guaranteed T) -> () { +nonisolated func nonisolatedAsync(_ t: T) async {} + +// CHECK: // mainActorAsync(_:) +// CHECK-NEXT: // Isolation: global_actor. type: MainActor +// CHECK-NEXT: sil hidden [ossa] @$s16assume_mainactor14mainActorAsyncyyxYalF : $@convention(thin) @async (@in_guaranteed T) -> () { +@MainActor func mainActorAsync(_ t: T) async {} + +// CHECK: // customActorAsync(_:) +// CHECK-NEXT: // Isolation: global_actor. type: CustomActor +// CHECK-NEXT: sil hidden [ossa] @$s16assume_mainactor16customActorAsyncyyxYalF : $@convention(thin) @async (@in_guaranteed T) -> () { +@CustomActor func customActorAsync(_ t: T) async {} + + +@CustomActor +struct CustomActorStruct { + // Variable expression is custom actor... but since we have a nonisolated + // init, we should be fine? + // + // CHECK: // variable initialization expression of CustomActorStruct.k + // CHECK-NEXT: // Isolation: global_actor. type: CustomActor + // CHECK-NEXT: sil hidden [transparent] [ossa] @$s16assume_mainactor17CustomActorStructV1kAA5KlassCvpfi : $@convention(thin) () -> @owned Klass { + + // CHECK: // CustomActorStruct.k.getter + // CHECK-NEXT: // Isolation: global_actor. type: CustomActor + // CHECK-NEXT: sil hidden [transparent] [ossa] @$s16assume_mainactor17CustomActorStructV1kAA5KlassCvg : $@convention(method) (@guaranteed CustomActorStruct) -> @owned Klass { + + // CHECK: // CustomActorStruct.k.setter + // CHECK-NEXT: // Isolation: global_actor. type: CustomActor + // CHECK-NEXT: sil hidden [transparent] [ossa] @$s16assume_mainactor17CustomActorStructV1kAA5KlassCvs : $@convention(method) (@owned Klass, @inout CustomActorStruct) -> () { + var k = Klass() +} + +// CHECK: // unspecifiedFunctionTest() +// CHECK-NEXT: // Isolation: global_actor. type: MainActor +// CHECK-NEXT: sil hidden [ossa] @$s16assume_mainactor23unspecifiedFunctionTestyyYaF : $@convention(thin) @async () -> () { +func unspecifiedFunctionTest() async { + let k = Klass() + await unspecifiedAsync(k) + await nonisolatedAsync(k) + await mainActorAsync(k) +} + +// CHECK: // unspecifiedFunctionTest2() +// CHECK-NEXT: // Isolation: global_actor. type: MainActor +// CHECK-NEXT: sil hidden [ossa] @$s16assume_mainactor24unspecifiedFunctionTest2yyYaF : $@convention(thin) @async () -> () { +func unspecifiedFunctionTest2() async { + let k = StructContainingKlass() + await unspecifiedAsync(k) + await nonisolatedAsync(k) + await mainActorAsync(k) + + await unspecifiedAsync(k.k) + await nonisolatedAsync(k.k) + await mainActorAsync(k.k) +} + +// CHECK: // unspecifiedFunctionTest3() +// CHECK-NEXT: // Isolation: global_actor. type: MainActor +// CHECK-NEXT: sil hidden [ossa] @$s16assume_mainactor24unspecifiedFunctionTest3yyYaF : $@convention(thin) @async () -> () { +func unspecifiedFunctionTest3() async { + let k = NonIsolatedStructContainingKlass() + await unspecifiedAsync(k) + await nonisolatedAsync(k) + await mainActorAsync(k) + + await unspecifiedAsync(k.k) + await nonisolatedAsync(k.k) + await mainActorAsync(k.k) +} + +// CHECK: // nonisolatedFunctionTest() +// CHECK-NEXT: // Isolation: nonisolated +// CHECK-NEXT: sil hidden [ossa] @$s16assume_mainactor23nonisolatedFunctionTestyyYaF : $@convention(thin) @async () -> () { +nonisolated func nonisolatedFunctionTest() async { + let k = NonIsolatedStructContainingKlass() + await unspecifiedAsync(k.k) + await nonisolatedAsync(k.k) + await mainActorAsync(k.k) +} + +actor MyActor { + // CHECK: // variable initialization expression of MyActor.k + // CHECK-NEXT: // Isolation: actor_instance + // CHECK-NEXT: sil hidden [transparent] [ossa] @$s16assume_mainactor7MyActorC1kAA5KlassCvpfi : $@convention(thin) () -> @owned Klass { + + // CHECK: // MyActor.k.getter + // CHECK-NEXT: // Isolation: actor_instance. name: 'self' + // CHECK-NEXT: sil hidden [transparent] [ossa] @$s16assume_mainactor7MyActorC1kAA5KlassCvg : $@convention(method) (@sil_isolated @guaranteed MyActor) -> @owned Klass { + + // CHECK: // MyActor.k.setter + // CHECK-NEXT: // Isolation: actor_instance. name: 'self' + // CHECK-NEXT: sil hidden [transparent] [ossa] @$s16assume_mainactor7MyActorC1kAA5KlassCvs : $@convention(method) (@owned Klass, @sil_isolated @guaranteed MyActor) -> () { + var k = Klass() + + // Implicit deinit + // CHECK: // MyActor.deinit + // CHECK-NEXT: // Isolation: nonisolated + // CHECK-NEXT: sil hidden [ossa] @$s16assume_mainactor7MyActorCfd : $@convention(method) (@guaranteed MyActor) -> @owned Builtin.NativeObject { + + // Non-async init should be nonisolated + // CHECK: // MyActor.init() + // CHECK-NEXT: // Isolation: nonisolated + // CHECK-NEXT: sil hidden [ossa] @$s16assume_mainactor7MyActorCACycfc : $@convention(method) (@owned MyActor) -> @owned MyActor { +} + +actor MyActor2 { + // CHECK: // MyActor2.init() + // CHECK-NEXT: // Isolation: global_actor. type: MainActor + // CHECK-NEXT: sil hidden [ossa] @$s16assume_mainactor8MyActor2CACycfc : $@convention(method) (@owned MyActor2) -> @owned MyActor2 { + @MainActor + init() {} + + // CHECK: // MyActor2.init(x:) + // CHECK-NEXT: // Isolation: global_actor. type: CustomActor + // CHECK-NEXT: sil hidden [ossa] @$s16assume_mainactor8MyActor2C1xACyt_tcfc : $@convention(method) (@owned MyActor2) -> @owned MyActor2 { + @CustomActor + init(x: ()) {} +} diff --git a/test/Concurrency/assume_mainactor_typechecker_errors.swift b/test/Concurrency/assume_mainactor_typechecker_errors.swift new file mode 100644 index 00000000000..26739e6b9c3 --- /dev/null +++ b/test/Concurrency/assume_mainactor_typechecker_errors.swift @@ -0,0 +1,71 @@ +// RUN: %target-swift-frontend -swift-version 6 -emit-sil -enable-experimental-feature UnspecifiedMeansMainActorIsolated %s -verify + +// READ THIS! This test is meant to check the specific isolation when +// UnspecifiedMeansMainActorIsolated is enabled in combination with validating +// behavior around explicitly non-Sendable types that trigger type checker +// specific errors. Please do not put other types of tests in here. + +// Fake Sendable Data +class SendableData : @unchecked Sendable {} + +nonisolated func getDataFromSocket() -> SendableData { SendableData() } + +class Klass { // expected-note 3 {{}} + let s = SendableData() + + init() { s = SendableData() } + init(_ s: SendableData) {} + + func doSomething() {} +} + +@available(*, unavailable) +extension Klass : Sendable {} + +struct StructContainingKlass { + var k = Klass() +} + +func unspecifiedAsync(_ t: T) async {} +nonisolated func nonisolatedAsync(_ t: T) async {} +@MainActor func mainActorAsync(_ t: T) async {} + +func unspecifiedFunctionTest() async { + let k = Klass() + await unspecifiedAsync(k) + await nonisolatedAsync(k) + await mainActorAsync(k) +} + +func unspecifiedFunctionTest2() async { + let k = StructContainingKlass() + await unspecifiedAsync(k) + await nonisolatedAsync(k) + await mainActorAsync(k) + + await unspecifiedAsync(k.k) + await nonisolatedAsync(k.k) + await mainActorAsync(k.k) +} + +nonisolated func nonisolatedFunctionTest() async { + let k = StructContainingKlass() + await unspecifiedAsync(k.k) // expected-error {{non-sendable type 'Klass' of property 'k' cannot exit main actor-isolated context}} + await nonisolatedAsync(k.k) // expected-error {{non-sendable type 'Klass' of property 'k' cannot exit main actor-isolated context}} + await mainActorAsync(k.k) // expected-error {{non-sendable type 'Klass' of property 'k' cannot exit main actor-isolated context}} +} + +func testTask() async { + Task { + let k = Klass(getDataFromSocket()) + k.doSomething() + } +} + +func testTaskDetached() async { + Task.detached { + let k = Klass(getDataFromSocket()) + // Have to pop back onto the main thread to do something. + await k.doSomething() + } +} From 929c0ca7d8287f9ea88bbdde0e73972b7abc3de7 Mon Sep 17 00:00:00 2001 From: Gabor Horvath Date: Thu, 12 Sep 2024 14:01:13 +0100 Subject: [PATCH 3/3] [cxx-interop] Introduce a safe C++ interop mode In this mode all C++ types are imported as unsafe by default. Users explicitly marking types are escapable or not escapable can make them imported as safe. In the future, we also want to import unannotated functions as unsafe and add more logic to infer types that are actually safe, like agregates of escapable types. --- include/swift/Basic/Features.def | 3 ++ lib/AST/FeatureSet.cpp | 1 + lib/ClangImporter/ClangImporter.cpp | 4 ++ lib/ClangImporter/ImportDecl.cpp | 27 +++++++++++- lib/ClangImporter/ImporterImpl.h | 2 + .../SwiftBridging/swift/bridging | 8 ++++ .../Interop/Cxx/class/safe-interop-mode.swift | 43 +++++++++++++++++++ 7 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 test/Interop/Cxx/class/safe-interop-mode.swift diff --git a/include/swift/Basic/Features.def b/include/swift/Basic/Features.def index 16cd362e583..d48992c3bcf 100644 --- a/include/swift/Basic/Features.def +++ b/include/swift/Basic/Features.def @@ -408,6 +408,9 @@ SUPPRESSIBLE_EXPERIMENTAL_FEATURE(AllowUnsafeAttribute, true) /// Warn on use of unsafe constructs. EXPERIMENTAL_FEATURE(WarnUnsafe, true) +/// Import unsafe C and C++ constructs as @unsafe. +EXPERIMENTAL_FEATURE(SafeInterop, true) + // Enable values in generic signatures, e.g. EXPERIMENTAL_FEATURE(ValueGenerics, true) diff --git a/lib/AST/FeatureSet.cpp b/lib/AST/FeatureSet.cpp index 7857c8a014b..8aa8457a7cb 100644 --- a/lib/AST/FeatureSet.cpp +++ b/lib/AST/FeatureSet.cpp @@ -300,6 +300,7 @@ static bool usesFeatureAllowUnsafeAttribute(Decl *decl) { } UNINTERESTING_FEATURE(WarnUnsafe) +UNINTERESTING_FEATURE(SafeInterop) static bool usesFeatureValueGenerics(Decl *decl) { auto genericContext = decl->getAsGenericContext(); diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 529521bb559..c15c08b3a34 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -7391,6 +7391,10 @@ bool importer::hasNonEscapableAttr(const clang::RecordDecl *decl) { return hasSwiftAttribute(decl, "~Escapable"); } +bool importer::hasEscapableAttr(const clang::RecordDecl *decl) { + return hasSwiftAttribute(decl, "Escapable"); +} + /// Recursively checks that there are no pointers in any fields or base classes. /// Does not check C++ records with specific API annotations. static bool hasPointerInSubobjects(const clang::CXXRecordDecl *decl) { diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index abecfd20722..ae5d544f312 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -37,6 +37,7 @@ #include "swift/AST/PrettyStackTrace.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/Stmt.h" +#include "swift/AST/Type.h" #include "swift/AST/TypeCheckRequests.h" #include "swift/AST/Types.h" #include "swift/Basic/Assertions.h" @@ -8154,6 +8155,21 @@ bool swift::importer::isMutabilityAttr(const clang::SwiftAttrAttr *swiftAttr) { swiftAttr->getAttribute() == "nonmutating"; } +static bool importAsUnsafe(const ASTContext &context, + const clang::RecordDecl *decl, + const Decl *MappedDecl) { + if (!context.LangOpts.hasFeature(Feature::SafeInterop) || + !context.LangOpts.hasFeature(Feature::AllowUnsafeAttribute) || !decl) + return false; + + if (isa(MappedDecl)) + return false; + + // TODO: Add logic to cover structural rules. + return !importer::hasNonEscapableAttr(decl) && + !importer::hasEscapableAttr(decl); +} + void ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) { auto ClangDecl = @@ -8178,6 +8194,7 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) { // // __attribute__((swift_attr("attribute"))) // + bool seenUnsafe = false; for (auto swiftAttr : ClangDecl->specific_attrs()) { // FIXME: Hard-code @MainActor and @UIActor, because we don't have a // point at which to do name lookup for imported entities. @@ -8287,8 +8304,7 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) { if (swiftAttr->getAttribute() == "unsafe") { if (!SwiftContext.LangOpts.hasFeature(Feature::AllowUnsafeAttribute)) continue; - auto attr = new (SwiftContext) UnsafeAttr(/*implicit=*/false); - MappedDecl->getAttrs().add(attr); + seenUnsafe = true; continue; } @@ -8332,6 +8348,13 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) { swiftAttr->getAttribute()); } } + + if (seenUnsafe || + importAsUnsafe(SwiftContext, dyn_cast(ClangDecl), + MappedDecl)) { + auto attr = new (SwiftContext) UnsafeAttr(/*implicit=*/!seenUnsafe); + MappedDecl->getAttrs().add(attr); + } }; importAttrsFromDecl(ClangDecl); diff --git a/lib/ClangImporter/ImporterImpl.h b/lib/ClangImporter/ImporterImpl.h index d22aab99eed..18939159e27 100644 --- a/lib/ClangImporter/ImporterImpl.h +++ b/lib/ClangImporter/ImporterImpl.h @@ -2031,6 +2031,8 @@ bool hasUnsafeAPIAttr(const clang::Decl *decl); bool hasNonEscapableAttr(const clang::RecordDecl *decl); +bool hasEscapableAttr(const clang::RecordDecl *decl); + bool isViewType(const clang::CXXRecordDecl *decl); } // end namespace importer diff --git a/lib/ClangImporter/SwiftBridging/swift/bridging b/lib/ClangImporter/SwiftBridging/swift/bridging index 8530321a798..703f1e305d1 100644 --- a/lib/ClangImporter/SwiftBridging/swift/bridging +++ b/lib/ClangImporter/SwiftBridging/swift/bridging @@ -164,6 +164,13 @@ #define SWIFT_NONESCAPABLE \ __attribute__((swift_attr("~Escapable"))) +/// Specifies that a specific c++ type such class or struct should be imported +/// as a escapable Swift value. While this matches the default behavior, +/// in safe mode interop mode it ensures that the type is not marked as +/// unsafe. +#define SWIFT_ESCAPABLE \ + __attribute__((swift_attr("Escapable"))) + /// Specifies that the return value is passed as owned for C++ functions and /// methods returning types annotated as `SWIFT_SHARED_REFERENCE` #define SWIFT_RETURNS_RETAINED __attribute__((swift_attr("returns_retained"))) @@ -187,6 +194,7 @@ #define SWIFT_UNCHECKED_SENDABLE #define SWIFT_NONCOPYABLE #define SWIFT_NONESCAPABLE +#define SWIFT_ESCAPABLE #define SWIFT_RETURNS_RETAINED #define SWIFT_RETURNS_UNRETAINED diff --git a/test/Interop/Cxx/class/safe-interop-mode.swift b/test/Interop/Cxx/class/safe-interop-mode.swift new file mode 100644 index 00000000000..8949037b690 --- /dev/null +++ b/test/Interop/Cxx/class/safe-interop-mode.swift @@ -0,0 +1,43 @@ + +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: %target-swift-frontend -typecheck -verify -I %swift_src_root/lib/ClangImporter/SwiftBridging -I %t/Inputs %t/test.swift -enable-experimental-feature AllowUnsafeAttribute -enable-experimental-feature WarnUnsafe -enable-experimental-feature NonescapableTypes -enable-experimental-feature SafeInterop -cxx-interoperability-mode=default -diagnostic-style llvm 2>&1 + +// REQUIRES: objc_interop + +//--- Inputs/module.modulemap +module Test { + header "nonescapable.h" + requires cplusplus +} + +//--- Inputs/nonescapable.h +#include "swift/bridging" + +struct SWIFT_NONESCAPABLE View { + __attribute__((swift_attr("@lifetime(immortal)"))) + View() : member(nullptr) {} + __attribute__((swift_attr("@lifetime(p)"))) + View(const int *p [[clang::lifetimebound]]) : member(p) {} + View(const View&) = default; +private: + const int *member; +}; + +struct SWIFT_ESCAPABLE Owner {}; + +struct Unannotated {}; + +//--- test.swift + +import Test +import CoreFoundation + +func useUnsafeParam(x: Unannotated) { // expected-warning{{reference to unsafe struct 'Unannotated'}} +} + +func useSafeParams(x: Owner, y: View) { +} + +func useCfType(x: CFArray) { +}