Prohibit isolated conformances for checked casts to potentially-SendableMetatype types

This removes the IsolatedConformances feature gate from SILGen for
dynamic casts, such that all dynamic casts that will not work with
isolated conformances are marked as such. Isolated conformances can
still only come into a program when part of it enables the feature
flag.
This commit is contained in:
Doug Gregor
2025-03-28 16:51:45 -07:00
parent 2f5c6d21bb
commit c08bd5668e
4 changed files with 77 additions and 26 deletions

View File

@@ -308,16 +308,17 @@ namespace {
if (!TargetType->isAnyExistentialType())
return CastingIsolatedConformances::Allow;
// Only do this if isolated conformances are enabled.
ASTContext &ctx = TargetType->getASTContext();
if (!ctx.LangOpts.hasFeature(Feature::IsolatedConformances))
return CastingIsolatedConformances::Allow;
auto proto = ctx.getProtocol(KnownProtocolKind::SendableMetatype);
// If there is a conformance to SendableMetatype, then this existential
// can leave the current isolation domain. Prohibit isolated conformances.
if (proto && lookupConformance(TargetType, proto, /*allowMissing=*/false))
ASTContext &ctx = TargetType->getASTContext();
Type checkType;
if (auto existentialMetatype = TargetType->getAs<ExistentialMetatypeType>())
checkType = existentialMetatype->getInstanceType();
else
checkType = TargetType;
auto proto = ctx.getProtocol(KnownProtocolKind::SendableMetatype);
if (proto && lookupConformance(checkType, proto, /*allowMissing=*/false))
return CastingIsolatedConformances::Prohibit;
return CastingIsolatedConformances::Allow;

View File

@@ -0,0 +1,53 @@
// RUN: %target-swift-frontend -typecheck -verify -target %target-swift-5.1-abi-triple -swift-version 6 -enable-experimental-feature IsolatedConformances -enable-experimental-feature InferIsolatedConformances %s
// REQUIRES: swift_feature_IsolatedConformances
// REQUIRES: swift_feature_InferIsolatedConformances
// REQUIRES: concurrency
protocol P {
func f()
}
@SomeGlobalActor
protocol Q {
func g()
}
@globalActor
actor SomeGlobalActor {
static let shared = SomeGlobalActor()
}
@SomeGlobalActor
protocol R {
func h()
}
@SomeGlobalActor
class CExplicit: P {
func f() { } // okay! conformance above is isolated
}
// If the protocol itself is isolated, don't do anything.
extension CExplicit: Q {
func g() { }
}
// 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 }}
nonisolated class CNonIsolated: P {
@SomeGlobalActor func f() { } // expected-note{{global actor 'SomeGlobalActor'-isolated instance method 'f()' cannot satisfy nonisolated requirement}}
}
func acceptSendablePMeta<T: Sendable & P>(_: T.Type) { }
func acceptSendableQMeta<T: Sendable & Q>(_: T.Type) { }
nonisolated func testConformancesFromNonisolated() {
let _: any P = CExplicit() // expected-error{{global actor 'SomeGlobalActor'-isolated conformance of 'CExplicit' to 'P' cannot be used in nonisolated context}}
let _: any P = CNonIsolated()
// Okay, these are nonisolated conformances.
let _: any Q = CExplicit()
}

View File

@@ -1,7 +1,4 @@
// RUN: %target-swift-frontend -target %target-swift-5.1-abi-triple -Xllvm -sil-print-types -emit-silgen -enable-experimental-feature IsolatedConformances -verify %s | %FileCheck %s
// REQUIRES: swift_feature_IsolatedConformances
// REQUIRES: concurrency
// RUN: %target-swift-frontend -target %target-swift-5.1-abi-triple -Xllvm -sil-print-types -emit-silgen -verify %s | %FileCheck %s
protocol P { }

View File

@@ -296,17 +296,17 @@ func downcasts(
let _ = baseAndP as! Derived
// CHECK: [[COPIED:%.*]] = copy_value [[ARG0]] : $any Base<Int> & P
// CHECK-NEXT: checked_cast_br any Base<Int> & P in [[COPIED]] : $any Base<Int> & P to any Derived & R
// CHECK-NEXT: checked_cast_br [prohibit_isolated_conformances] any Base<Int> & P in [[COPIED]] : $any Base<Int> & P to any Derived & R
let _ = baseAndP as? (Derived & R)
// CHECK: [[COPIED:%.*]] = copy_value [[ARG0]] : $any Base<Int> & P
// CHECK-NEXT: unconditional_checked_cast [[COPIED]] : $any Base<Int> & P to any Derived & R
// CHECK-NEXT: unconditional_checked_cast [prohibit_isolated_conformances] [[COPIED]] : $any Base<Int> & P to any Derived & R
let _ = baseAndP as! (Derived & R)
// CHECK: checked_cast_br Derived.Type in %3 : $@thick Derived.Type to any (Derived & R).Type
// CHECK: checked_cast_br [prohibit_isolated_conformances] Derived.Type in %3 : $@thick Derived.Type to any (Derived & R).Type
let _ = derivedType as? (Derived & R).Type
// CHECK: unconditional_checked_cast %3 : $@thick Derived.Type to any (Derived & R).Type
// CHECK: unconditional_checked_cast [prohibit_isolated_conformances] %3 : $@thick Derived.Type to any (Derived & R).Type
let _ = derivedType as! (Derived & R).Type
// CHECK: checked_cast_br any (Base<Int> & P).Type in %2 : $@thick any (Base<Int> & P).Type to Derived.Type
@@ -315,10 +315,10 @@ func downcasts(
// CHECK: unconditional_checked_cast %2 : $@thick any (Base<Int> & P).Type to Derived.Type
let _ = baseAndPType as! Derived.Type
// CHECK: checked_cast_br any (Base<Int> & P).Type in %2 : $@thick any (Base<Int> & P).Type to any (Derived & R).Type
// CHECK: checked_cast_br [prohibit_isolated_conformances] any (Base<Int> & P).Type in %2 : $@thick any (Base<Int> & P).Type to any (Derived & R).Type
let _ = baseAndPType as? (Derived & R).Type
// CHECK: unconditional_checked_cast %2 : $@thick any (Base<Int> & P).Type to any (Derived & R).Type
// CHECK: unconditional_checked_cast [prohibit_isolated_conformances] %2 : $@thick any (Base<Int> & P).Type to any (Derived & R).Type
let _ = baseAndPType as! (Derived & R).Type
// CHECK: return
@@ -378,33 +378,33 @@ func archetypeDowncasts<S,
// CHECK: [[COPY:%.*]] = alloc_stack $S
// CHECK-NEXT: copy_addr %0 to [init] [[COPY]] : $*S
// CHECK-NEXT: [[RESULT:%.*]] = alloc_stack $any Base<T> & P
// CHECK-NEXT: checked_cast_addr_br take_always S in [[COPY]] : $*S to any Base<T> & P in [[RESULT]] : $*any Base<T> & P
// CHECK-NEXT: checked_cast_addr_br [prohibit_isolated_conformances] take_always S in [[COPY]] : $*S to any Base<T> & P in [[RESULT]] : $*any Base<T> & P
let _ = s as? (Base<T> & P)
// CHECK: [[COPY:%.*]] = alloc_stack $S
// CHECK-NEXT: copy_addr [[ARG0]] to [init] [[COPY]] : $*S
// CHECK-NEXT: [[RESULT:%.*]] = alloc_stack $any Base<T> & P
// CHECK-NEXT: unconditional_checked_cast_addr S in [[COPY]] : $*S to any Base<T> & P in [[RESULT]] : $*any Base<T> & P
// CHECK-NEXT: unconditional_checked_cast_addr [prohibit_isolated_conformances] S in [[COPY]] : $*S to any Base<T> & P in [[RESULT]] : $*any Base<T> & P
let _ = s as! (Base<T> & P)
// CHECK: [[COPY:%.*]] = alloc_stack $S
// CHECK-NEXT: copy_addr [[ARG0]] to [init] [[COPY]] : $*S
// CHECK-NEXT: [[RESULT:%.*]] = alloc_stack $any Base<Int> & P
// CHECK-NEXT: checked_cast_addr_br take_always S in [[COPY]] : $*S to any Base<Int> & P in [[RESULT]] : $*any Base<Int> & P
// CHECK-NEXT: checked_cast_addr_br [prohibit_isolated_conformances] take_always S in [[COPY]] : $*S to any Base<Int> & P in [[RESULT]] : $*any Base<Int> & P
let _ = s as? (Base<Int> & P)
// CHECK: [[COPY:%.*]] = alloc_stack $S
// CHECK-NEXT: copy_addr [[ARG0]] to [init] [[COPY]] : $*S
// CHECK-NEXT: [[RESULT:%.*]] = alloc_stack $any Base<Int> & P
// CHECK-NEXT: unconditional_checked_cast_addr S in [[COPY]] : $*S to any Base<Int> & P in [[RESULT]] : $*any Base<Int> & P
// CHECK-NEXT: unconditional_checked_cast_addr [prohibit_isolated_conformances] S in [[COPY]] : $*S to any Base<Int> & P in [[RESULT]] : $*any Base<Int> & P
let _ = s as! (Base<Int> & P)
// CHECK: [[COPIED:%.*]] = copy_value [[ARG5]] : $BaseTAndP
// CHECK-NEXT: checked_cast_br BaseTAndP in [[COPIED]] : $BaseTAndP to any Derived & R
// CHECK-NEXT: checked_cast_br [prohibit_isolated_conformances] BaseTAndP in [[COPIED]] : $BaseTAndP to any Derived & R
let _ = baseTAndP_archetype as? (Derived & R)
// CHECK: [[COPIED:%.*]] = copy_value [[ARG5]] : $BaseTAndP
// CHECK-NEXT: unconditional_checked_cast [[COPIED]] : $BaseTAndP to any Derived & R
// CHECK-NEXT: unconditional_checked_cast [prohibit_isolated_conformances] [[COPIED]] : $BaseTAndP to any Derived & R
let _ = baseTAndP_archetype as! (Derived & R)
// CHECK: [[COPIED:%.*]] = copy_value [[ARG9]] : $any Base<T> & P
@@ -447,11 +447,11 @@ func archetypeDowncasts<S,
let _ = baseTAndP_concrete as! BaseTAndP
// CHECK: [[COPIED:%.*]] = copy_value [[ARG6]] : $BaseIntAndP
// CHECK-NEXT: checked_cast_br BaseIntAndP in [[COPIED]] : $BaseIntAndP to any Derived & R
// CHECK-NEXT: checked_cast_br [prohibit_isolated_conformances] BaseIntAndP in [[COPIED]] : $BaseIntAndP to any Derived & R
let _ = baseIntAndP_archetype as? (Derived & R)
// CHECK: [[COPIED:%.*]] = copy_value [[ARG6]] : $BaseIntAndP
// CHECK-NEXT: unconditional_checked_cast [[COPIED]] : $BaseIntAndP to any Derived & R
// CHECK-NEXT: unconditional_checked_cast [prohibit_isolated_conformances] [[COPIED]] : $BaseIntAndP to any Derived & R
let _ = baseIntAndP_archetype as! (Derived & R)
// CHECK: [[COPIED:%.*]] = copy_value [[ARG10]] : $any Base<Int> & P