From f7aae22e43e38abd4fd619cd6ba916213e3f5bda Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Thu, 20 Nov 2025 07:34:33 -0800 Subject: [PATCH 01/11] [embedded] A few more test cases for existential casting --- test/embedded/existential.swift | 85 +++++++++++++++- test/embedded/existential_cast_fails.swift | 107 +++++++++++++++++++++ 2 files changed, 191 insertions(+), 1 deletion(-) create mode 100644 test/embedded/existential_cast_fails.swift diff --git a/test/embedded/existential.swift b/test/embedded/existential.swift index 004e29c987136..812090fad8faa 100644 --- a/test/embedded/existential.swift +++ b/test/embedded/existential.swift @@ -216,6 +216,48 @@ func test7(_ p: any Any) { 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() +} @main struct Main { static func main() { @@ -262,7 +304,7 @@ struct Main { // OUTPUT: deinit called // OUTPUT: cast failed // OUTPUT-NOT: deinit called - test5(GenericStructWithClass()) + test5(GenericStructWithClass()) // OUTPUT: test any as? MyStruct // OUTPUT: cast failed // OUTPUT: deinit called @@ -289,5 +331,46 @@ struct Main { // 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 } } diff --git a/test/embedded/existential_cast_fails.swift b/test/embedded/existential_cast_fails.swift new file mode 100644 index 0000000000000..f8f5a8ff2990b --- /dev/null +++ b/test/embedded/existential_cast_fails.swift @@ -0,0 +1,107 @@ +// 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 + +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 + } +} From 01977846253d701cdcd9ff7b3ba916ca4725b2f3 Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Thu, 20 Nov 2025 07:54:09 -0800 Subject: [PATCH 02/11] [embedded] Tighten the check which existentials we support in embedded with existentials --- .../Optimizer/ModulePasses/EmbeddedSwiftDiagnostics.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/SwiftCompilerSources/Sources/Optimizer/ModulePasses/EmbeddedSwiftDiagnostics.swift b/SwiftCompilerSources/Sources/Optimizer/ModulePasses/EmbeddedSwiftDiagnostics.swift index ed1432bb01359..48870708273fa 100644 --- a/SwiftCompilerSources/Sources/Optimizer/ModulePasses/EmbeddedSwiftDiagnostics.swift +++ b/SwiftCompilerSources/Sources/Optimizer/ModulePasses/EmbeddedSwiftDiagnostics.swift @@ -94,6 +94,11 @@ private struct FunctionChecker { 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: From 8beaad60bdb7d9a414315f0ba2972687a1ee53de Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Thu, 20 Nov 2025 10:00:10 -0800 Subject: [PATCH 03/11] [embedded] Allow casting to tuples --- .../Sources/Optimizer/Utilities/OptUtils.swift | 3 +-- test/embedded/existential.swift | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift b/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift index 216a4a3d33d43..64b10f5928844 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift +++ b/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift @@ -1010,8 +1010,7 @@ func isCastSupportedInEmbeddedSwift(from sourceType: Type, return false } - // Tuple? - if !destType.isStruct && !destType.isClass && !destType.isEnum { + if !destType.isStruct && !destType.isClass && !destType.isEnum && !destType.isTuple { return false } diff --git a/test/embedded/existential.swift b/test/embedded/existential.swift index 812090fad8faa..9e1611f074fef 100644 --- a/test/embedded/existential.swift +++ b/test/embedded/existential.swift @@ -258,6 +258,17 @@ func test11(_ p: any Any) { 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") + } +} + @main struct Main { static func main() { @@ -372,5 +383,9 @@ struct Main { // 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 } } From 920def9319cdb16226dff56e39980597dd368f1e Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Thu, 20 Nov 2025 10:16:19 -0800 Subject: [PATCH 04/11] [embedded] One more test --- test/embedded/existential.swift | 36 +++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/test/embedded/existential.swift b/test/embedded/existential.swift index 9e1611f074fef..d0852940ec93d 100644 --- a/test/embedded/existential.swift +++ b/test/embedded/existential.swift @@ -269,6 +269,39 @@ func test12(_ p: any Any) { } } +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() { @@ -387,5 +420,8 @@ struct Main { // OUTPUT: test any as! (Int, Int, Int, Int) // OUTPUT: success // OUTPUT: tuple: 0 + test13(P4Conformer()) +// OUTPUT: test13 +// OUTPUT: QConformer 3 } } From 60a28cd57036469b2695b70c3b7d395cdeea4fca Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Tue, 11 Nov 2025 10:17:23 +0100 Subject: [PATCH 05/11] embedded: don't re-abstract witness method calls for existentials Don't convert indirect to direct arguments. This is needed to support general existentials in embedded swift. --- .../Sources/Optimizer/Utilities/GenericSpecialization.swift | 4 ++-- include/swift/SILOptimizer/Utils/Generics.h | 2 ++ lib/SILOptimizer/Utils/Generics.cpp | 6 ++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/GenericSpecialization.swift b/SwiftCompilerSources/Sources/Optimizer/Utilities/GenericSpecialization.swift index ef8dbc679afc8..d402190c05a4a 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/GenericSpecialization.swift +++ b/SwiftCompilerSources/Sources/Optimizer/Utilities/GenericSpecialization.swift @@ -148,7 +148,7 @@ func specializeWitnessTable(for conformance: Conformance, _ context: ModulePassC guard !methodSubs.conformances.contains(where: {!$0.isValid}), context.loadFunction(function: origMethod, loadCalleesRecursively: true), let specializedMethod = context.specialize(function: origMethod, for: methodSubs, - convertIndirectToDirect: true, isMandatory: true) + convertIndirectToDirect: false, isMandatory: true) else { return origEntry } @@ -215,7 +215,7 @@ private func specializeDefaultMethods(for conformance: Conformance, guard !methodSubs.conformances.contains(where: {!$0.isValid}), context.loadFunction(function: origMethod, loadCalleesRecursively: true), let specializedMethod = context.specialize(function: origMethod, for: methodSubs, - convertIndirectToDirect: true, isMandatory: true) + convertIndirectToDirect: false, isMandatory: true) else { return origEntry } diff --git a/include/swift/SILOptimizer/Utils/Generics.h b/include/swift/SILOptimizer/Utils/Generics.h index 8b320ce92da27..11c914d46c8d6 100644 --- a/include/swift/SILOptimizer/Utils/Generics.h +++ b/include/swift/SILOptimizer/Utils/Generics.h @@ -216,7 +216,9 @@ class ReabstractionInfo { ReabstractionInfo(CanSILFunctionType substitutedType, SILDeclRef methodDecl, + bool convertIndirectToDirect, SILModule &M) : + ConvertIndirectToDirect(convertIndirectToDirect), SubstitutedType(substitutedType), methodDecl(methodDecl), M(&M), isWholeModule(M.isWholeModule()) {} diff --git a/lib/SILOptimizer/Utils/Generics.cpp b/lib/SILOptimizer/Utils/Generics.cpp index 9ef5e942708cf..b12bf8c36a0b8 100644 --- a/lib/SILOptimizer/Utils/Generics.cpp +++ b/lib/SILOptimizer/Utils/Generics.cpp @@ -2413,7 +2413,8 @@ bool swift::specializeClassMethodInst(ClassMethodInst *cm) { SILType substitutedType = funcTy.substGenericArgs(m, subs, TypeExpansionContext::minimal()); - ReabstractionInfo reInfo(substitutedType.getAs(), cm->getMember(), m); + ReabstractionInfo reInfo(substitutedType.getAs(), cm->getMember(), + /*convertIndirectToDirect=*/ true, m); reInfo.createSubstitutedAndSpecializedTypes(); CanSILFunctionType finalFuncTy = reInfo.getSpecializedType(); SILType finalSILTy = SILType::getPrimitiveObjectType(finalFuncTy); @@ -2465,7 +2466,8 @@ bool swift::specializeWitnessMethodInst(WitnessMethodInst *wm) { SILType substitutedType = funcTy.substGenericArgs(m, subs, TypeExpansionContext::minimal()); - ReabstractionInfo reInfo(substitutedType.getAs(), wm->getMember(), m); + ReabstractionInfo reInfo(substitutedType.getAs(), wm->getMember(), + /*convertIndirectToDirect=*/ false, m); reInfo.createSubstitutedAndSpecializedTypes(); CanSILFunctionType finalFuncTy = reInfo.getSpecializedType(); SILType finalSILTy = SILType::getPrimitiveObjectType(finalFuncTy); From b218d9ab5ccabc63541f0638283e9de436d76c70 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Tue, 11 Nov 2025 10:19:13 +0100 Subject: [PATCH 06/11] SIL: add some APIs for `InitExistentialAddrInst` * `var conformances: ConformanceArray` * `var formalConcreteType: CanonicalType` --- SwiftCompilerSources/Sources/SIL/Instruction.swift | 4 ++++ include/swift/SIL/SILBridging.h | 1 + include/swift/SIL/SILBridgingImpl.h | 5 +++++ 3 files changed, 10 insertions(+) diff --git a/SwiftCompilerSources/Sources/SIL/Instruction.swift b/SwiftCompilerSources/Sources/SIL/Instruction.swift index 8057aeb6d7266..e7484232c58c7 100644 --- a/SwiftCompilerSources/Sources/SIL/Instruction.swift +++ b/SwiftCompilerSources/Sources/SIL/Instruction.swift @@ -883,6 +883,10 @@ class InitExistentialAddrInst : SingleValueInstruction, UnaryInstruction { public var conformances: ConformanceArray { ConformanceArray(bridged: bridged.InitExistentialAddrInst_getConformances()) } + + public var formalConcreteType: CanonicalType { + CanonicalType(bridged: bridged.InitExistentialAddrInst_getFormalConcreteType()) + } } final public diff --git a/include/swift/SIL/SILBridging.h b/include/swift/SIL/SILBridging.h index a3445fe32f6cb..f15ae2db9f79d 100644 --- a/include/swift/SIL/SILBridging.h +++ b/include/swift/SIL/SILBridging.h @@ -798,6 +798,7 @@ struct BridgedInstruction { 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; + SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedCanType InitExistentialAddrInst_getFormalConcreteType() 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 750c53e38b45f..523aba833fc0a 100644 --- a/include/swift/SIL/SILBridgingImpl.h +++ b/include/swift/SIL/SILBridgingImpl.h @@ -1252,6 +1252,11 @@ BridgedCanType BridgedInstruction::InitExistentialRefInst_getFormalConcreteType( BridgedConformanceArray BridgedInstruction::InitExistentialAddrInst_getConformances() const { return {getAs()->getConformances()}; } + +BridgedCanType BridgedInstruction::InitExistentialAddrInst_getFormalConcreteType() const { + return getAs()->getFormalConcreteType(); +} + bool BridgedInstruction::OpenExistentialAddr_isImmutable() const { switch (getAs()->getAccessKind()) { case swift::OpenedExistentialAccess::Immutable: return true; From 1486d009b0470a301ebda8ec5b940302ce86d54b Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Thu, 20 Nov 2025 19:45:23 +0100 Subject: [PATCH 07/11] AST: add `var ProtocolDecl.isMarkerProtocol` --- SwiftCompilerSources/Sources/AST/Declarations.swift | 1 + include/swift/AST/ASTBridging.h | 1 + include/swift/AST/ASTBridgingImpl.h | 4 ++++ 3 files changed, 6 insertions(+) diff --git a/SwiftCompilerSources/Sources/AST/Declarations.swift b/SwiftCompilerSources/Sources/AST/Declarations.swift index 2041d32fd22de..780ed1aa23314 100644 --- a/SwiftCompilerSources/Sources/AST/Declarations.swift +++ b/SwiftCompilerSources/Sources/AST/Declarations.swift @@ -76,6 +76,7 @@ final public class ClassDecl: NominalTypeDecl { final public class ProtocolDecl: NominalTypeDecl { public var requiresClass: Bool { bridged.ProtocolDecl_requiresClass() } + public var isMarkerProtocol: Bool { bridged.ProtocolDecl_isMarkerProtocol() } } final public class BuiltinTupleDecl: NominalTypeDecl {} diff --git a/include/swift/AST/ASTBridging.h b/include/swift/AST/ASTBridging.h index e529082f034a8..8a692d52aa65a 100644 --- a/include/swift/AST/ASTBridging.h +++ b/include/swift/AST/ASTBridging.h @@ -335,6 +335,7 @@ struct BridgedDeclObj { SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedASTType Class_getSuperclass() const; SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedDeclObj Class_getDestructor() const; BRIDGED_INLINE bool ProtocolDecl_requiresClass() const; + BRIDGED_INLINE bool ProtocolDecl_isMarkerProtocol() const; BRIDGED_INLINE bool AbstractFunction_isOverridden() const; BRIDGED_INLINE bool Destructor_isIsolated() const; BRIDGED_INLINE bool EnumElementDecl_hasAssociatedValues() const; diff --git a/include/swift/AST/ASTBridgingImpl.h b/include/swift/AST/ASTBridgingImpl.h index aa2bf1dbbd9fd..d892c89e2388b 100644 --- a/include/swift/AST/ASTBridgingImpl.h +++ b/include/swift/AST/ASTBridgingImpl.h @@ -222,6 +222,10 @@ bool BridgedDeclObj::ProtocolDecl_requiresClass() const { return getAs()->requiresClass(); } +bool BridgedDeclObj::ProtocolDecl_isMarkerProtocol() const { + return getAs()->isMarkerProtocol(); +} + bool BridgedDeclObj::AbstractFunction_isOverridden() const { return getAs()->isOverridden(); } From 4191543855e4a9abe6f141283a7fcad4a0078bf2 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Tue, 11 Nov 2025 10:22:47 +0100 Subject: [PATCH 08/11] MandatoryPerformanceOptimizations: specialize witness tables for general existentials --- .../MandatoryPerformanceOptimizations.swift | 8 + test/embedded/general-existentials1.swift | 300 ++++++++++++++++++ 2 files changed, 308 insertions(+) create mode 100644 test/embedded/general-existentials1.swift diff --git a/SwiftCompilerSources/Sources/Optimizer/ModulePasses/MandatoryPerformanceOptimizations.swift b/SwiftCompilerSources/Sources/Optimizer/ModulePasses/MandatoryPerformanceOptimizations.swift index 11616d84335bb..b62f6c5d8c180 100644 --- a/SwiftCompilerSources/Sources/Optimizer/ModulePasses/MandatoryPerformanceOptimizations.swift +++ b/SwiftCompilerSources/Sources/Optimizer/ModulePasses/MandatoryPerformanceOptimizations.swift @@ -143,6 +143,14 @@ private func optimize(function: Function, _ context: FunctionPassContext, _ modu } } + case let initExAddr as InitExistentialAddrInst: + if context.options.enableEmbeddedSwift { + for c in initExAddr.conformances where c.isConcrete && !c.protocol.isMarkerProtocol { + specializeWitnessTable(for: c, moduleContext) + worklist.addWitnessMethods(of: c, moduleContext) + } + } + case let bi as BuiltinInst: switch bi.id { case .BuildOrdinaryTaskExecutorRef, diff --git a/test/embedded/general-existentials1.swift b/test/embedded/general-existentials1.swift new file mode 100644 index 0000000000000..a39b48ab77c1b --- /dev/null +++ b/test/embedded/general-existentials1.swift @@ -0,0 +1,300 @@ +// RUN: %target-run-simple-swift(-enable-experimental-feature Embedded -enable-experimental-feature EmbeddedExistentials -parse-as-library -wmo) | %FileCheck %s +// RUN: %target-run-simple-swift(-enable-experimental-feature Embedded -enable-experimental-feature EmbeddedExistentials -parse-as-library -wmo -O) | %FileCheck %s +// RUN: %target-run-simple-swift(-enable-experimental-feature Embedded -enable-experimental-feature EmbeddedExistentials -parse-as-library -wmo -Osize) | %FileCheck %s + +// REQUIRES: swift_in_compiler +// REQUIRES: executable_test +// REQUIRES: optimized_stdlib +// REQUIRES: swift_feature_Embedded +// REQUIRES: swift_feature_EmbeddedExistentials + +protocol P { + func foo() + func bar() + func predicate() -> Bool +} + +extension P { + public func predicate() -> Bool { true } +} + +struct MyStruct : P { + var i: Int + + func foo() { print("MyStruct.foo: \(i)") } + func bar() { print("MyStruct.bar: \(i)") } +} + +struct LargeStruct : P { + var a: Int + var b: Int + var c: Int + var d: Int + + func foo() { print("LargeStruct.foo: \(a), \(b), \(c), \(d)") } + func bar() { print("LargeStruct.bar: \(a), \(b), \(c), \(d)") } +} + +struct GenericStruct : P { + var t: T + + func foo() { print("GenericStruct.foo: \(t)") } + func bar() { print("GenericStruct.bar: \(t)") } +} + +func test(existential: any P) { + existential.foo() + existential.bar() +} + +/* +public protocol ProtoWithAssocType { + associatedtype T + func foo(t: T) +} + +final public class GenClass: ProtoWithAssocType { + public func foo(t: T) { + print(t) + } +} + +public func createExWithAssocType() -> any ProtoWithAssocType { + return GenClass() +} + +public func callExWithAssocType(_ p: any ProtoWithAssocType) { + p.foo(t: 27) +} + +public protocol Q { + func bar() +} + +public protocol ProtoWithAssocConf { + associatedtype A: Q + func foo() -> A +} + +public class GenClass2: Q { + final var t: T + + init(t : T) { self.t = t } + + public func bar() { + print("bar") + } +} + +public class DerivedFromGenClass2: GenClass2 { + init() { super.init(t: 42) } + + public override func bar() { + print("derived-bar") + } +} + +final public class GenClass3: ProtoWithAssocConf { + public func foo() -> GenClass2 { + print("foo") + return GenClass2(t: 27) + } +} + +final public class OtherClass: ProtoWithAssocConf { + public func foo() -> GenClass2 { + print("other-foo") + return DerivedFromGenClass2() + } +} + + +public func createExWithAssocConf() -> any ProtoWithAssocConf { + return GenClass3() +} + +public func callExWithAssocConf(_ p: any ProtoWithAssocConf) { + let x = p.foo() + x.bar() +} + +public class Base: P { + public func foo() { print("Base.foo()") } + public func bar() { print("Base.bar()") } +} + +public class Derived1: Base { + public override func foo() { print("Derived1.foo()") } + public override func bar() { print("Derived1.bar()") } +} + +public class Derived2: Base { + public override func foo() { print("Derived2.foo()") } + public override func bar() { print("Derived2.bar()") } +} + +public func takes_p1(_ p: P1) { + p.normal() +} + +public protocol P1 { + func normal() +} + +public protocol P2 { + func foo() +} + +public class ConditionalConformanceBase { + final var a: A + + init(a: A) { self.a = a } +} + +extension ConditionalConformanceBase: P1 where A: P2 { + public func normal() { + a.foo() + } +} + +public class ConditionalConformanceDerived: ConditionalConformanceBase { + init(t: T) { super.init(a: t) } +} + + +public func testConditionalConformance(t: T) { + takes_p1(ConditionalConformanceDerived(t: t)) +} + + +struct S: P2 { + var i: Int + + func foo() { + print(i) + } +} + +protocol Q3 { + func bar() +} + +protocol P3 { + associatedtype T: Q3 + + var t: T { get } + + func foo() +} + +extension P3 { + func foo() { + t.bar() + } +} + +struct C3: P3 { + var t: T + + + init(t: T) { self.t = t } +} + +struct S3: Q3 { + var x: I + + func bar() { + print(x) + } +} + +@inline(never) +func testP3() -> any P3 { + return C3(t: S3(x: 102)) +} + +protocol P4 { + associatedtype T: Q + + var t: T { get } + + func foo() +} + +extension P4 { + func foo() { + t.bar() + } +} + +struct C4: P4 { + var t: T + + + init(t: T) { self.t = t } +} + +struct K4: Q { + var x: I + + init(x: I) { self.x = x } + + func bar() { + print(x) + } +} + +@inline(never) +func testP4() -> any P4 { + return C4(t: K4(x: 437)) +} +*/ + +@main +struct Main { + static func main() { + test(existential: MyStruct(i: 27)) + // CHECK: MyStruct.foo: 27 + // CHECK: MyStruct.bar: 27 + + test(existential: LargeStruct(a: 10, b: 11, c: 12, d: 13)) + // CHECK: LargeStruct.foo: 10, 11, 12, 13 + // CHECK: LargeStruct.bar: 10, 11, 12, 13 + + test(existential: GenericStruct(t: 28)) + // CHECK: GenericStruct.foo: 28 + // CHECK: GenericStruct.bar: 28 + + /* + callExWithAssocType(createExWithAssocType()) + // xCHECK: 27 + + callExWithAssocConf(createExWithAssocConf()) + // xCHECK: foo + // xCHECK: bar + + callExWithAssocConf(OtherClass()) + // xCHECK: other-foo + // xCHECK: derived-bar + + test(existential: Derived1()) + // xCHECK: Derived1.foo() + // xCHECK: Derived1.bar() + + test(existential: Derived2()) + // xCHECK: Derived2.foo() + // xCHECK: Derived2.bar() + + testConditionalConformance(t: S(i: 27)) + // xCHECK: 27 + + testP3().foo() + // xCHECK: 102 + + testP4().foo() + // xCHECK: 437 + */ + } +} + + From ffca3b462392c21af02d586ed7816a1f35255a1e Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Thu, 20 Nov 2025 13:10:44 -0800 Subject: [PATCH 09/11] [embedded] Fix associate type conformances involving specialized conformances --- lib/IRGen/GenProto.cpp | 4 +++ test/embedded/existential.swift | 49 +++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/lib/IRGen/GenProto.cpp b/lib/IRGen/GenProto.cpp index 4824098395ade..7be207a615d2a 100644 --- a/lib/IRGen/GenProto.cpp +++ b/lib/IRGen/GenProto.cpp @@ -1751,6 +1751,10 @@ class AccessorConformanceInfo : public ConformanceInfo { #endif auto typeWitness = Conformance.getTypeWitness(assocType); + if (langOpts.hasFeature(Feature::EmbeddedExistentials) && + SILWT->isSpecialized()) { + typeWitness = entry.getAssociatedTypeWitness().Witness; + } if (IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) { // In Embedded Swift associated type witness point to the metadata. diff --git a/test/embedded/existential.swift b/test/embedded/existential.swift index d0852940ec93d..e1f6cc2314668 100644 --- a/test/embedded/existential.swift +++ b/test/embedded/existential.swift @@ -302,6 +302,47 @@ func test13(_ p: any P4) { p.t.printit() } +struct GenericConformer : ValuePrinter { + var t: T? + var l = (0, 1, 2, 3) + + init(_ t: T) { self.t = t } + + func printValue() { + print("GenericConformer \(l.0) \(l.1) \(l.2) \(l.3)") + } + + mutating func mutate() { + l = (4, 5, 6, 7) + } +} + +struct GenericConformerWithAssoc : WithAssoc { + var g : GenericConformer + + init( _ g: T) { + self.g = GenericConformer(g) + } + + func a() -> GenericConformer { + return g + } +} + +func test14(_ p: any ValuePrinter) { + print("test any ValuePrinter") + p.printValue() + var p2 = p + p2.mutate() + p2.printValue() +} + +func test15(_ p: any WithAssoc) { + print("test any WithAssoc") + let l = p.a() + l.printValue() +} + @main struct Main { static func main() { @@ -423,5 +464,13 @@ struct Main { test13(P4Conformer()) // OUTPUT: test13 // OUTPUT: QConformer 3 +// OUTPUT-NOT: deinit + test14(GenericConformer(1)) +// OUTPUT: test any ValuePrinter +// OUTPUT: GenericConformer 0 1 2 3 +// OUTPUT: GenericConformer 4 5 6 7 + test15(GenericConformerWithAssoc(1)) +// OUTPUT: test any WithAssoc +// OUTPUT: GenericConformer 0 1 2 3 } } From a5aa929d1f9e59a9a8da7cd3f6ad93d0260ec0f9 Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Fri, 21 Nov 2025 06:37:47 -0800 Subject: [PATCH 10/11] [embedded] Existentials: enable tests that now pass --- test/embedded/general-existentials1.swift | 24 ++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/test/embedded/general-existentials1.swift b/test/embedded/general-existentials1.swift index a39b48ab77c1b..ee5cb235899a2 100644 --- a/test/embedded/general-existentials1.swift +++ b/test/embedded/general-existentials1.swift @@ -47,7 +47,6 @@ func test(existential: any P) { existential.bar() } -/* public protocol ProtoWithAssocType { associatedtype T func foo(t: T) @@ -133,6 +132,7 @@ public class Derived2: Base { public override func bar() { print("Derived2.bar()") } } +/* public func takes_p1(_ p: P1) { p.normal() } @@ -265,25 +265,27 @@ struct Main { // CHECK: GenericStruct.foo: 28 // CHECK: GenericStruct.bar: 28 - /* callExWithAssocType(createExWithAssocType()) - // xCHECK: 27 + // CHECK: 27 callExWithAssocConf(createExWithAssocConf()) - // xCHECK: foo - // xCHECK: bar + // CHECK: foo + // CHECK: bar callExWithAssocConf(OtherClass()) - // xCHECK: other-foo - // xCHECK: derived-bar + // CHECK: other-foo + // CHECK: derived-bar test(existential: Derived1()) - // xCHECK: Derived1.foo() - // xCHECK: Derived1.bar() + // CHECK: Derived1.foo() + // CHECK: Derived1.bar() + test(existential: Derived2()) - // xCHECK: Derived2.foo() - // xCHECK: Derived2.bar() + // CHECK: Derived2.foo() + // CHECK: Derived2.bar() + + /* testConditionalConformance(t: S(i: 27)) // xCHECK: 27 From 16206fad9f271fa1642f66057c4a515c5f4c9540 Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Fri, 21 Nov 2025 06:48:51 -0800 Subject: [PATCH 11/11] [embedded] Enable all tests in general-existentials1.swift --- test/embedded/general-existentials1.swift | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/test/embedded/general-existentials1.swift b/test/embedded/general-existentials1.swift index ee5cb235899a2..fb62a0aa9edcc 100644 --- a/test/embedded/general-existentials1.swift +++ b/test/embedded/general-existentials1.swift @@ -132,7 +132,6 @@ public class Derived2: Base { public override func bar() { print("Derived2.bar()") } } -/* public func takes_p1(_ p: P1) { p.normal() } @@ -248,7 +247,6 @@ struct K4: Q { func testP4() -> any P4 { return C4(t: K4(x: 437)) } -*/ @main struct Main { @@ -285,17 +283,15 @@ struct Main { // CHECK: Derived2.foo() // CHECK: Derived2.bar() - /* testConditionalConformance(t: S(i: 27)) - // xCHECK: 27 + // CHECK: 27 testP3().foo() - // xCHECK: 102 + // CHECK: 102 testP4().foo() - // xCHECK: 437 - */ + // CHECK: 437 } }