SIL: allow casts to class-bound archetypes to be done as scalar-casts instead of address-casts

This allows such casts to be done in embedded swift.

rdar://156302495
This commit is contained in:
Erik Eckstein
2025-07-21 21:08:45 +02:00
parent 3bacfa7fb9
commit b68cafb870
9 changed files with 131 additions and 24 deletions

View File

@@ -210,7 +210,8 @@ llvm::Value *irgen::emitClassDowncast(IRGenFunction &IGF, llvm::Value *from,
// If the destination type is known to have a Swift-compatible
// implementation, use the most specific entrypoint.
if (destClass && destClass->hasKnownSwiftImplementation()) {
if ((destClass && destClass->hasKnownSwiftImplementation()) ||
IGF.IGM.Context.LangOpts.hasFeature(Feature::Embedded)) {
metadataRef = IGF.emitTypeMetadataRef(toType);
switch (mode) {

View File

@@ -1378,23 +1378,26 @@ bool swift::canIRGenUseScalarCheckedCastInstructions(SILModule &M,
// bridging, unless we can statically see that the source type inherits
// NSError.
// A class-constrained archetype may be bound to NSError, unless it has a
// non-NSError superclass constraint. Casts to archetypes thus must always be
// indirect.
if (auto archetype = targetFormalType->getAs<ArchetypeType>()) {
// Only ever permit this if the source type is a reference type.
if (!objectType.isAnyClassReferenceType())
return false;
auto super = archetype->getSuperclass();
if (super.isNull())
return false;
// A base class constraint that isn't NSError rules out the archetype being
// bound to NSError.
if (M.getASTContext().LangOpts.EnableObjCInterop) {
// A class-constrained archetype may be bound to NSError, unless it has a
// non-NSError superclass constraint. Casts to archetypes thus must always be
// indirect.
auto super = archetype->getSuperclass();
if (super.isNull())
return false;
// A base class constraint that isn't NSError rules out the archetype being
// bound to NSError.
if (auto nserror = M.Types.getNSErrorType())
return !super->isEqual(nserror);
} else {
if (!archetype->requiresClass())
return false;
}
// If NSError wasn't loaded, any base class constraint must not be NSError.

View File

@@ -35,7 +35,7 @@ public class SelfCasts {
}
// CHECK-LABEL: define {{(dllexport )?}}{{(protected )?}}swiftcc ptr @"$s17dynamic_self_cast9SelfCastsC016classGenericFromD0xyRlzClFZ"(ptr %T, ptr swiftself %0)
// CHECK: call zeroext i1 @swift_dynamicCast(ptr {{%.*}}, ptr {{%.*}}, ptr %0, ptr %T, {{.*}})
// CHECK: call ptr @swift_dynamicCastUnknownClassUnconditional(ptr {{%.*}}, ptr %T, ptr null, i32 0, i32 0) #4
// CHECK: ret
public static func classGenericFromSelf<T : AnyObject>() -> T {
let s = Self()
@@ -72,7 +72,7 @@ public class SelfCasts {
}
// CHECK-LABEL: define {{(dllexport )?}}{{(protected )?}}swiftcc {{i32|i64}} @"$s17dynamic_self_cast9SelfCastsC016classGenericFromD11ConditionalxSgyRlzClFZ"(ptr %T, ptr swiftself %0)
// CHECK: call zeroext i1 @swift_dynamicCast(ptr {{%.*}}, ptr {{%.*}}, ptr %0, ptr %T, {{.*}})
// CHECK: call ptr @swift_dynamicCastUnknownClass(ptr {{%.*}}, ptr %T)
// CHECK: ret
public static func classGenericFromSelfConditional<T : AnyObject>() -> T? {
let s = Self()

View File

@@ -1,4 +1,4 @@
// RUN: %target-swift-frontend -Xllvm -sil-print-types -emit-silgen %s | %FileCheck %s
// RUN: %target-swift-frontend -Xllvm -sil-print-types -disable-objc-interop -emit-silgen %s | %FileCheck %s
public class SelfCasts {
// CHECK-LABEL: sil [ossa] @$s17dynamic_self_cast9SelfCastsC02toD0yACXDACFZ : $@convention(method) (@guaranteed SelfCasts, @thick SelfCasts.Type) -> @owned SelfCasts {
@@ -31,7 +31,7 @@ public class SelfCasts {
}
// CHECK-LABEL: sil [ossa] @$s17dynamic_self_cast9SelfCastsC016classGenericFromD0xyRlzClFZ : $@convention(method) <T where T : AnyObject> (@thick SelfCasts.Type) -> @owned T
// CHECK: unconditional_checked_cast_addr @dynamic_self SelfCasts in {{.*}} : $*SelfCasts to T in {{.*}} : $*T
// CHECK: unconditional_checked_cast {{.*}} : $SelfCasts to T
// CHECK: }
public static func classGenericFromSelf<T : AnyObject>() -> T {
let s = Self()
@@ -68,7 +68,7 @@ public class SelfCasts {
}
// CHECK-LABEL: sil [ossa] @$s17dynamic_self_cast9SelfCastsC016classGenericFromD11ConditionalxSgyRlzClFZ : $@convention(method) <T where T : AnyObject> (@thick SelfCasts.Type) -> @owned Optional<T> {
// CHECK: checked_cast_addr_br take_always @dynamic_self SelfCasts in {{.*}} : $*SelfCasts to T in {{.*}} : $*T
// CHECK: checked_cast_br @dynamic_self SelfCasts in {{.*}} : $SelfCasts to T
// CHECK: }
public static func classGenericFromSelfConditional<T : AnyObject>() -> T? {
let s = Self()

View File

@@ -0,0 +1,82 @@
// RUN: %target-swift-frontend -Xllvm -sil-print-types -module-name=dynamic_self_cast -emit-silgen %s | %FileCheck %s
// REQUIRES: objc_interop
public class SelfCasts {
// CHECK-LABEL: sil [ossa] @$s17dynamic_self_cast9SelfCastsC02toD0yACXDACFZ : $@convention(method) (@guaranteed SelfCasts, @thick SelfCasts.Type) -> @owned SelfCasts {
// CHECK: unconditional_checked_cast {{.*}} : $SelfCasts to @dynamic_self SelfCasts
// CHECK: }
public static func toSelf(_ s: SelfCasts) -> Self {
return s as! Self
}
// CHECK-LABEL: sil [ossa] @$s17dynamic_self_cast9SelfCastsC09genericToD0yACXDxlFZ : $@convention(method) <T> (@in_guaranteed T, @thick SelfCasts.Type) -> @owned SelfCasts {
// CHECK: unconditional_checked_cast_addr T in {{.*}} : $*T to @dynamic_self SelfCasts in {{.*}} : $*SelfCasts
// CHECK: }
public static func genericToSelf<T>(_ s: T) -> Self {
return s as! Self
}
// CHECK-LABEL: sil [ossa] @$s17dynamic_self_cast9SelfCastsC014classGenericToD0yACXDxRlzClFZ : $@convention(method) <T where T : AnyObject> (@guaranteed T, @thick SelfCasts.Type) -> @owned SelfCasts {
// CHECK: unconditional_checked_cast {{.*}} : $T to @dynamic_self SelfCasts
// CHECK: }
public static func classGenericToSelf<T : AnyObject>(_ s: T) -> Self {
return s as! Self
}
// CHECK-LABEL: sil [ossa] @$s17dynamic_self_cast9SelfCastsC011genericFromD0xylFZ : $@convention(method) <T> (@thick SelfCasts.Type) -> @out T {
// CHECK: unconditional_checked_cast_addr @dynamic_self SelfCasts in {{.*}} : $*SelfCasts to T in {{.*}} : $*T
// CHECK: }
public static func genericFromSelf<T>() -> T {
let s = Self()
return s as! T
}
// CHECK-LABEL: sil [ossa] @$s17dynamic_self_cast9SelfCastsC016classGenericFromD0xyRlzClFZ : $@convention(method) <T where T : AnyObject> (@thick SelfCasts.Type) -> @owned T
// CHECK: unconditional_checked_cast_addr @dynamic_self SelfCasts in {{.*}} : $*SelfCasts to T in {{.*}} : $*T
// CHECK: }
public static func classGenericFromSelf<T : AnyObject>() -> T {
let s = Self()
return s as! T
}
// CHECK-LABEL: sil [ossa] @$s17dynamic_self_cast9SelfCastsC02toD11ConditionalyACXDSgACFZ : $@convention(method) (@guaranteed SelfCasts, @thick SelfCasts.Type) -> @owned Optional<SelfCasts> {
// CHECK: checked_cast_br SelfCasts in {{.*}} : $SelfCasts to @dynamic_self SelfCasts
// CHECK: }
public static func toSelfConditional(_ s: SelfCasts) -> Self? {
return s as? Self
}
// CHECK-LABEL: sil [ossa] @$s17dynamic_self_cast9SelfCastsC09genericToD11ConditionalyACXDSgxlFZ : $@convention(method) <T> (@in_guaranteed T, @thick SelfCasts.Type) -> @owned Optional<SelfCasts> {
// CHECK: checked_cast_addr_br take_always T in {{.*}} : $*T to @dynamic_self SelfCasts in {{.*}} : $*SelfCasts
// CHECK: }
public static func genericToSelfConditional<T>(_ s: T) -> Self? {
return s as? Self
}
// CHECK-LABEL: sil [ossa] @$s17dynamic_self_cast9SelfCastsC014classGenericToD11ConditionalyACXDSgxRlzClFZ : $@convention(method) <T where T : AnyObject> (@guaranteed T, @thick SelfCasts.Type) -> @owned Optional<SelfCasts> {
// CHECK: checked_cast_br T in {{.*}} : $T to @dynamic_self SelfCasts
// CHECK: }
public static func classGenericToSelfConditional<T : AnyObject>(_ s: T) -> Self? {
return s as? Self
}
// CHECK-LABEL: sil [ossa] @$s17dynamic_self_cast9SelfCastsC011genericFromD11ConditionalxSgylFZ : $@convention(method) <T> (@thick SelfCasts.Type) -> @out Optional<T> {
// CHECK: checked_cast_addr_br take_always @dynamic_self SelfCasts in {{.*}} : $*SelfCasts to T in {{.*}} : $*T
// CHECK: }
public static func genericFromSelfConditional<T>() -> T? {
let s = Self()
return s as? T
}
// CHECK-LABEL: sil [ossa] @$s17dynamic_self_cast9SelfCastsC016classGenericFromD11ConditionalxSgyRlzClFZ : $@convention(method) <T where T : AnyObject> (@thick SelfCasts.Type) -> @owned Optional<T> {
// CHECK: checked_cast_addr_br take_always @dynamic_self SelfCasts in {{.*}} : $*SelfCasts to T in {{.*}} : $*T
// CHECK: }
public static func classGenericFromSelfConditional<T : AnyObject>() -> T? {
let s = Self()
return s as? T
}
public required init() {}
}

View File

@@ -1,5 +1,5 @@
// RUN: %target-swift-emit-silgen -Xllvm -sil-print-types -swift-version 5 -module-name generic_casts -Xllvm -sil-full-demangle %s | %FileCheck %s
// RUN: %target-swift-emit-silgen -Xllvm -sil-print-types -disable-objc-interop -swift-version 5 -module-name generic_casts -Xllvm -sil-full-demangle %s | %FileCheck %s
protocol ClassBound : class {}
protocol NotClassBound {}
@@ -59,8 +59,7 @@ func class_archetype_to_class_archetype
<T:ClassBound, U:ClassBound>(_ t:T) -> U {
return t as! U
// Error bridging can change the identity of class-constrained archetypes.
// CHECK: unconditional_checked_cast_addr T in {{%.*}} : $*T to U in [[DOWNCAST_ADDR:%.*]] : $*U
// CHECK: [[DOWNCAST:%.*]] = load [take] [[DOWNCAST_ADDR]]
// CHECK: [[DOWNCAST:%.*]] = unconditional_checked_cast {{%.*}} : $T to U
// CHECK: return [[DOWNCAST]] : $U
}
@@ -69,7 +68,7 @@ func class_archetype_is_class_archetype
<T:ClassBound, U:ClassBound>(_ t:T, u:U.Type) -> Bool {
return t is U
// Error bridging can change the identity of class-constrained archetypes.
// CHECK: checked_cast_addr_br {{.*}} T in {{%.*}} : $*T to U in {{%.*}} : $*U
// CHECK: checked_cast_br T in {{%.*}} : $T to U
}
// CHECK-LABEL: sil hidden [ossa] @$s13generic_casts38opaque_archetype_to_addr_only_concrete{{[_0-9a-zA-Z]*}}F
@@ -158,8 +157,7 @@ func opaque_existential_is_class_archetype
func class_existential_to_class_archetype
<T:ClassBound>(_ p:ClassBound) -> T {
return p as! T
// CHECK: unconditional_checked_cast_addr any ClassBound in {{%.*}} : $*any ClassBound to T in [[DOWNCAST_ADDR:%.*]] : $*T
// CHECK: [[DOWNCAST:%.*]] = load [take] [[DOWNCAST_ADDR]]
// CHECK: [[DOWNCAST:%.*]] = unconditional_checked_cast {{%.*}} : $any ClassBound to T
// CHECK: return [[DOWNCAST]] : $T
}
@@ -167,7 +165,7 @@ func class_existential_to_class_archetype
func class_existential_is_class_archetype
<T:ClassBound>(_ p:ClassBound, _: T) -> Bool {
return p is T
// CHECK: checked_cast_addr_br {{.*}} any ClassBound in {{%.*}} : $*any ClassBound to T in {{%.*}} : $*T
// CHECK: checked_cast_br any ClassBound in {{%.*}} : $any ClassBound to T
}
// CHECK-LABEL: sil hidden [ossa] @$s13generic_casts40opaque_existential_to_addr_only_concrete{{[_0-9a-zA-Z]*}}F

View File

@@ -2,6 +2,7 @@
// REQUIRES: optimized_stdlib
// REQUIRES: swift_stdlib_no_asserts
// REQUIRES: OS=macosx
///////////////////
// Generic Casts //

View File

@@ -1,4 +1,4 @@
// RUN: %target-sil-opt -rc-id-dumper -enable-sil-opaque-values -module-name Swift %s -o /dev/null | %FileCheck %s
// RUN: %target-sil-opt -rc-id-dumper -enable-sil-opaque-values -disable-objc-interop -module-name Swift %s -o /dev/null | %FileCheck %s
import Builtin
@@ -295,7 +295,7 @@ bb0(%0 : @owned $T):
//
// CHECK-LABEL: @testClassToClassArchetypeIsBridged
// CHECK: RESULT #0: 0 = 0
// CHECK: RESULT #1: 1 = 1
// CHECK: RESULT #1: 1 = 0
sil [ossa] @testClassToClassArchetypeIsBridged : $@convention(thin) <U : AnyObject>(@owned Base) -> @owned U {
bb0(%0 : @owned $Base):
%1 = unconditional_checked_cast %0 : $Base to U

View File

@@ -10,6 +10,7 @@
protocol ClassBound: AnyObject {
func foo()
func bar()
func predicate() -> Bool
}
class MyClass {}
@@ -230,6 +231,22 @@ func testP4() -> any P4 {
return C4(t: K4(x: 437))
}
var gg: any ClassBound = MyClass()
extension ClassBound {
public func isSame(_ rhs: some ClassBound) -> Bool {
if let rhs = rhs as? Self {
return rhs.predicate()
}
return false
}
public func predicate() -> Bool { true }
}
@inline(never)
func testIsSame(_ p: any ClassBound) -> Bool {
return p.isSame(gg)
}
@main
@@ -261,6 +278,11 @@ struct Main {
// CHECK: 102
testP4().foo()
// CHECK: 437
print(testIsSame(MyClass()))
// CHECK: true
print(testIsSame(MyOtherClass()))
// CHECK: false
}
}