diff --git a/SwiftCompilerSources/Sources/Optimizer/ModulePasses/EmbeddedSwiftDiagnostics.swift b/SwiftCompilerSources/Sources/Optimizer/ModulePasses/EmbeddedSwiftDiagnostics.swift index b2a3a1e0bd190..48870708273fa 100644 --- a/SwiftCompilerSources/Sources/Optimizer/ModulePasses/EmbeddedSwiftDiagnostics.swift +++ b/SwiftCompilerSources/Sources/Optimizer/ModulePasses/EmbeddedSwiftDiagnostics.swift @@ -90,6 +90,15 @@ private struct FunctionChecker { is ExistentialMetatypeInst: if !context.options.enableEmbeddedSwiftExistentials { throw Diagnostic(.embedded_swift_existential_type, instruction.operands[0].value.type, at: instruction.location) + } else if let ie = instruction as? InitExistentialAddrInst { + for conf in ie.conformances { + try checkConformance(conf, location: ie.location) + } + } else if instruction is OpenExistentialAddrInst { + // okay in embedded with exitentials + } else { + // not supported even in embedded with exitentials + throw Diagnostic(.embedded_swift_existential_type, instruction.operands[0].value.type, at: instruction.location) } case let aeb as AllocExistentialBoxInst: @@ -116,7 +125,20 @@ private struct FunctionChecker { case is CheckedCastAddrBranchInst, is UnconditionalCheckedCastAddrInst: - throw Diagnostic(.embedded_swift_dynamic_cast, at: instruction.location) + if !context.options.enableEmbeddedSwiftExistentials { + throw Diagnostic(.embedded_swift_dynamic_cast, at: instruction.location) + } else { + if let checkedCast = instruction as? CheckedCastAddrBranchInst { + if !checkedCast.supportedInEmbeddedSwift { + throw Diagnostic(.embedded_swift_dynamic_cast, at: instruction.location) + } + } else { + let checkedCast = instruction as! UnconditionalCheckedCastAddrInst + if !checkedCast.supportedInEmbeddedSwift { + throw Diagnostic(.embedded_swift_dynamic_cast, at: instruction.location) + } + } + } case let abi as AllocBoxInst: // It needs a bit of work to support alloc_box of generic non-copyable structs/enums with deinit, @@ -221,7 +243,8 @@ private struct FunctionChecker { else { return } - if !conformance.protocol.requiresClass { + if !context.options.enableEmbeddedSwiftExistentials && + !conformance.protocol.requiresClass { throw Diagnostic(.embedded_swift_existential_protocol, conformance.protocol.name, at: location) } diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift b/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift index 7b8ed833bd840..64b10f5928844 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift +++ b/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift @@ -1001,6 +1001,22 @@ func canDynamicallyCast(from sourceType: CanonicalType, to destType: CanonicalTy } } +func isCastSupportedInEmbeddedSwift(from sourceType: Type, + to destType: Type ) -> Bool { + if !sourceType.isExistential { + return false + } + if destType.hasArchetype { + return false + } + + if !destType.isStruct && !destType.isClass && !destType.isEnum && !destType.isTuple { + return false + } + + return true +} + extension CheckedCastAddrBranchInst { var dynamicCastResult: Bool? { switch classifyDynamicCastBridged(bridged) { @@ -1010,6 +1026,18 @@ extension CheckedCastAddrBranchInst { default: fatalError("unknown result from classifyDynamicCastBridged") } } + + var supportedInEmbeddedSwift: Bool { + return isCastSupportedInEmbeddedSwift(from: source.type, + to: destination.type) + } +} + +extension UnconditionalCheckedCastAddrInst { + var supportedInEmbeddedSwift: Bool { + return isCastSupportedInEmbeddedSwift(from: source.type, + to: destination.type) + } } extension CopyAddrInst { diff --git a/SwiftCompilerSources/Sources/SIL/Instruction.swift b/SwiftCompilerSources/Sources/SIL/Instruction.swift index 47c361ef1d4b7..8057aeb6d7266 100644 --- a/SwiftCompilerSources/Sources/SIL/Instruction.swift +++ b/SwiftCompilerSources/Sources/SIL/Instruction.swift @@ -879,7 +879,11 @@ final public class OpenExistentialValueInst : SingleValueInstruction, UnaryInstruction {} final public -class InitExistentialAddrInst : SingleValueInstruction, UnaryInstruction {} +class InitExistentialAddrInst : SingleValueInstruction, UnaryInstruction { + public var conformances: ConformanceArray { + ConformanceArray(bridged: bridged.InitExistentialAddrInst_getConformances()) + } +} final public class DeinitExistentialAddrInst : Instruction, UnaryInstruction {} diff --git a/include/swift/SIL/SILBridging.h b/include/swift/SIL/SILBridging.h index 3c1151f3b46f8..a3445fe32f6cb 100644 --- a/include/swift/SIL/SILBridging.h +++ b/include/swift/SIL/SILBridging.h @@ -797,6 +797,7 @@ struct BridgedInstruction { BRIDGED_INLINE bool IndexAddrInst_needsStackProtection() const; SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedConformanceArray InitExistentialRefInst_getConformances() const; SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedCanType InitExistentialRefInst_getFormalConcreteType() const; + SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedConformanceArray InitExistentialAddrInst_getConformances() const; BRIDGED_INLINE bool OpenExistentialAddr_isImmutable() const; SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedGlobalVar GlobalAccessInst_getGlobal() const; SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedGlobalVar AllocGlobalInst_getGlobal() const; diff --git a/include/swift/SIL/SILBridgingImpl.h b/include/swift/SIL/SILBridgingImpl.h index f38feab6e6c46..750c53e38b45f 100644 --- a/include/swift/SIL/SILBridgingImpl.h +++ b/include/swift/SIL/SILBridgingImpl.h @@ -1249,6 +1249,9 @@ BridgedCanType BridgedInstruction::InitExistentialRefInst_getFormalConcreteType( return getAs()->getFormalConcreteType(); } +BridgedConformanceArray BridgedInstruction::InitExistentialAddrInst_getConformances() const { + return {getAs()->getConformances()}; +} bool BridgedInstruction::OpenExistentialAddr_isImmutable() const { switch (getAs()->getAccessKind()) { case swift::OpenedExistentialAccess::Immutable: return true; diff --git a/lib/IRGen/GenCast.cpp b/lib/IRGen/GenCast.cpp index 2f6eb20f57621..8b7a43a68fc35 100644 --- a/lib/IRGen/GenCast.cpp +++ b/lib/IRGen/GenCast.cpp @@ -85,7 +85,14 @@ llvm::Value *irgen::emitCheckedCast(IRGenFunction &IGF, src = IGF.Builder.CreateElementBitCast(src, IGF.IGM.OpaqueTy); // Load type metadata for the source's static type and the target type. - llvm::Value *srcMetadata = IGF.emitTypeMetadataRef(srcType); + llvm::Value *srcMetadata = nullptr; + + // Embedded swift currently only supports existential -> concrete type casts. + if (IGF.IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) { + srcMetadata = llvm::ConstantPointerNull::get(IGF.IGM.TypeMetadataPtrTy); + } else { + srcMetadata = IGF.emitTypeMetadataRef(srcType); + } llvm::Value *targetMetadata = IGF.emitTypeMetadataRef(targetType); llvm::Value *args[] = { diff --git a/stdlib/public/SwiftShims/swift/shims/EmbeddedShims.h b/stdlib/public/SwiftShims/swift/shims/EmbeddedShims.h index f73a40e63ae91..60205394cbcd2 100644 --- a/stdlib/public/SwiftShims/swift/shims/EmbeddedShims.h +++ b/stdlib/public/SwiftShims/swift/shims/EmbeddedShims.h @@ -96,7 +96,11 @@ typedef struct { void* (*initializeWithCopyFn)(void*, void*, void*); #endif void *assignWithCopyFn; - void *initializeWithTakeFn; +#if __has_feature(ptrauth_calls) + void* (* __ptrauth(0, 1, 0x48d8) initializeWithTakeFn)(void*, void*, void*); +#else + void* (*initializeWithTakeFn)(void *, void*, void*); +#endif void *assignWithTakeFn; void *getEnumTagSinglePayloadFn; void *storeEnumTagSinglePayload; @@ -113,43 +117,105 @@ typedef struct { #endif } EmbeddedMetaDataPrefix; +typedef enum { + AlignmentMask = 0x000000FF, + IsNonInline = 0x00020000, +} ValueWitnessTableFlags; + +static inline +EmbeddedMetaDataPrefix *_swift_embedded_get_full_metadata(void *metadata) { + EmbeddedMetaDataPrefix *fullmeta = (EmbeddedMetaDataPrefix*)&((void **)metadata)[-1]; + return fullmeta; +} + static inline __swift_size_t _swift_embedded_metadata_get_size(void *metadata) { - EmbeddedMetaDataPrefix *fullmeta = (EmbeddedMetaDataPrefix*)&((void **)metadata)[-1]; + EmbeddedMetaDataPrefix *fullmeta = _swift_embedded_get_full_metadata(metadata); return fullmeta->vwt->size; } static inline __swift_size_t _swift_embedded_metadata_get_align_mask_impl(EmbeddedMetaDataPrefix *fullMetadata) { unsigned flags = fullMetadata->vwt->flags; - unsigned embeddedValueWitnessTableFlagsMask = 0xFF; - - return flags & embeddedValueWitnessTableFlagsMask; + ValueWitnessTableFlags alignMask = AlignmentMask; + return flags & alignMask; } static inline __swift_size_t _swift_embedded_metadata_get_align_mask(void *metadata) { - EmbeddedMetaDataPrefix *fullmeta = (EmbeddedMetaDataPrefix*)&((void **)metadata)[-1]; + EmbeddedMetaDataPrefix *fullmeta = _swift_embedded_get_full_metadata(metadata); return _swift_embedded_metadata_get_align_mask_impl(fullmeta); } -static inline void -_swift_embedded_invoke_box_destroy(void *object) { - void *metadata = ((EmbeddedHeapObject *)object)->metadata; - EmbeddedMetaDataPrefix *fullmeta = (EmbeddedMetaDataPrefix*)&((void **)metadata)[-1]; +static inline void * +_swift_embedded_box_project(void *object, EmbeddedMetaDataPrefix *fullmeta) { __swift_size_t alignMask = _swift_embedded_metadata_get_align_mask_impl(fullmeta); __swift_size_t headerSize = sizeof(void*) + sizeof(__swift_size_t); __swift_size_t startOfBoxedValue = (headerSize + alignMask) & ~alignMask; void *addrInBox = (void *)(((unsigned char *)object) + startOfBoxedValue); + return addrInBox; +} +static inline void +_swift_embedded_invoke_box_destroy(void *object) { + void *metadata = ((EmbeddedHeapObject *)object)->metadata; + EmbeddedMetaDataPrefix *fullmeta = _swift_embedded_get_full_metadata(metadata); + void *addrInBox = _swift_embedded_box_project(object, fullmeta); fullmeta->vwt->destroyFn(addrInBox, metadata); } static inline void _swift_embedded_initialize_box(void *metadata, void *newObjectAddr, void *oldObjectAddr) { - EmbeddedMetaDataPrefix *fullmeta = (EmbeddedMetaDataPrefix*)&((void **)metadata)[-1]; + EmbeddedMetaDataPrefix *fullmeta = _swift_embedded_get_full_metadata(metadata); fullmeta->vwt->initializeWithCopyFn(newObjectAddr, oldObjectAddr, metadata); } +typedef struct { + void *inlineBuffer[3]; + void *metadata; +} ExistentialValue; + +static inline void +_swift_embedded_existential_destroy(void *exist) { + ExistentialValue* existVal = (ExistentialValue*)exist; + void *metadata = existVal->metadata; + EmbeddedMetaDataPrefix *fullmeta = _swift_embedded_get_full_metadata(metadata); + ValueWitnessTableFlags isNonInlineMask = IsNonInline; + if (fullmeta->vwt->flags & IsNonInline) { + void *addrInBox = _swift_embedded_box_project(existVal->inlineBuffer[0], fullmeta); + fullmeta->vwt->destroyFn(addrInBox, metadata); + } else { + fullmeta->vwt->destroyFn(&(existVal->inlineBuffer[0]), metadata); + } +} + +static inline void +_swift_embedded_existential_init_with_take(void *dst, void *srcExist) { + ExistentialValue* existVal = (ExistentialValue*)srcExist; + void *metadata = existVal->metadata; + EmbeddedMetaDataPrefix *fullmeta = _swift_embedded_get_full_metadata(metadata); + ValueWitnessTableFlags isNonInlineMask = IsNonInline; + if (fullmeta->vwt->flags & IsNonInline) { + void *addrInBox = _swift_embedded_box_project(existVal->inlineBuffer[0], fullmeta); + fullmeta->vwt->initializeWithTakeFn(dst, addrInBox, metadata); + } else { + fullmeta->vwt->initializeWithTakeFn(dst, &(existVal->inlineBuffer[0]), metadata); + } +} + +static inline void +_swift_embedded_existential_init_with_copy(void *dst, void *srcExist) { +ExistentialValue* existVal = (ExistentialValue*)srcExist; + void *metadata = existVal->metadata; + EmbeddedMetaDataPrefix *fullmeta = _swift_embedded_get_full_metadata(metadata); + ValueWitnessTableFlags isNonInlineMask = IsNonInline; + if (fullmeta->vwt->flags & IsNonInline) { + void *addrInBox = _swift_embedded_box_project(existVal->inlineBuffer[0], fullmeta); + fullmeta->vwt->initializeWithCopyFn(dst, addrInBox, metadata); + } else { + fullmeta->vwt->initializeWithCopyFn(dst, &(existVal->inlineBuffer[0]), metadata); + } +} + #ifdef __cplusplus } // extern "C" #endif diff --git a/stdlib/public/core/EmbeddedRuntime.swift b/stdlib/public/core/EmbeddedRuntime.swift index 299b1d82a8f75..b5360be7b41fa 100644 --- a/stdlib/public/core/EmbeddedRuntime.swift +++ b/stdlib/public/core/EmbeddedRuntime.swift @@ -315,6 +315,113 @@ public func swifft_makeBoxUnique(buffer: Builtin.RawPointer, metadata: Builtin.R return (box, oldObjectAddr._rawValue) } } +/// Dynamic cast support +/// Only supports existential container to concrete casts. +struct DynamicCastFlags { + static let Default = UInt(bitPattern: 0x0) + static let Unconditional = UInt(bitPattern: 0x1) + static let TakeOnSuccess = UInt(bitPattern: 0x2) + static let DestroyOnFailure = UInt(bitPattern: 0x4) +} + +struct MetadataKind { + static let LastEnumerated = UInt(bitPattern: 0x7FF) +} + +func projectExistentialMetadata( + _ exist: Builtin.RawPointer +) -> Builtin.RawPointer { + + let offset = (3 * MemoryLayout.size) + let addrOfMetadata = unsafe UnsafeMutableRawPointer(exist) + offset + let metadataPtrAddr = unsafe addrOfMetadata.bindMemory(to: Builtin.RawPointer.self, capacity: 1) + return unsafe metadataPtrAddr.pointee +} + +func isClassMetadata(_ metadata: Builtin.RawPointer) -> Bool { + let addrOfMetadataKind = unsafe UnsafePointer(metadata) + let kind = unsafe addrOfMetadataKind.pointee + return kind == 0 || kind > MetadataKind.LastEnumerated +} + +func projectHeapObject(_ exist: Builtin.RawPointer) -> UnsafeMutableRawPointer{ + let addrOfHeapObject = unsafe UnsafePointer(exist) + return unsafe addrOfHeapObject.pointee +} + +@c +public func swift_dynamicCast( + _ dest: Builtin.RawPointer, /* points to a concrete type */ + _ src: Builtin.RawPointer, /* points to an existential */ + _ srcMetadata: Builtin.RawPointer, /* always nullptr */ + _ dstMetadata: Builtin.RawPointer, + _ flags: UInt +) -> Bool { + + let isUnconditionalCast : Bool = (flags & DynamicCastFlags.Unconditional) != 0 + let isTakeOnSuccess : Bool = (flags & DynamicCastFlags.TakeOnSuccess) != 0 + let isDestroyOnFailure : Bool = + (flags & DynamicCastFlags.DestroyOnFailure) != 0 + + let srcMetadata = projectExistentialMetadata(src) + + let isClass = isClassMetadata(dstMetadata) + + if isClass { + var success = false + let obj = unsafe projectHeapObject(src) + if isClassMetadata(srcMetadata) { + if srcMetadata != dstMetadata { + // check parent chain + success = unsafe swift_dynamicCastClass(object: obj, targetMetadata: UnsafeMutableRawPointer(dstMetadata)) != nil + } else { + success = true + } + } + if !success { + if isDestroyOnFailure { + unsafe _swift_embedded_existential_destroy(UnsafeMutableRawPointer(src)) + } + + if isUnconditionalCast { + fatalError("failed cast") + } + return false + } + if isTakeOnSuccess { + let dst = unsafe UnsafeMutablePointer(dest) + unsafe dst.pointee = obj + + } else { + let dst = unsafe UnsafeMutablePointer(dest) + unsafe dst.pointee = obj + swift_retain(object: obj._rawValue) + } + return true; + } + // destintation type is not a class. Test exact match. + let success = srcMetadata == dstMetadata + if !success { + if isDestroyOnFailure { + unsafe _swift_embedded_existential_destroy(UnsafeMutableRawPointer(src)) + } + + if isUnconditionalCast { + fatalError("failed cast") + } + return false + } + if isTakeOnSuccess { + // take from an existential to a concrete type + unsafe _swift_embedded_existential_init_with_take( + UnsafeMutableRawPointer(dest), UnsafeMutableRawPointer(src)) + } else { + // copy from an existential to a concrete type + unsafe _swift_embedded_existential_init_with_copy( + UnsafeMutableRawPointer(dest), UnsafeMutableRawPointer(src)) + } + return true +} /// Refcounting diff --git a/test/embedded/existential.swift b/test/embedded/existential.swift index dd850b85f4175..d0852940ec93d 100644 --- a/test/embedded/existential.swift +++ b/test/embedded/existential.swift @@ -190,6 +190,118 @@ func test4(_ p: any WithAssoc) { c.printValue() } +func test5(_ p: any Any) { + print("test any as? MyStruct") + if let c = p as? MyStruct { + print("success") + c.a() + } else { + print("cast failed") + } +} + +func test6(_ p: any Any) { + print("test any as? LargeMyStruct") + if let c = p as? LargeMyStruct { + print("success") + c.a() + } else { + print("cast failed") + } +} + +func test7(_ p: any Any) { + print("test any as! LargeMyStruct") + let c = p as! LargeMyStruct + c.a() +} + +class BaseClass { + func foo() { print("BaseClass.foo") } + deinit { + print("BaseClass.deinit") + } +} + +class SubClass : BaseClass { + override func foo() { print("SubClass.foo") } +} + +func test8(_ p: any Any) { + print("test any as? SubClass") + if let c = p as? SubClass { + print("success") + c.foo() + } else { + print("cast failed") + } +} + +func test9(_ p: any Any) { + print("test any as? BaseClass") + if let c = p as? BaseClass { + print("success") + c.foo() + } else { + print("cast failed") + } +} + +func test10(_ p: any Any) { + print("test any as! BaseClass") + let c = p as! BaseClass + c.foo() +} + +func test11(_ p: any Any) { + print("test any as! SubClass") + let c = p as! SubClass + c.foo() +} + +func test12(_ p: any Any) { + print("test any as! (Int, Int, Int, Int)") + if let c = p as? (Int, Int, Int, Int) { + print("success") + print("tuple: \(c.0)") + } else { + print("cast failed") + } +} + +protocol Q { + func printit() +} + +protocol P4 { + associatedtype T: Q + + var t: T { get } +} + +struct QConformer : Q { + var x = (0, 1, 2,3) + + func printit() { + print("QConformer \(x.3)") + } +} + +struct P4Conformer : P4 { + var q = QConformer() + + var t : QConformer { + get { + return q + } + } +} + +func test13(_ p: any P4) { + print("test13") + p.t.printit() +} + @main struct Main { static func main() { @@ -221,5 +333,95 @@ struct Main { // OUTPUT: my value of LargeMyStruct: 5 // OUTPUT: deinit called // OUTPUT-NOT: deinit called + + test5(MyStruct()) +// OUTPUT: test any as? MyStruct +// OUTPUT: success +// OUTPUT: a MyStruct 5 + test5(GC()) +// OUTPUT: test any as? MyStruct +// OUTPUT: cast failed +// OUTPUT: deinit called +// OUTPUT-NOT: deinit called + test5(LargeMyStruct()) +// OUTPUT: test any as? MyStruct +// OUTPUT: deinit called +// OUTPUT: cast failed +// OUTPUT-NOT: deinit called + test5(GenericStructWithClass()) +// OUTPUT: test any as? MyStruct +// OUTPUT: cast failed +// OUTPUT: deinit called +// OUTPUT: deinit called +// OUTPUT-NOT: deinit called + test6(MyStruct()) +// OUTPUT: test any as? LargeMyStruct +// OUTPUT: cast failed +// OUTPUT-NOT: deinit called + test6(LargeMyStruct()) +// OUTPUT: test any as? LargeMyStruct +// OUTPUT: success +// OUTPUT: a LargeMyStruct 5 +// OUTPUT: deinit called +// OUTPUT-NOT: deinit called + test6(GenericStructWithClass()) +// OUTPUT: test any as? LargeMyStruct +// OUTPUT: cast failed +// OUTPUT: deinit called +// OUTPUT: deinit called +// OUTPUT-NOT: deinit called + test7(LargeMyStruct()) +// OUTPUT: test any as! LargeMyStruct +// OUTPUT: a LargeMyStruct 5 +// OUTPUT: deinit called +// OUTPUT-NOT: deinit called + test8(SubClass()) +// OUTPUT: success +// OUTPUT: SubClass.foo +// OUTPUT: BaseClass.deinit +// OUTPUT-NOT: deinit + test8(BaseClass()) +// OUTPUT: test any as? SubClass +// OUTPUT: cast failed +// OUTPUT: BaseClass.deinit +// OUTPUT-NOT: deinit + test9(SubClass()) +// OUTPUT: test any as? BaseClass +// OUTPUT: success +// OUTPUT: SubClass.foo +// OUTPUT: BaseClass.deinit +// OUTPUT-NOT: deinit + test9(BaseClass()) +// OUTPUT: test any as? BaseClass +// OUTPUT: success +// OUTPUT: BaseClass.foo +// OUTPUT: BaseClass.deinit +// OUTPUT-NOT: deinit + test9(C()) +// OUTPUT: test any as? BaseClass +// OUTPUT: cast failed +// OUTPUT-NOT: deinit + test10(BaseClass()) +// OUTPUT: test any as! BaseClass +// OUTPUT: BaseClass.foo +// OUTPUT: BaseClass.deinit +// OUTPUT-NOT: deinit + test10(SubClass()) +// OUTPUT: test any as! BaseClass +// OUTPUT: SubClass.foo +// OUTPUT: BaseClass.deinit +// OUTPUT-NOT: deinit + test11(SubClass()) +// OUTPUT: test any as! SubClass +// OUTPUT: SubClass.foo +// OUTPUT: BaseClass.deinit +// OUTPUT-NOT: deinit + test12((0, 1, 2, 3)) +// OUTPUT: test any as! (Int, Int, Int, Int) +// OUTPUT: success +// OUTPUT: tuple: 0 + test13(P4Conformer()) +// OUTPUT: test13 +// OUTPUT: QConformer 3 } } diff --git a/test/embedded/existential_cast_fails.swift b/test/embedded/existential_cast_fails.swift new file mode 100644 index 0000000000000..655bb418a846c --- /dev/null +++ b/test/embedded/existential_cast_fails.swift @@ -0,0 +1,109 @@ +// RUN: %empty-directory(%t) + +// RUN: %target-clang -x c -c %S/Inputs/unbuffered-putchar.c -o %t/unbuffered-putchar.o + +// RUN: %target-build-swift -DT1 -enable-experimental-feature Embedded \ +// RUN: -parse-as-library -Xlinker %t/unbuffered-putchar.o \ +// RUN: -enable-experimental-feature EmbeddedExistentials \ +// RUN: -wmo -runtime-compatibility-version none %s -o %t/t1.out +// RUN: not --crash %t/t1.out 2>&1 | %FileCheck %s --check-prefix=CHECK-T1 + +// RUN: %target-build-swift -DT2 -enable-experimental-feature Embedded \ +// RUN: -parse-as-library -Xlinker %t/unbuffered-putchar.o \ +// RUN: -enable-experimental-feature EmbeddedExistentials \ +// RUN: -wmo -runtime-compatibility-version none %s -o %t/t2.out +// RUN: not --crash %t/t2.out 2>&1 | %FileCheck %s --check-prefix=CHECK-T2 + +// RUN: %target-build-swift -DT3 -enable-experimental-feature Embedded \ +// RUN: -parse-as-library -Xlinker %t/unbuffered-putchar.o \ +// RUN: -enable-experimental-feature EmbeddedExistentials \ +// RUN: -wmo -runtime-compatibility-version none %s -o %t/t3.out +// RUN: not --crash %t/t3.out 2>&1 | %FileCheck %s --check-prefix=CHECK-T3 + +// RUN: %target-build-swift -DT4 -enable-experimental-feature Embedded \ +// RUN: -parse-as-library -Xlinker %t/unbuffered-putchar.o \ +// RUN: -enable-experimental-feature EmbeddedExistentials \ +// RUN: -wmo -runtime-compatibility-version none %s -o %t/t4.out +// RUN: not --crash %t/t4.out 2>&1 | %FileCheck %s --check-prefix=CHECK-T4 + +// REQUIRES: swift_in_compiler +// REQUIRES: executable_test +// REQUIRES: optimized_stdlib +// REQUIRES: swift_feature_Embedded +// REQUIRES: swift_feature_EmbeddedExistentials + +// REQUIRES: OS=macosx + +class CP { + func foo() { print("foo called") } + deinit { + print("deinit called") + } +} + +class C : CP { + override func foo() { print("C.foo called") } +} + +class CP2 { + func foo() { print("CP2.foo called") } + deinit { + print("deinit called") + } +} + +struct StructWithClass { + var c = C() +} + + +struct LargeStructWithClass { + var c = C() + var t = (0, 1, 2, 3, 4, 5, 6, 7, 8) + func foo() { c.foo() } +} + +struct LargetMyStruct { + var l = LargeStructWithClass() +} + +func test1(_ p: any Any) { + print("test any as! CP") + let c = p as! CP + c.foo() +} + +func test2(_ p: any Any) { + print("test any as! LargeStructWithClass") + let c = p as! LargeStructWithClass + c.foo() +} + +@main +struct Main { + static func main() { +#if T1 + test1(StructWithClass()) +// CHECK-T1: test any as! CP +// CHECK-T1: failed cast +#endif + +#if T2 + test1(CP2()) +// CHECK-T2: test any as! CP +// CHECK-T2: failed cast +#endif + +#if T3 + test2(StructWithClass()) +// CHECK-T3: test any as! LargeStructWithClass +// CHECK-T3: failed cast +#endif + +#if T4 + test2(CP2()) +// CHECK-T4: test any as! LargeStructWithClass +// CHECK-T4: failed cast +#endif + } +}