mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[SE-0470] Prohibit inference of isolated conformances with nonisolated witnesses
If all of the witnesses to a conformance are nonisolated, then infer that conformance as nonisolated rather than global-actor-isolated. This is only relevant when InferIsolatedConformances is enabled, and prevents that inference to help maintain source compatibility.
This commit is contained in:
@@ -7991,17 +7991,51 @@ ConformanceIsolationRequest::evaluate(Evaluator &evaluator, ProtocolConformance
|
||||
if (getActorIsolation(rootNormal->getProtocol()).isActorIsolated())
|
||||
return ActorIsolation::forNonisolated(false);
|
||||
|
||||
// If we are inferring isolated conformances and the conforming type is
|
||||
// isolated to a global actor, use the conforming type's isolation.
|
||||
// Isolation inference rules follow. If we aren't inferring isolated conformances,
|
||||
// we're done.
|
||||
if (!ctx.LangOpts.hasFeature(Feature::InferIsolatedConformances))
|
||||
return ActorIsolation::forNonisolated(false);
|
||||
|
||||
auto nominal = dc->getSelfNominalTypeDecl();
|
||||
if (ctx.LangOpts.hasFeature(Feature::InferIsolatedConformances) &&
|
||||
nominal) {
|
||||
auto nominalIsolation = getActorIsolation(nominal);
|
||||
if (nominalIsolation.isGlobalActor())
|
||||
return nominalIsolation;
|
||||
if (!nominal) {
|
||||
return ActorIsolation::forNonisolated(false);
|
||||
}
|
||||
|
||||
return ActorIsolation::forNonisolated(false);
|
||||
// If we are inferring isolated conformances and the conforming type is
|
||||
// isolated to a global actor, we may use the conforming type's isolation.
|
||||
auto nominalIsolation = getActorIsolation(nominal);
|
||||
if (!nominalIsolation.isGlobalActor()) {
|
||||
return ActorIsolation::forNonisolated(false);
|
||||
}
|
||||
|
||||
// If all of the value witnesses are nonisolated, then we should not infer
|
||||
// global actor isolation.
|
||||
bool anyIsolatedWitness = false;
|
||||
auto protocol = conformance->getProtocol();
|
||||
for (auto requirement : protocol->getMembers()) {
|
||||
if (isa<TypeDecl>(requirement))
|
||||
continue;
|
||||
|
||||
auto valueReq = dyn_cast<ValueDecl>(requirement);
|
||||
if (!valueReq)
|
||||
continue;
|
||||
|
||||
auto witness = conformance->getWitnessDecl(valueReq);
|
||||
if (!witness)
|
||||
continue;
|
||||
|
||||
auto witnessIsolation = getActorIsolation(witness);
|
||||
if (witnessIsolation.isActorIsolated()) {
|
||||
anyIsolatedWitness = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!anyIsolatedWitness) {
|
||||
return ActorIsolation::forNonisolated(false);
|
||||
}
|
||||
|
||||
return nominalIsolation;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -46,8 +46,8 @@ func acceptSendablePMeta<T: Sendable & P>(_: T.Type) { }
|
||||
func acceptSendableQMeta<T: Sendable & Q>(_: T.Type) { }
|
||||
|
||||
nonisolated func testConformancesFromNonisolated() {
|
||||
let _: any P = CExplicitMainActor() // expected-error{{main actor-isolated conformance of 'CExplicitMainActor' to 'P' cannot be used in nonisolated context}}
|
||||
let _: any P = CImplicitMainActor() // expected-error{{main actor-isolated conformance of 'CImplicitMainActor' to 'P' cannot be used in nonisolated context}}
|
||||
let _: any P = CExplicitMainActor() // okay
|
||||
let _: any P = CImplicitMainActor() // okay
|
||||
|
||||
let _: any P = CNonIsolated()
|
||||
let _: any P = CImplicitMainActorNonisolatedConformance()
|
||||
|
||||
@@ -32,6 +32,11 @@ extension CExplicit: Q {
|
||||
func g() { }
|
||||
}
|
||||
|
||||
@SomeGlobalActor
|
||||
class CViaNonisolatedWitness: P {
|
||||
nonisolated func f() { } // okay! conformance above is nonisolated via this witness
|
||||
}
|
||||
|
||||
// expected-error@+3{{conformance of 'CNonIsolated' to protocol 'P' crosses into global actor 'SomeGlobalActor'-isolated code and can cause data races}}
|
||||
// expected-note@+2{{turn data races into runtime errors with '@preconcurrency'}}
|
||||
// expected-note@+1{{isolate this conformance to the global actor 'SomeGlobalActor' with '@SomeGlobalActor'}}{{33-33=@SomeGlobalActor }}
|
||||
@@ -49,4 +54,6 @@ nonisolated func testConformancesFromNonisolated() {
|
||||
|
||||
// Okay, these are nonisolated conformances.
|
||||
let _: any Q = CExplicit()
|
||||
|
||||
let _: any P = CViaNonisolatedWitness()
|
||||
}
|
||||
|
||||
@@ -2881,6 +2881,58 @@ public struct HangingMacro: PeerMacro {
|
||||
}
|
||||
}
|
||||
|
||||
public struct PWithNonisolatedFuncMacro: ExtensionMacro {
|
||||
public static var inferNonisolatedConformances: Bool { false }
|
||||
|
||||
public static func expansion(
|
||||
of node: AttributeSyntax,
|
||||
attachedTo decl: some DeclGroupSyntax,
|
||||
providingExtensionsOf type: some TypeSyntaxProtocol,
|
||||
conformingTo protocols: [TypeSyntax],
|
||||
in context: some MacroExpansionContext
|
||||
) throws -> [ExtensionDeclSyntax] {
|
||||
if (protocols.isEmpty) {
|
||||
return []
|
||||
}
|
||||
|
||||
let decl: DeclSyntax =
|
||||
"""
|
||||
extension \(raw: type.trimmedDescription): P {
|
||||
nonisolated static func requirement() { }
|
||||
}
|
||||
"""
|
||||
|
||||
return [
|
||||
decl.cast(ExtensionDeclSyntax.self)
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
public struct NonisolatedPWithNonisolatedFuncMacro: ExtensionMacro {
|
||||
public static func expansion(
|
||||
of node: AttributeSyntax,
|
||||
attachedTo decl: some DeclGroupSyntax,
|
||||
providingExtensionsOf type: some TypeSyntaxProtocol,
|
||||
conformingTo protocols: [TypeSyntax],
|
||||
in context: some MacroExpansionContext
|
||||
) throws -> [ExtensionDeclSyntax] {
|
||||
if (protocols.isEmpty) {
|
||||
return []
|
||||
}
|
||||
|
||||
let decl: DeclSyntax =
|
||||
"""
|
||||
extension \(raw: type.trimmedDescription): P {
|
||||
nonisolated static func requirement() { }
|
||||
}
|
||||
"""
|
||||
|
||||
return [
|
||||
decl.cast(ExtensionDeclSyntax.self)
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
public struct BigEndianAccessorMacro: AccessorMacro {
|
||||
public static func expansion(
|
||||
of node: AttributeSyntax,
|
||||
|
||||
@@ -283,3 +283,15 @@ struct HasNestedType {
|
||||
// extensions of nested types when the outer type has an
|
||||
// attached macro that can add other nested types.
|
||||
extension HasNestedType.Inner {}
|
||||
|
||||
@attached(extension, conformances: P, names: named(requirement))
|
||||
macro AddPWithNonisolated() = #externalMacro(module: "MacroDefinition", type: "PWithNonisolatedFuncMacro")
|
||||
|
||||
@attached(extension, conformances: P, names: named(requirement))
|
||||
macro AddNonisolatedPWithNonisolated() = #externalMacro(module: "MacroDefinition", type: "NonisolatedPWithNonisolatedFuncMacro")
|
||||
|
||||
@AddNonisolatedPWithNonisolated
|
||||
struct MakeMeNonisolated { }
|
||||
|
||||
@AddPWithNonisolated
|
||||
struct KeepMeIsolated { }
|
||||
|
||||
Reference in New Issue
Block a user