From 2a1f1e6fa43619d0d22a8bf6d2fbb7cfc974b72b Mon Sep 17 00:00:00 2001 From: Kuba Mracek Date: Mon, 17 Nov 2025 19:50:26 -0800 Subject: [PATCH 01/11] SE-0492: Metatypes and existential metatypes --- .../Optimizer/Utilities/OptUtils.swift | 13 +++++ include/swift/SIL/SILBuilder.h | 9 +++ include/swift/SIL/SILInstruction.h | 8 +++ lib/IRGen/GenConstant.cpp | 21 +++++++ lib/IRGen/GenExistential.cpp | 34 +++++++++++ lib/IRGen/GenExistential.h | 25 ++++---- lib/SIL/IR/SILInstructions.cpp | 18 ++++++ lib/Sema/LegalConstExprVerifier.cpp | 21 +++++++ test/ConstValues/SectionIR.swift | 15 ++++- test/ConstValues/SectionSyntactic.swift | 3 + test/SILOptimizer/static_init_metatypes.swift | 57 +++++++++++++++++++ 11 files changed, 212 insertions(+), 12 deletions(-) create mode 100644 test/SILOptimizer/static_init_metatypes.swift diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift b/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift index 053b71c9535db..32b0a46db521d 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift +++ b/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift @@ -503,6 +503,19 @@ extension Instruction { } case let gvi as GlobalValueInst: return context.canMakeStaticObjectReadOnly(objectType: gvi.type) + + // Metatypes (and upcasts of them to existentials) can be used as static initializers for SE-0492, as long as they + // are not generic or resilient + case let mti as MetatypeInst: + if !mti.type.hasTypeParameter, + let nominal = mti.type.canonicalType.instanceTypeOfMetatype.nominal, + !nominal.isResilient(in: mti.parentFunction) { + return true + } + return false + case let iemi as InitExistentialMetatypeInst: + return !iemi.type.hasTypeParameter + case is StructInst, is TupleInst, is EnumInst, diff --git a/include/swift/SIL/SILBuilder.h b/include/swift/SIL/SILBuilder.h index dde860ce12daa..ee5a3ccd1376f 100644 --- a/include/swift/SIL/SILBuilder.h +++ b/include/swift/SIL/SILBuilder.h @@ -2155,6 +2155,11 @@ class SILBuilder { createInitExistentialMetatype(SILLocation Loc, SILValue metatype, SILType existentialType, ArrayRef conformances) { + if (isInsertingIntoGlobal()) { + return insert(InitExistentialMetatypeInst::create( + getSILDebugLocation(Loc), existentialType, metatype, conformances, + getModule())); + } return insert(InitExistentialMetatypeInst::create( getSILDebugLocation(Loc), existentialType, metatype, conformances, &getFunction())); @@ -2289,6 +2294,10 @@ class SILBuilder { } MetatypeInst *createMetatype(SILLocation Loc, SILType Metatype) { + if (isInsertingIntoGlobal()) { + return insert(MetatypeInst::create(getSILDebugLocation(Loc), Metatype, + getModule())); + } return insert(MetatypeInst::create(getSILDebugLocation(Loc), Metatype, &getFunction())); } diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index a24c08d0e3fd9..84c017c5990af 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -7509,6 +7509,9 @@ class MetatypeInst final ArrayRef TypeDependentOperands) : NullaryInstructionWithTypeDependentOperandsBase(DebugLoc, TypeDependentOperands, Metatype) {} + + static MetatypeInst *create(SILDebugLocation DebugLoc, SILType Metatype, + SILModule &Mod); static MetatypeInst *create(SILDebugLocation DebugLoc, SILType Metatype, SILFunction *F); @@ -8186,6 +8189,11 @@ class InitExistentialMetatypeInst final ArrayRef TypeDependentOperands, ArrayRef conformances); + static InitExistentialMetatypeInst * + create(SILDebugLocation DebugLoc, SILType existentialMetatypeType, + SILValue metatype, ArrayRef conformances, + SILModule &mod); + static InitExistentialMetatypeInst * create(SILDebugLocation DebugLoc, SILType existentialMetatypeType, SILValue metatype, ArrayRef conformances, diff --git a/lib/IRGen/GenConstant.cpp b/lib/IRGen/GenConstant.cpp index 7271525557751..5e6b89a92e4eb 100644 --- a/lib/IRGen/GenConstant.cpp +++ b/lib/IRGen/GenConstant.cpp @@ -20,6 +20,7 @@ #include "Explosion.h" #include "GenConstant.h" #include "GenEnum.h" +#include "GenExistential.h" #include "GenIntegerLiteral.h" #include "GenStruct.h" #include "GenTuple.h" @@ -31,9 +32,11 @@ #include "swift/IRGen/Linking.h" #include "swift/Basic/Assertions.h" #include "swift/Basic/Range.h" +#include "swift/SIL/SILInstruction.h" #include "swift/SIL/SILModule.h" #include "llvm/Analysis/ConstantFolding.h" #include "llvm/Support/BLAKE3.h" +#include "llvm/Support/ErrorHandling.h" using namespace swift; using namespace irgen; @@ -414,6 +417,24 @@ Explosion irgen::emitConstantValue(IRGenModule &IGM, SILValue operand, assert(ti.isFixedSize(expansion)); Address addr = IGM.getAddrOfSILGlobalVariable(var, ti, NotForDefinition); return addr.getAddress(); + } else if (auto *mti = dyn_cast(operand)) { + auto metaTy = mti->getType().castTo(); + auto type = metaTy.getInstanceType(); + return IGM.getAddrOfTypeMetadata(type); + } else if (auto *iemi = dyn_cast(operand)) { + auto *mti = + dyn_cast(iemi->getOperand().getDefiningInstruction()); + ASSERT(mti != nullptr && "couldn't constant fold initializer expression"); + + auto metaTy = mti->getType().castTo(); + auto type = metaTy.getInstanceType(); + llvm::Constant *metatype = IGM.getAddrOfTypeMetadata(type); + + Explosion result; + emitExistentialMetatypeContainer(IGM, result, iemi->getType(), metatype, + iemi->getOperand()->getType(), + iemi->getConformances()); + return result; } else if (auto *atp = dyn_cast(operand)) { auto *val = emitConstantValue(IGM, atp->getOperand()).claimNextConstant(); return val; diff --git a/lib/IRGen/GenExistential.cpp b/lib/IRGen/GenExistential.cpp index 89e017d922d96..3f6b9c1dd04d8 100644 --- a/lib/IRGen/GenExistential.cpp +++ b/lib/IRGen/GenExistential.cpp @@ -2033,6 +2033,40 @@ void irgen::emitExistentialMetatypeContainer(IRGenFunction &IGF, }); } +/// Emit an existential metatype container from a metatype value as an +/// explosion of llvm::Constant values (suitable for static initialization). +void irgen::emitExistentialMetatypeContainer( + IRGenModule &IGM, Explosion &out, SILType outType, llvm::Constant *metatype, + SILType metatypeType, ArrayRef conformances) { + assert(outType.is()); + auto &destTI = IGM.getTypeInfo(outType).as(); + out.add(metatype); + + auto srcType = metatypeType.castTo().getInstanceType(); + auto destType = outType.castTo().getInstanceType(); + while (auto destMetatypeType = dyn_cast(destType)) { + destType = destMetatypeType.getInstanceType(); + srcType = cast(srcType).getInstanceType(); + } + + // Emit the witness table pointers as constants. + for (auto protocol : destTI.getStoredProtocols()) { + // Find the corresponding conformance + ProtocolConformanceRef conformance; + for (auto conf : conformances) { + if (conf.getProtocol() == protocol) { + conformance = conf; + break; + } + } + assert(conformance.isConcrete() && "missing conformance"); + + // Emit witness table constant + auto table = IGM.getAddrOfWitnessTable(conformance.getConcrete()); + out.add(table); + } +} + void irgen::emitMetatypeOfOpaqueExistential(IRGenFunction &IGF, Address buffer, SILType type, Explosion &out) { assert(type.isExistentialType()); diff --git a/lib/IRGen/GenExistential.h b/lib/IRGen/GenExistential.h index 32af225a38545..4159c37d61e3d 100644 --- a/lib/IRGen/GenExistential.h +++ b/lib/IRGen/GenExistential.h @@ -34,6 +34,7 @@ namespace irgen { class Address; class Explosion; class IRGenFunction; + class IRGenModule; /// Emit the metadata and witness table initialization for an allocated /// opaque existential container. @@ -44,16 +45,20 @@ namespace irgen { SILType loweredSrcType, ArrayRef conformances); - /// Emit an existential metatype container from a metatype value - /// as an explosion. - void emitExistentialMetatypeContainer(IRGenFunction &IGF, - Explosion &out, - SILType outType, - llvm::Value *metatype, - SILType metatypeType, - ArrayRef conformances); - - + /// Emit an existential metatype container from a metatype value as an + /// explosion. + void emitExistentialMetatypeContainer( + IRGenFunction &IGF, Explosion &out, SILType outType, + llvm::Value *metatype, SILType metatypeType, + ArrayRef conformances); + + /// Emit an existential metatype container from a metatype value as an + /// explosion of llvm::Constant values (suitable for static initialization). + void emitExistentialMetatypeContainer( + IRGenModule &IGM, Explosion &out, SILType outType, + llvm::Constant *metatype, SILType metatypeType, + ArrayRef conformances); + /// Emit a class existential container from a class instance value /// as an explosion. void emitClassExistentialContainer(IRGenFunction &IGF, diff --git a/lib/SIL/IR/SILInstructions.cpp b/lib/SIL/IR/SILInstructions.cpp index f6abf477dbcfb..eac25389be153 100644 --- a/lib/SIL/IR/SILInstructions.cpp +++ b/lib/SIL/IR/SILInstructions.cpp @@ -2328,6 +2328,17 @@ InitExistentialMetatypeInst::InitExistentialMetatypeInst( getTrailingObjects()); } +InitExistentialMetatypeInst *InitExistentialMetatypeInst::create( + SILDebugLocation Loc, SILType existentialMetatypeType, SILValue metatype, + ArrayRef conformances, SILModule &M) { + unsigned size = totalSizeToAlloc( + 1, conformances.size()); + void *buffer = M.allocateInst(size, alignof(InitExistentialMetatypeInst)); + return ::new (buffer) InitExistentialMetatypeInst( + Loc, existentialMetatypeType, metatype, + {}, conformances); +} + InitExistentialMetatypeInst *InitExistentialMetatypeInst::create( SILDebugLocation Loc, SILType existentialMetatypeType, SILValue metatype, ArrayRef conformances, SILFunction *F) { @@ -2738,6 +2749,13 @@ CheckedCastBranchInst *CheckedCastBranchInst::create( Target2Count, forwardingOwnershipKind, preservesOwnership); } +MetatypeInst *MetatypeInst::create(SILDebugLocation Loc, SILType Ty, + SILModule &Mod) { + auto Size = totalSizeToAlloc(1); + auto Buffer = Mod.allocateInst(Size, alignof(MetatypeInst)); + return ::new (Buffer) MetatypeInst(Loc, Ty, {}); +} + MetatypeInst *MetatypeInst::create(SILDebugLocation Loc, SILType Ty, SILFunction *F) { SILModule &Mod = F->getModule(); diff --git a/lib/Sema/LegalConstExprVerifier.cpp b/lib/Sema/LegalConstExprVerifier.cpp index 62f3c611def0c..9b4055471c83e 100644 --- a/lib/Sema/LegalConstExprVerifier.cpp +++ b/lib/Sema/LegalConstExprVerifier.cpp @@ -20,8 +20,10 @@ #include "TypeChecker.h" #include "swift/AST/ASTContext.h" #include "swift/AST/ASTWalker.h" +#include "swift/AST/Expr.h" #include "swift/AST/ParameterList.h" #include "swift/AST/SemanticAttrs.h" +#include "swift/AST/Type.h" #include "swift/Basic/Assertions.h" using namespace swift; @@ -343,6 +345,25 @@ checkSupportedWithSectionAttribute(const Expr *expr, expressionsToCheck.push_back(identityExpr->getSubExpr()); continue; } + + // Upcasts of metatypes to existential metatypes (e.g. Any.Type) + if (const ErasureExpr *erasureExpr = dyn_cast(expr)) { + if (const DotSelfExpr *dotSelfExpr = dyn_cast(erasureExpr->getSubExpr())) { + if (const TypeExpr *typeExpr = dyn_cast(dotSelfExpr->getSubExpr())) { + auto baseType = typeExpr->getType(); + if (baseType && baseType->is()) { + auto instanceType = baseType->getMetatypeInstanceType(); + if (auto nominal = instanceType->getNominalOrBoundGenericNominal()) { + // Allow non-generic, non-resilient types + if (!nominal->isGeneric() && !nominal->isResilient()) { + continue; + } + } + } + } + } + return std::make_pair(expr, TypeExpression); + } // Function calls and constructors are not allowed if (isa(expr)) diff --git a/test/ConstValues/SectionIR.swift b/test/ConstValues/SectionIR.swift index a981b2de77806..81349d3a885ec 100644 --- a/test/ConstValues/SectionIR.swift +++ b/test/ConstValues/SectionIR.swift @@ -39,8 +39,14 @@ struct W { @section("mysection") static let closure7: @convention(c) (Int) -> Int = { x in x * 2 } } -// metatypes - TODO -//@section("mysection") let metatype1 = Int.self +struct S: Hashable, Sendable {} + +// metatypes +@section("mysection") let metatype1 = Int.self +@section("mysection") let metatype2: Any.Type = Int.self +@section("mysection") let metatype3: Any.Type = S.self +@section("mysection") let metatype4: any (Hashable & Sendable).Type = Int.self +@section("mysection") let metatype5: any (Hashable & Sendable).Type = S.self // tuples @section("mysection") let tuple1 = (1, 2, 3, 2.718, true) // ok @@ -76,6 +82,11 @@ struct W { // CHECK: @"$s9SectionIR8closure6yS2iXCvp" = {{.*}}constant ptr @"$s9SectionIR8closure6yS2iXCvpfiS2icfU_To{{(.ptrauth)?}}", section "mysection" // CHECK: @"$s9SectionIR1WV8closure7yS2iXCvpZ" = {{.*}}constant ptr @"$s9SectionIR1WV8closure7yS2iXCvpZfiS2icfU_To{{(.ptrauth)?}}", section "mysection" +// CHECK: @"$s9SectionIR9metatype2ypXpvp" = {{.*}}constant ptr @"$sSiN", section "mysection" +// CHECK: @"$s9SectionIR9metatype3ypXpvp" = {{.*}}constant ptr getelementptr inbounds (<{ ptr, ptr, {{i64|i32}}, ptr }>, ptr @"$s9SectionIR1SVMf", i32 0, i32 2), section "mysection" +// CHECK: @"$s9SectionIR9metatype4SH_s8SendablepXpvp" = {{.*}}constant <{ ptr, ptr }> <{ ptr @"$sSiN", ptr @"$sSiSHsWP" }>, section "mysection" +// CHECK: @"$s9SectionIR9metatype5SH_s8SendablepXpvp" = {{.*}}constant <{ ptr, ptr }> <{ ptr getelementptr inbounds (<{ ptr, ptr, {{i64|i32}}, ptr }>, ptr @"$s9SectionIR1SVMf", i32 0, i32 2), ptr @"$s9SectionIR1SVSHAAWP" }>, section "mysection" + // CHECK: @"$s9SectionIR6tuple1Si_S2iSdSbtvp" = {{.*}}constant <{ %TSi, %TSi, %TSi, {{.*}} }> <{ %TSi <{ {{i64|i32}} 1 }>, %TSi <{ {{i64|i32}} 2 }>, %TSi <{ {{i64|i32}} 3 }>, {{.*}} }>, section "mysection" // CHECK: @"$s9SectionIR6tuple2Si_SfSbtvp" = {{.*}}constant <{ %TSi, %TSf, %TSb }> <{ %TSi <{ {{i64|i32}} 42 }>, %TSf <{ float 0x40091EB860000000 }>, %TSb zeroinitializer }>, section "mysection" // CHECK: @"$s9SectionIR6tuple3Siyc_SSSictvp" = {{.*}}constant <{ %swift.function, %swift.function }> <{ %swift.function { ptr @"$s9SectionIR3fooSiyF{{.*}}", ptr null }, %swift.function { ptr @"$s9SectionIR3bar1xSSSi_tF{{.*}}", ptr null } }>, section "mysection" diff --git a/test/ConstValues/SectionSyntactic.swift b/test/ConstValues/SectionSyntactic.swift index ce2165a25cc36..6fb4204998fcf 100644 --- a/test/ConstValues/SectionSyntactic.swift +++ b/test/ConstValues/SectionSyntactic.swift @@ -121,6 +121,9 @@ enum E { case a } // metatypes @section("mysection") let metatype1 = Int.self // ok +@section("mysection") let metatype2: Any.Type = Int.self // ok +@section("mysection") let metatype3: any (Hashable).Type = Int.self // ok +@section("mysection") let metatype4: any (Hashable & Sendable).Type = Int.self // ok // invalid metatype references @section("mysection") let invalidMetatype1 = Int.self.self diff --git a/test/SILOptimizer/static_init_metatypes.swift b/test/SILOptimizer/static_init_metatypes.swift new file mode 100644 index 0000000000000..e8c23905c6dff --- /dev/null +++ b/test/SILOptimizer/static_init_metatypes.swift @@ -0,0 +1,57 @@ +// RUN: %target-swift-frontend %s -parse-as-library -module-name=test -emit-sil | %FileCheck %s +// RUN: %target-swift-frontend %s -parse-as-library -O -module-name=test -emit-sil | %FileCheck %s + +struct S: Hashable, Sendable {} + +public let metatype1 = Int.self +public let metatype2: Any.Type = Int.self +public let metatype3: Any.Type = S.self +public let metatype4: any (Hashable & Sendable).Type = Int.self +public let metatype5: any (Hashable & Sendable).Type = S.self +public let metatype6: Any.Type = Array.self +public let metatype7: any (Hashable & Sendable).Type = Array.self + +// CHECK: sil_global [let] @$s4test9metatype1Simvp : $@thin Int.Type = { +// CHECK-NEXT: %initval = metatype $@thin Int.Type +// CHECK-NEXT: } + +// CHECK: sil_global [let] @$s4test9metatype2ypXpvp : $@thick any Any.Type = { +// CHECK-NEXT: %0 = metatype $@thick Int.Type +// CHECK-NEXT: %initval = init_existential_metatype %0, $@thick any Any.Type +// CHECK-NEXT: } + +// CHECK: sil_global [let] @$s4test9metatype3ypXpvp : $@thick any Any.Type = { +// CHECK-NEXT: %0 = metatype $@thick S.Type +// CHECK-NEXT: %initval = init_existential_metatype %0, $@thick any Any.Type +// CHECK-NEXT: } + +// CHECK: sil_global [let] @$s4test9metatype4SH_s8SendablepXpvp : $@thick any (Hashable & Sendable).Type = { +// CHECK-NEXT: %0 = metatype $@thick Int.Type +// CHECK-NEXT: %initval = init_existential_metatype %0, $@thick any (Hashable & Sendable).Type +// CHECK-NEXT: } + +// CHECK: sil_global [let] @$s4test9metatype5SH_s8SendablepXpvp : $@thick any (Hashable & Sendable).Type = { +// CHECK-NEXT: %0 = metatype $@thick S.Type +// CHECK-NEXT: %initval = init_existential_metatype %0, $@thick any (Hashable & Sendable).Type +// CHECK-NEXT: } + +// CHECK: sil_global [let] @$s4test9metatype6ypXpvp : $@thick any Any.Type = { +// CHECK-NEXT: %0 = metatype $@thick Array.Type +// CHECK-NEXT: %initval = init_existential_metatype %0, $@thick any Any.Type +// CHECK-NEXT: } + +// CHECK: sil_global [let] @$s4test9metatype7SH_s8SendablepXpvp : $@thick any (Hashable & Sendable).Type = { +// CHECK-NEXT: %0 = metatype $@thick Array.Type +// CHECK-NEXT: %initval = init_existential_metatype %0, $@thick any (Hashable & Sendable).Type +// CHECK-NEXT: } + +// These are not constant folded and stay as lazily-initialized +public let metatypeA: Any.Type = (Bool.random() ? Array.self : Array.self).self +public let metatypeB: Any.Type = Mirror.self // resilient + +// CHECK: sil_global private @$s4test9metatypeA_Wz : $Builtin.Word +// CHECK: sil_global [let] @$s4test9metatypeAypXpvp : $@thick any Any.Type +// CHECK: sil_global private @$s4test9metatypeB_Wz : $Builtin.Word +// CHECK: sil_global [let] @$s4test9metatypeBypXpvp : $@thick any Any.Type +// CHECK: sil private [global_init_once_fn] @$s4test9metatypeA_WZ : $@convention(c) (Builtin.RawPointer) -> () { +// CHECK: sil private [global_init_once_fn] @$s4test9metatypeB_WZ : $@convention(c) (Builtin.RawPointer) -> () { From c118b0ac17ab57317915675962d6df19360ddb86 Mon Sep 17 00:00:00 2001 From: Kuba Mracek Date: Mon, 17 Nov 2025 19:51:04 -0800 Subject: [PATCH 02/11] Restrict metatype support in @section to only non-resilient conformances --- .../Sources/AST/Conformance.swift | 17 ++++++++ .../Sources/AST/Declarations.swift | 1 + .../Optimizer/Utilities/OptUtils.swift | 5 ++- .../Sources/SIL/Instruction.swift | 4 ++ include/swift/AST/ASTBridging.h | 2 + include/swift/AST/ASTBridgingImpl.h | 6 +++ include/swift/SIL/SILBridging.h | 1 + include/swift/SIL/SILBridgingImpl.h | 4 ++ lib/Sema/LegalConstExprVerifier.cpp | 40 ++++++++++++++--- test/ConstValues/SectionIR.swift | 12 +++--- test/ConstValues/SectionSyntactic.swift | 16 ++++++- .../optimizable_metatype_references.swift | 43 +++++++++++++++++++ 12 files changed, 137 insertions(+), 14 deletions(-) create mode 100644 test/SILOptimizer/optimizable_metatype_references.swift diff --git a/SwiftCompilerSources/Sources/AST/Conformance.swift b/SwiftCompilerSources/Sources/AST/Conformance.swift index f67c942924c3d..7574ce34d631e 100644 --- a/SwiftCompilerSources/Sources/AST/Conformance.swift +++ b/SwiftCompilerSources/Sources/AST/Conformance.swift @@ -87,6 +87,23 @@ public struct Conformance: CustomStringConvertible, Hashable, NoReflectionChildr assert(isConcrete) return bridged.getAssociatedConformance(assocType.bridged, proto.bridged).conformance } + + // This is a less precise definition of "resilient conformance" because it's + // only used for SIL optimizations -- where it's okay to think a conformance + // is resilient even if IRGen will decide it's not (see + // IRGenModule::isResilientConformance). + public func isResilientConformance(currentModule: ModuleDecl) -> Bool { + let conformanceModule = self.protocol.parentModule + let protocolModule = self.protocol.parentModule + + // If the protocol and the conformance are both in the current module, + // they're not resilient. + if conformanceModule == currentModule && protocolModule == currentModule { + return false + } + + return true + } } public struct ConformanceArray : RandomAccessCollection, CustomReflectable { diff --git a/SwiftCompilerSources/Sources/AST/Declarations.swift b/SwiftCompilerSources/Sources/AST/Declarations.swift index 35ba159eea2c3..13f4b3c4bd3d1 100644 --- a/SwiftCompilerSources/Sources/AST/Declarations.swift +++ b/SwiftCompilerSources/Sources/AST/Declarations.swift @@ -135,6 +135,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() } + public var isInvertible: Bool { bridged.ProtocolDecl_isInvertible() } } final public class BuiltinTupleDecl: NominalTypeDecl {} diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift b/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift index 32b0a46db521d..84176f51399dd 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift +++ b/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift @@ -514,7 +514,10 @@ extension Instruction { } return false case let iemi as InitExistentialMetatypeInst: - return !iemi.type.hasTypeParameter + let isAnyConformanceResilient = iemi.conformances.contains { + !$0.protocol.isInvertible && $0.isResilientConformance(currentModule: context.currentModuleContext) + } + return !iemi.type.hasTypeParameter && !isAnyConformanceResilient case is StructInst, is TupleInst, diff --git a/SwiftCompilerSources/Sources/SIL/Instruction.swift b/SwiftCompilerSources/Sources/SIL/Instruction.swift index 3255c41cd310a..0b835e7c69701 100644 --- a/SwiftCompilerSources/Sources/SIL/Instruction.swift +++ b/SwiftCompilerSources/Sources/SIL/Instruction.swift @@ -944,6 +944,10 @@ class OpenExistentialBoxValueInst : SingleValueInstruction, UnaryInstruction {} final public class InitExistentialMetatypeInst : SingleValueInstruction, UnaryInstruction { public var metatype: Value { operand.value } + + public var conformances: ConformanceArray { + ConformanceArray(bridged: bridged.InitExistentialMetatypeInst_getConformances()) + } } final public diff --git a/include/swift/AST/ASTBridging.h b/include/swift/AST/ASTBridging.h index d4f17ace66efe..4f794e0a82280 100644 --- a/include/swift/AST/ASTBridging.h +++ b/include/swift/AST/ASTBridging.h @@ -17,6 +17,7 @@ /// include here and keep these includes minimal! /// /// See include guidelines and caveats in `BasicBridging.h`. +#include "swift/ABI/InvertibleProtocols.h" #include "swift/AST/AccessorKind.h" #include "swift/AST/AttrKind.h" #include "swift/AST/DiagnosticKind.h" @@ -350,6 +351,7 @@ struct BridgedDeclObj { BRIDGED_INLINE bool Class_isForeign() const; BRIDGED_INLINE bool ProtocolDecl_requiresClass() const; BRIDGED_INLINE bool ProtocolDecl_isMarkerProtocol() const; + BRIDGED_INLINE bool ProtocolDecl_isInvertible() const; BRIDGED_INLINE bool AbstractFunction_isOverridden() const; BRIDGED_INLINE bool Constructor_isInheritable() const; BRIDGED_INLINE bool Destructor_isIsolated() const; diff --git a/include/swift/AST/ASTBridgingImpl.h b/include/swift/AST/ASTBridgingImpl.h index 128b1c444dd15..4fa140dbeb5f9 100644 --- a/include/swift/AST/ASTBridgingImpl.h +++ b/include/swift/AST/ASTBridgingImpl.h @@ -22,6 +22,7 @@ #include "swift/AST/GenericSignature.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/IfConfigClauseRangeInfo.h" +#include "swift/AST/KnownProtocols.h" #include "swift/AST/MacroDeclaration.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/ProtocolConformanceRef.h" @@ -29,6 +30,7 @@ #include "swift/AST/Stmt.h" #include "swift/Basic/Assertions.h" #include "swift/Basic/Fingerprint.h" +#include SWIFT_BEGIN_NULLABILITY_ANNOTATIONS @@ -268,6 +270,10 @@ bool BridgedDeclObj::ProtocolDecl_isMarkerProtocol() const { return getAs()->isMarkerProtocol(); } +bool BridgedDeclObj::ProtocolDecl_isInvertible() const { + return getAs()->getInvertibleProtocolKind() != std::nullopt; +} + bool BridgedDeclObj::AbstractFunction_isOverridden() const { return getAs()->isOverridden(); } diff --git a/include/swift/SIL/SILBridging.h b/include/swift/SIL/SILBridging.h index e2b729ab23902..b3c38ea06f914 100644 --- a/include/swift/SIL/SILBridging.h +++ b/include/swift/SIL/SILBridging.h @@ -810,6 +810,7 @@ struct BridgedInstruction { 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; + SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedConformanceArray InitExistentialMetatypeInst_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 36b256bdd1ddf..bcac1dbdea3ad 100644 --- a/include/swift/SIL/SILBridgingImpl.h +++ b/include/swift/SIL/SILBridgingImpl.h @@ -1296,6 +1296,10 @@ BridgedCanType BridgedInstruction::InitExistentialAddrInst_getFormalConcreteType return getAs()->getFormalConcreteType(); } +BridgedConformanceArray BridgedInstruction::InitExistentialMetatypeInst_getConformances() const { + return {getAs()->getConformances()}; +} + bool BridgedInstruction::OpenExistentialAddr_isImmutable() const { switch (getAs()->getAccessKind()) { case swift::OpenedExistentialAccess::Immutable: return true; diff --git a/lib/Sema/LegalConstExprVerifier.cpp b/lib/Sema/LegalConstExprVerifier.cpp index 9b4055471c83e..4bafe74a0c04e 100644 --- a/lib/Sema/LegalConstExprVerifier.cpp +++ b/lib/Sema/LegalConstExprVerifier.cpp @@ -20,10 +20,9 @@ #include "TypeChecker.h" #include "swift/AST/ASTContext.h" #include "swift/AST/ASTWalker.h" -#include "swift/AST/Expr.h" #include "swift/AST/ParameterList.h" #include "swift/AST/SemanticAttrs.h" -#include "swift/AST/Type.h" +#include "swift/AST/ProtocolConformance.h" #include "swift/Basic/Assertions.h" using namespace swift; @@ -345,15 +344,44 @@ checkSupportedWithSectionAttribute(const Expr *expr, expressionsToCheck.push_back(identityExpr->getSubExpr()); continue; } - + // Upcasts of metatypes to existential metatypes (e.g. Any.Type) if (const ErasureExpr *erasureExpr = dyn_cast(expr)) { - if (const DotSelfExpr *dotSelfExpr = dyn_cast(erasureExpr->getSubExpr())) { - if (const TypeExpr *typeExpr = dyn_cast(dotSelfExpr->getSubExpr())) { + // Only allow concrete conformances when the protocol, type and + // conformance are all from the current module (except for + // Copyable+Escapable so that we can upcast to Any.Type) + bool allConformanceAreOkay = true; + for (auto conformance : erasureExpr->getConformances()) { + auto concreteConformance = conformance.getConcrete(); + if (!concreteConformance) { + allConformanceAreOkay = false; + break; + } + if (conformance.getProtocol()->getInvertibleProtocolKind()) { + continue; + } + auto conformanceModule = + concreteConformance->getDeclContext()->getParentModule(); + if (conformanceModule != declContext->getParentModule() || + conformanceModule != conformance.getProtocol()->getParentModule()) { + allConformanceAreOkay = false; + break; + } + } + + if (!allConformanceAreOkay) { + return std::make_pair(expr, TypeExpression); + } + + if (const DotSelfExpr *dotSelfExpr = + dyn_cast(erasureExpr->getSubExpr())) { + if (const TypeExpr *typeExpr = + dyn_cast(dotSelfExpr->getSubExpr())) { auto baseType = typeExpr->getType(); if (baseType && baseType->is()) { auto instanceType = baseType->getMetatypeInstanceType(); - if (auto nominal = instanceType->getNominalOrBoundGenericNominal()) { + if (auto nominal = + instanceType->getNominalOrBoundGenericNominal()) { // Allow non-generic, non-resilient types if (!nominal->isGeneric() && !nominal->isResilient()) { continue; diff --git a/test/ConstValues/SectionIR.swift b/test/ConstValues/SectionIR.swift index 81349d3a885ec..98aaaa26107e3 100644 --- a/test/ConstValues/SectionIR.swift +++ b/test/ConstValues/SectionIR.swift @@ -39,14 +39,16 @@ struct W { @section("mysection") static let closure7: @convention(c) (Int) -> Int = { x in x * 2 } } -struct S: Hashable, Sendable {} +protocol P1 { } +protocol P2 { } +struct S: Hashable, Sendable, P1, P2 {} // metatypes @section("mysection") let metatype1 = Int.self @section("mysection") let metatype2: Any.Type = Int.self @section("mysection") let metatype3: Any.Type = S.self -@section("mysection") let metatype4: any (Hashable & Sendable).Type = Int.self -@section("mysection") let metatype5: any (Hashable & Sendable).Type = S.self +@section("mysection") let metatype4: any (P1).Type = S.self +@section("mysection") let metatype5: any (P1 & P2).Type = S.self // tuples @section("mysection") let tuple1 = (1, 2, 3, 2.718, true) // ok @@ -84,8 +86,8 @@ struct S: Hashable, Sendable {} // CHECK: @"$s9SectionIR9metatype2ypXpvp" = {{.*}}constant ptr @"$sSiN", section "mysection" // CHECK: @"$s9SectionIR9metatype3ypXpvp" = {{.*}}constant ptr getelementptr inbounds (<{ ptr, ptr, {{i64|i32}}, ptr }>, ptr @"$s9SectionIR1SVMf", i32 0, i32 2), section "mysection" -// CHECK: @"$s9SectionIR9metatype4SH_s8SendablepXpvp" = {{.*}}constant <{ ptr, ptr }> <{ ptr @"$sSiN", ptr @"$sSiSHsWP" }>, section "mysection" -// CHECK: @"$s9SectionIR9metatype5SH_s8SendablepXpvp" = {{.*}}constant <{ ptr, ptr }> <{ ptr getelementptr inbounds (<{ ptr, ptr, {{i64|i32}}, ptr }>, ptr @"$s9SectionIR1SVMf", i32 0, i32 2), ptr @"$s9SectionIR1SVSHAAWP" }>, section "mysection" +// CHECK: @"$s9SectionIR9metatype4AA2P1_pXpvp" = {{.*}}constant <{ ptr, ptr }> <{ ptr getelementptr inbounds (<{ ptr, ptr, {{i64|i32}}, ptr }>, ptr @"$s9SectionIR1SVMf", i32 0, i32 2), ptr @"$s9SectionIR1SVAA2P1AAWP" }>, section "mysection" +// CHECK: @"$s9SectionIR9metatype5AA2P1_AA2P2pXpvp" = {{.*}}constant <{ ptr, ptr, ptr }> <{ ptr getelementptr inbounds (<{ ptr, ptr, {{i64|i32}}, ptr }>, ptr @"$s9SectionIR1SVMf", i32 0, i32 2), ptr @"$s9SectionIR1SVAA2P1AAWP", ptr @"$s9SectionIR1SVAA2P2AAWP" }>, section "mysection" // CHECK: @"$s9SectionIR6tuple1Si_S2iSdSbtvp" = {{.*}}constant <{ %TSi, %TSi, %TSi, {{.*}} }> <{ %TSi <{ {{i64|i32}} 1 }>, %TSi <{ {{i64|i32}} 2 }>, %TSi <{ {{i64|i32}} 3 }>, {{.*}} }>, section "mysection" // CHECK: @"$s9SectionIR6tuple2Si_SfSbtvp" = {{.*}}constant <{ %TSi, %TSf, %TSb }> <{ %TSi <{ {{i64|i32}} 42 }>, %TSf <{ float 0x40091EB860000000 }>, %TSb zeroinitializer }>, section "mysection" diff --git a/test/ConstValues/SectionSyntactic.swift b/test/ConstValues/SectionSyntactic.swift index 6fb4204998fcf..de4a2f43c13df 100644 --- a/test/ConstValues/SectionSyntactic.swift +++ b/test/ConstValues/SectionSyntactic.swift @@ -118,12 +118,16 @@ var capturedMutableVar = TestClass() struct S { } enum E { case a } +protocol P1 { } +protocol P2 { } +extension S: P1, P2 { } // metatypes @section("mysection") let metatype1 = Int.self // ok @section("mysection") let metatype2: Any.Type = Int.self // ok -@section("mysection") let metatype3: any (Hashable).Type = Int.self // ok -@section("mysection") let metatype4: any (Hashable & Sendable).Type = Int.self // ok +@section("mysection") let metatype3: Any.Type = S.self // ok +@section("mysection") let metatype4: any (P1).Type = S.self // ok +@section("mysection") let metatype5: any (P1 & P2).Type = S.self // ok // invalid metatype references @section("mysection") let invalidMetatype1 = Int.self.self @@ -134,6 +138,14 @@ enum E { case a } // expected-error@-1{{type expressions not supported in a constant expression}} @section("mysection") let invalidMetatype4 = Mirror.self // resilient // expected-error@-1{{type expressions not supported in a constant expression}} +@section("mysection") let invalidMetatype5: any (Hashable).Type = Int.self +// expected-error@-1{{type expressions not supported in a constant expression}} +@section("mysection") let invalidMetatype6: any (Hashable & Sendable).Type = Int.self +// expected-error@-1{{type expressions not supported in a constant expression}} +@section("mysection") let invalidMetatype7: any (Sendable).Type = S.self +// expected-error@-1{{type expressions not supported in a constant expression}} +@section("mysection") let invalidMetatype8: any (P1 & Sendable).Type = S.self +// expected-error@-1{{type expressions not supported in a constant expression}} // tuples @section("mysection") let tuple1 = (1, 2, 3, 2.718, true) // ok diff --git a/test/SILOptimizer/optimizable_metatype_references.swift b/test/SILOptimizer/optimizable_metatype_references.swift new file mode 100644 index 0000000000000..1d604ac068179 --- /dev/null +++ b/test/SILOptimizer/optimizable_metatype_references.swift @@ -0,0 +1,43 @@ +// REQUIRES: executable_test + +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -swift-version 5 %s -module-name MyModule -emit-sil -o - | %FileCheck --check-prefix=SIL %s +// RUN: %target-build-swift -swift-version 5 %s -o %t/main -module-name MyModule +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main | %FileCheck %s + +protocol MyProtocol {} + +struct MyStruct: Hashable, Equatable, MyProtocol { + static let static1: any Any.Type = MyStruct.self + static let static2: any Equatable.Type = MyStruct.self + static let static3: any (Hashable & Equatable).Type = MyStruct.self + static let static4 = MyStruct.self + static let static5: any MyProtocol.Type = MyStruct.self +} + +print("OK!") +// CHECK: OK + +// No constant folding for static1 (empty next line) +// SIL: sil_global hidden [let] @$s8MyModule0A6StructV7static1ypXpvpZ : $@thick any Any.Type +// SIL-EMPTY: + +// No constant folding for static2 (empty next line) +// SIL: sil_global hidden [let] @$s8MyModule0A6StructV7static2SQ_pXpvpZ : $@thick any Equatable.Type +// SIL-EMPTY: + +// No constant folding for static3 (empty next line) +// SIL: sil_global hidden [let] @$s8MyModule0A6StructV7static3SH_pXpvpZ : $@thick any Hashable.Type +// SIL-EMPTY: + +// Constant folding for static4 kicks in +// SIL: sil_global hidden [let] @$s8MyModule0A6StructV7static4ACmvpZ : $@thin MyStruct.Type = { +// SIL-NEXT: %initval = metatype $@thin MyStruct.Type +// SIL-NEXT: } + +// Constant folding for static5 kicks in +// SIL: sil_global hidden [let] @$s8MyModule0A6StructV7static5AA0A8Protocol_pXpvpZ : $@thick any MyProtocol.Type = { +// SIL: %0 = metatype $@thick MyStruct.Type +// SIL: %initval = init_existential_metatype %0, $@thick any MyProtocol.Type +// SIL: } From a4461d5b9b1f1441c8f0d18a499e46f332b549a4 Mon Sep 17 00:00:00 2001 From: Kuba Mracek Date: Tue, 11 Nov 2025 15:22:55 -0800 Subject: [PATCH 03/11] Fold optimizable_metatype_references.swift into static_init_metatypes.swift --- .../optimizable_metatype_references.swift | 43 ------ test/SILOptimizer/static_init_metatypes.swift | 137 +++++++++++------- 2 files changed, 85 insertions(+), 95 deletions(-) delete mode 100644 test/SILOptimizer/optimizable_metatype_references.swift diff --git a/test/SILOptimizer/optimizable_metatype_references.swift b/test/SILOptimizer/optimizable_metatype_references.swift deleted file mode 100644 index 1d604ac068179..0000000000000 --- a/test/SILOptimizer/optimizable_metatype_references.swift +++ /dev/null @@ -1,43 +0,0 @@ -// REQUIRES: executable_test - -// RUN: %empty-directory(%t) -// RUN: %target-build-swift -swift-version 5 %s -module-name MyModule -emit-sil -o - | %FileCheck --check-prefix=SIL %s -// RUN: %target-build-swift -swift-version 5 %s -o %t/main -module-name MyModule -// RUN: %target-codesign %t/main -// RUN: %target-run %t/main | %FileCheck %s - -protocol MyProtocol {} - -struct MyStruct: Hashable, Equatable, MyProtocol { - static let static1: any Any.Type = MyStruct.self - static let static2: any Equatable.Type = MyStruct.self - static let static3: any (Hashable & Equatable).Type = MyStruct.self - static let static4 = MyStruct.self - static let static5: any MyProtocol.Type = MyStruct.self -} - -print("OK!") -// CHECK: OK - -// No constant folding for static1 (empty next line) -// SIL: sil_global hidden [let] @$s8MyModule0A6StructV7static1ypXpvpZ : $@thick any Any.Type -// SIL-EMPTY: - -// No constant folding for static2 (empty next line) -// SIL: sil_global hidden [let] @$s8MyModule0A6StructV7static2SQ_pXpvpZ : $@thick any Equatable.Type -// SIL-EMPTY: - -// No constant folding for static3 (empty next line) -// SIL: sil_global hidden [let] @$s8MyModule0A6StructV7static3SH_pXpvpZ : $@thick any Hashable.Type -// SIL-EMPTY: - -// Constant folding for static4 kicks in -// SIL: sil_global hidden [let] @$s8MyModule0A6StructV7static4ACmvpZ : $@thin MyStruct.Type = { -// SIL-NEXT: %initval = metatype $@thin MyStruct.Type -// SIL-NEXT: } - -// Constant folding for static5 kicks in -// SIL: sil_global hidden [let] @$s8MyModule0A6StructV7static5AA0A8Protocol_pXpvpZ : $@thick any MyProtocol.Type = { -// SIL: %0 = metatype $@thick MyStruct.Type -// SIL: %initval = init_existential_metatype %0, $@thick any MyProtocol.Type -// SIL: } diff --git a/test/SILOptimizer/static_init_metatypes.swift b/test/SILOptimizer/static_init_metatypes.swift index e8c23905c6dff..1500937f34ff8 100644 --- a/test/SILOptimizer/static_init_metatypes.swift +++ b/test/SILOptimizer/static_init_metatypes.swift @@ -1,57 +1,90 @@ -// RUN: %target-swift-frontend %s -parse-as-library -module-name=test -emit-sil | %FileCheck %s -// RUN: %target-swift-frontend %s -parse-as-library -O -module-name=test -emit-sil | %FileCheck %s +// RUN: %target-swift-frontend %s -parse-as-library -module-name=test -emit-sil | %FileCheck --check-prefix=SIL %s +// RUN: %target-swift-frontend %s -parse-as-library -O -module-name=test -emit-sil | %FileCheck --check-prefix=SIL %s -struct S: Hashable, Sendable {} +// RUN: %target-swift-frontend %s -parse-as-library -module-name=test -emit-ir | %FileCheck --check-prefix=IR %s +// RUN: %target-swift-frontend %s -parse-as-library -O -module-name=test -emit-ir | %FileCheck --check-prefix=IR %s -public let metatype1 = Int.self -public let metatype2: Any.Type = Int.self -public let metatype3: Any.Type = S.self -public let metatype4: any (Hashable & Sendable).Type = Int.self -public let metatype5: any (Hashable & Sendable).Type = S.self -public let metatype6: Any.Type = Array.self -public let metatype7: any (Hashable & Sendable).Type = Array.self - -// CHECK: sil_global [let] @$s4test9metatype1Simvp : $@thin Int.Type = { -// CHECK-NEXT: %initval = metatype $@thin Int.Type -// CHECK-NEXT: } - -// CHECK: sil_global [let] @$s4test9metatype2ypXpvp : $@thick any Any.Type = { -// CHECK-NEXT: %0 = metatype $@thick Int.Type -// CHECK-NEXT: %initval = init_existential_metatype %0, $@thick any Any.Type -// CHECK-NEXT: } - -// CHECK: sil_global [let] @$s4test9metatype3ypXpvp : $@thick any Any.Type = { -// CHECK-NEXT: %0 = metatype $@thick S.Type -// CHECK-NEXT: %initval = init_existential_metatype %0, $@thick any Any.Type -// CHECK-NEXT: } +public protocol MyProtocol {} +public protocol MyProtocol2 {} -// CHECK: sil_global [let] @$s4test9metatype4SH_s8SendablepXpvp : $@thick any (Hashable & Sendable).Type = { -// CHECK-NEXT: %0 = metatype $@thick Int.Type -// CHECK-NEXT: %initval = init_existential_metatype %0, $@thick any (Hashable & Sendable).Type -// CHECK-NEXT: } +public struct S: Hashable, Sendable, MyProtocol, MyProtocol2 {} +extension Int: MyProtocol, MyProtocol2 {} -// CHECK: sil_global [let] @$s4test9metatype5SH_s8SendablepXpvp : $@thick any (Hashable & Sendable).Type = { -// CHECK-NEXT: %0 = metatype $@thick S.Type -// CHECK-NEXT: %initval = init_existential_metatype %0, $@thick any (Hashable & Sendable).Type -// CHECK-NEXT: } - -// CHECK: sil_global [let] @$s4test9metatype6ypXpvp : $@thick any Any.Type = { -// CHECK-NEXT: %0 = metatype $@thick Array.Type -// CHECK-NEXT: %initval = init_existential_metatype %0, $@thick any Any.Type -// CHECK-NEXT: } - -// CHECK: sil_global [let] @$s4test9metatype7SH_s8SendablepXpvp : $@thick any (Hashable & Sendable).Type = { -// CHECK-NEXT: %0 = metatype $@thick Array.Type -// CHECK-NEXT: %initval = init_existential_metatype %0, $@thick any (Hashable & Sendable).Type -// CHECK-NEXT: } - -// These are not constant folded and stay as lazily-initialized -public let metatypeA: Any.Type = (Bool.random() ? Array.self : Array.self).self -public let metatypeB: Any.Type = Mirror.self // resilient +public let metatype1 = Int.self +// SIL: sil_global [let] @$s4test9metatype1Simvp : $@thin Int.Type = { +// SIL-NEXT: %initval = metatype $@thin Int.Type +// SIL-NEXT: } +public let metatype2 = S.self +// SIL: sil_global [let] @$s4test9metatype2AA1SVmvp : $@thin S.Type = { +// SIL-NEXT: %initval = metatype $@thin S.Type +// SIL-NEXT: } +public let metatype3: Any.Type = Int.self +// SIL: sil_global [let] @$s4test9metatype3ypXpvp : $@thick any Any.Type = { +// SIL-NEXT: %0 = metatype $@thick Int.Type // user: %1 +// SIL-NEXT: %initval = init_existential_metatype %0, $@thick any Any.Type +// SIL-NEXT: } +public let metatype4: Any.Type = S.self +// SIL: sil_global [let] @$s4test9metatype4ypXpvp : $@thick any Any.Type = { +// SIL-NEXT: %0 = metatype $@thick S.Type // user: %1 +// SIL-NEXT: %initval = init_existential_metatype %0, $@thick any Any.Type +// SIL-NEXT: } +public let metatype5: any MyProtocol.Type = Int.self +// SIL: sil_global [let] @$s4test9metatype5AA10MyProtocol_pXpvp : $@thick any MyProtocol.Type = { +// SIL-NEXT: %0 = metatype $@thick Int.Type // user: %1 +// SIL-NEXT: %initval = init_existential_metatype %0, $@thick any MyProtocol.Type +// SIL-NEXT: } +public let metatype6: any MyProtocol.Type = S.self +// SIL: sil_global [let] @$s4test9metatype6AA10MyProtocol_pXpvp : $@thick any MyProtocol.Type = { +// SIL-NEXT: %0 = metatype $@thick S.Type // user: %1 +// SIL-NEXT: %initval = init_existential_metatype %0, $@thick any MyProtocol.Type +// SIL-NEXT: } +public let metatype7: any (MyProtocol & MyProtocol2).Type = Int.self +// SIL: sil_global [let] @$s4test9metatype7AA10MyProtocol_AA0C9Protocol2pXpvp : $@thick any (MyProtocol & MyProtocol2).Type = { +// SIL-NEXT: %0 = metatype $@thick Int.Type // user: %1 +// SIL-NEXT: %initval = init_existential_metatype %0, $@thick any (MyProtocol & MyProtocol2).Type +// SIL-NEXT: } +public let metatype8: any (MyProtocol & MyProtocol2).Type = S.self +// SIL: sil_global [let] @$s4test9metatype8AA10MyProtocol_AA0C9Protocol2pXpvp : $@thick any (MyProtocol & MyProtocol2).Type = { +// SIL-NEXT: %0 = metatype $@thick S.Type // user: %1 +// SIL-NEXT: %initval = init_existential_metatype %0, $@thick any (MyProtocol & MyProtocol2).Type +// SIL-NEXT: } +public let metatype9: any Hashable.Type = Int.self +// SIL: sil_global [let] @$s4test9metatype9SH_pXpvp : $@thick any Hashable.Type +// SIL-EMPTY: +public let metatype10: any Hashable.Type = S.self +// SIL: sil_global [let] @$s4test10metatype10SH_pXpvp : $@thick any Hashable.Type +// SIL-EMPTY: +public let metatype11: any (Hashable & Sendable).Type = Int.self +// SIL: sil_global [let] @$s4test10metatype11SH_s8SendablepXpvp : $@thick any (Hashable & Sendable).Type +// SIL-EMPTY: +public let metatype12: any (Hashable & Sendable).Type = S.self +// SIL: sil_global [let] @$s4test10metatype12SH_s8SendablepXpvp : $@thick any (Hashable & Sendable).Type +// SIL-EMPTY: +public let metatype13: any (Hashable & MyProtocol).Type = Int.self +// SIL: sil_global [let] @$s4test10metatype13SH_AA10MyProtocolpXpvp : $@thick any (Hashable & MyProtocol).Type +// SIL-EMPTY: +public let metatype14: any (Hashable & MyProtocol).Type = S.self +// SIL: sil_global [let] @$s4test10metatype14SH_AA10MyProtocolpXpvp : $@thick any (Hashable & MyProtocol).Type +// SIL-EMPTY: +public let metatype15: Any.Type = Array.self +// SIL: sil_global [let] @$s4test10metatype15ypXpvp : $@thick any Any.Type = { +// SIL-NEXT: %0 = metatype $@thick Array.Type // user: %1 +// SIL-NEXT: %initval = init_existential_metatype %0, $@thick any Any.Type +// SIL-NEXT: } +public let metatype16: any (Hashable & Sendable).Type = Array.self +// SIL: sil_global [let] @$s4test10metatype16SH_s8SendablepXpvp : $@thick any (Hashable & Sendable).Type +// SIL-EMPTY: +public let metatype17: Any.Type = (Bool.random() ? Array.self : Array.self).self +// SIL: sil_global [let] @$s4test10metatype17ypXpvp : $@thick any Any.Type +// SIL-EMPTY: +public let metatype18: Any.Type = Mirror.self // resilient +// SIL: sil_global [let] @$s4test10metatype18ypXpvp : $@thick any Any.Type +// SIL-EMPTY: -// CHECK: sil_global private @$s4test9metatypeA_Wz : $Builtin.Word -// CHECK: sil_global [let] @$s4test9metatypeAypXpvp : $@thick any Any.Type -// CHECK: sil_global private @$s4test9metatypeB_Wz : $Builtin.Word -// CHECK: sil_global [let] @$s4test9metatypeBypXpvp : $@thick any Any.Type -// CHECK: sil private [global_init_once_fn] @$s4test9metatypeA_WZ : $@convention(c) (Builtin.RawPointer) -> () { -// CHECK: sil private [global_init_once_fn] @$s4test9metatypeB_WZ : $@convention(c) (Builtin.RawPointer) -> () { +// IR: @"$s4test9metatype3ypXpvp" = {{.*}}constant ptr @"$sSiN" +// IR: @"$s4test9metatype4ypXpvp" = {{.*}}constant ptr getelementptr inbounds{{.*}} ({{.*}}, ptr @"$s4test1SVMf", {{.*}}) +// IR: @"$s4test9metatype5AA10MyProtocol_pXpvp" = {{.*}}constant <{ ptr, ptr }> <{ ptr @"$sSiN", ptr @"$sSi4test10MyProtocolAAWP" }> +// IR: @"$s4test9metatype6AA10MyProtocol_pXpvp" = {{.*}}constant <{ ptr, ptr }> <{ ptr getelementptr inbounds{{.*}} ({{.*}}, ptr @"$s4test1SVMf", {{.*}}), ptr @"$s4test1SVAA10MyProtocolAAWP" }> +// IR: @"$s4test9metatype7AA10MyProtocol_AA0C9Protocol2pXpvp" = {{.*}}constant <{ ptr, ptr, ptr }> <{ ptr @"$sSiN", ptr @"$sSi4test10MyProtocolAAWP", ptr @"$sSi4test11MyProtocol2AAWP" }> +// IR: @"$s4test9metatype8AA10MyProtocol_AA0C9Protocol2pXpvp" = {{.*}}constant <{ ptr, ptr, ptr }> <{ ptr getelementptr inbounds{{.*}} ({{.*}}, ptr @"$s4test1SVMf", {{.*}}), ptr @"$s4test1SVAA10MyProtocolAAWP", ptr @"$s4test1SVAA11MyProtocol2AAWP" }> +// IR: @"$s4test10metatype15ypXpvp" = {{.*}}constant ptr getelementptr inbounds{{.*}} ({{.*}}, ptr @"$sSaySiGMf", {{.*}}) From c4ed82e35c9438a536ccb5fca63c8e0813dd6682 Mon Sep 17 00:00:00 2001 From: Kuba Mracek Date: Tue, 11 Nov 2025 16:46:48 -0800 Subject: [PATCH 04/11] Fix build of LegalConstExprVerifier.cpp: Use hasGenericParamList() instead of isGeneric() --- lib/Sema/LegalConstExprVerifier.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/Sema/LegalConstExprVerifier.cpp b/lib/Sema/LegalConstExprVerifier.cpp index 4bafe74a0c04e..359bd87436bf6 100644 --- a/lib/Sema/LegalConstExprVerifier.cpp +++ b/lib/Sema/LegalConstExprVerifier.cpp @@ -329,7 +329,9 @@ checkSupportedWithSectionAttribute(const Expr *expr, auto instanceType = baseType->getMetatypeInstanceType(); if (auto nominal = instanceType->getNominalOrBoundGenericNominal()) { // Allow non-generic, non-resilient types - if (!nominal->hasGenericParamList() && !nominal->isResilient()) { + if (!nominal->hasGenericParamList() && + !nominal->getDeclContext()->isGenericContext() && + !nominal->isResilient()) { continue; } } @@ -383,7 +385,9 @@ checkSupportedWithSectionAttribute(const Expr *expr, if (auto nominal = instanceType->getNominalOrBoundGenericNominal()) { // Allow non-generic, non-resilient types - if (!nominal->isGeneric() && !nominal->isResilient()) { + if (!nominal->hasGenericParamList() && + !nominal->getDeclContext()->isGenericContext() && + !nominal->isResilient()) { continue; } } From a84384e3d49d8a40e013a147ba12e2fdabf74139 Mon Sep 17 00:00:00 2001 From: Kuba Mracek Date: Wed, 12 Nov 2025 10:53:56 -0800 Subject: [PATCH 05/11] Fix generic detection, assert we have addressable metadata, expand tests --- .../Optimizer/Utilities/OptUtils.swift | 5 ++-- lib/IRGen/GenConstant.cpp | 7 +++-- lib/IRGen/GenDecl.cpp | 5 ++++ test/SILOptimizer/static_init_metatypes.swift | 28 +++++++++++++++---- 4 files changed, 36 insertions(+), 9 deletions(-) diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift b/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift index 84176f51399dd..5987f1c249069 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift +++ b/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift @@ -507,8 +507,9 @@ extension Instruction { // Metatypes (and upcasts of them to existentials) can be used as static initializers for SE-0492, as long as they // are not generic or resilient case let mti as MetatypeInst: - if !mti.type.hasTypeParameter, + if !mti.type.isGenericAtAnyLevel, let nominal = mti.type.canonicalType.instanceTypeOfMetatype.nominal, + !nominal.isGenericAtAnyLevel, !nominal.isResilient(in: mti.parentFunction) { return true } @@ -517,7 +518,7 @@ extension Instruction { let isAnyConformanceResilient = iemi.conformances.contains { !$0.protocol.isInvertible && $0.isResilientConformance(currentModule: context.currentModuleContext) } - return !iemi.type.hasTypeParameter && !isAnyConformanceResilient + return !iemi.type.isGenericAtAnyLevel && !isAnyConformanceResilient case is StructInst, is TupleInst, diff --git a/lib/IRGen/GenConstant.cpp b/lib/IRGen/GenConstant.cpp index 5e6b89a92e4eb..859e0e999d1e4 100644 --- a/lib/IRGen/GenConstant.cpp +++ b/lib/IRGen/GenConstant.cpp @@ -24,6 +24,7 @@ #include "GenIntegerLiteral.h" #include "GenStruct.h" #include "GenTuple.h" +#include "MetadataRequest.h" #include "TypeInfo.h" #include "StructLayout.h" #include "Callee.h" @@ -420,6 +421,7 @@ Explosion irgen::emitConstantValue(IRGenModule &IGM, SILValue operand, } else if (auto *mti = dyn_cast(operand)) { auto metaTy = mti->getType().castTo(); auto type = metaTy.getInstanceType(); + ASSERT(isCanonicalCompleteTypeMetadataStaticallyAddressable(IGM, type)); return IGM.getAddrOfTypeMetadata(type); } else if (auto *iemi = dyn_cast(operand)) { auto *mti = @@ -428,10 +430,11 @@ Explosion irgen::emitConstantValue(IRGenModule &IGM, SILValue operand, auto metaTy = mti->getType().castTo(); auto type = metaTy.getInstanceType(); - llvm::Constant *metatype = IGM.getAddrOfTypeMetadata(type); + ASSERT(isCanonicalCompleteTypeMetadataStaticallyAddressable(IGM, type)); + llvm::Constant *metadata = IGM.getAddrOfTypeMetadata(type); Explosion result; - emitExistentialMetatypeContainer(IGM, result, iemi->getType(), metatype, + emitExistentialMetatypeContainer(IGM, result, iemi->getType(), metadata, iemi->getOperand()->getType(), iemi->getConformances()); return result; diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index 1a6921606165b..053679c62d415 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -25,11 +25,13 @@ #include "swift/AST/ModuleDependencies.h" #include "swift/AST/NameLookup.h" #include "swift/AST/Pattern.h" +#include "swift/AST/PrettyStackTrace.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/TypeMemberVisitor.h" #include "swift/AST/Types.h" #include "swift/Basic/Assertions.h" #include "swift/Basic/Mangler.h" +#include "swift/Basic/PrettyStackTrace.h" #include "swift/ClangImporter/ClangModule.h" #include "swift/Demangling/ManglingMacros.h" #include "swift/IRGen/Linking.h" @@ -2931,6 +2933,9 @@ Address IRGenModule::getAddrOfSILGlobalVariable(SILGlobalVariable *var, llvm::Constant *IRGenModule::getGlobalInitValue(SILGlobalVariable *var, llvm::Type *storageType, Alignment alignment) { + PrettyStackTraceStringAction trace( + "constant-folding init value for SIL global", var->getName()); + if (var->isInitializedObject()) { StructLayout *layout = StaticObjectLayouts[var].layout.get(); ObjectInst *oi = cast(var->getStaticInitializerValue()); diff --git a/test/SILOptimizer/static_init_metatypes.swift b/test/SILOptimizer/static_init_metatypes.swift index 1500937f34ff8..6da326946714f 100644 --- a/test/SILOptimizer/static_init_metatypes.swift +++ b/test/SILOptimizer/static_init_metatypes.swift @@ -4,6 +4,11 @@ // RUN: %target-swift-frontend %s -parse-as-library -module-name=test -emit-ir | %FileCheck --check-prefix=IR %s // RUN: %target-swift-frontend %s -parse-as-library -O -module-name=test -emit-ir | %FileCheck --check-prefix=IR %s +// RUN: %target-run-simple-swift(-parse-as-library) +// RUN: %target-run-simple-swift(-parse-as-library -O) + +// REQUIRES: executable_test + public protocol MyProtocol {} public protocol MyProtocol2 {} @@ -67,10 +72,8 @@ public let metatype14: any (Hashable & MyProtocol).Type = S.self // SIL: sil_global [let] @$s4test10metatype14SH_AA10MyProtocolpXpvp : $@thick any (Hashable & MyProtocol).Type // SIL-EMPTY: public let metatype15: Any.Type = Array.self -// SIL: sil_global [let] @$s4test10metatype15ypXpvp : $@thick any Any.Type = { -// SIL-NEXT: %0 = metatype $@thick Array.Type // user: %1 -// SIL-NEXT: %initval = init_existential_metatype %0, $@thick any Any.Type -// SIL-NEXT: } +// SIL: sil_global [let] @$s4test10metatype15ypXpvp : $@thick any Any.Type +// SIL-EMPTY: public let metatype16: any (Hashable & Sendable).Type = Array.self // SIL: sil_global [let] @$s4test10metatype16SH_s8SendablepXpvp : $@thick any (Hashable & Sendable).Type // SIL-EMPTY: @@ -80,6 +83,22 @@ public let metatype17: Any.Type = (Bool.random() ? Array.self : Array public let metatype18: Any.Type = Mirror.self // resilient // SIL: sil_global [let] @$s4test10metatype18ypXpvp : $@thick any Any.Type // SIL-EMPTY: +public var metatype19: Any.Type = (any MyProtocol).self +// SIL: sil_global @$s4test10metatype19ypXpvp : $@thick any Any.Type +// SIL-EMPTY: +public var metatype20: [Any.Type] = [(any MyProtocol).self] +// SIL: sil_global @$s4test10metatype20SayypXpGvp : $Array +// SIL-EMPTY: +public var metatype21: [Any.Type] = [Int.self] +// SIL: sil_global @$s4test10metatype21SayypXpGvp : $Array +// SIL-EMPTY: +public var metatype22: [Any.Type] = [S.self] +// SIL: sil_global @$s4test10metatype22SayypXpGvp : $Array +// SIL-EMPTY: + +// Only to detect link-time missing symbols +@main +struct Main { static func main() { } } // IR: @"$s4test9metatype3ypXpvp" = {{.*}}constant ptr @"$sSiN" // IR: @"$s4test9metatype4ypXpvp" = {{.*}}constant ptr getelementptr inbounds{{.*}} ({{.*}}, ptr @"$s4test1SVMf", {{.*}}) @@ -87,4 +106,3 @@ public let metatype18: Any.Type = Mirror.self // resilient // IR: @"$s4test9metatype6AA10MyProtocol_pXpvp" = {{.*}}constant <{ ptr, ptr }> <{ ptr getelementptr inbounds{{.*}} ({{.*}}, ptr @"$s4test1SVMf", {{.*}}), ptr @"$s4test1SVAA10MyProtocolAAWP" }> // IR: @"$s4test9metatype7AA10MyProtocol_AA0C9Protocol2pXpvp" = {{.*}}constant <{ ptr, ptr, ptr }> <{ ptr @"$sSiN", ptr @"$sSi4test10MyProtocolAAWP", ptr @"$sSi4test11MyProtocol2AAWP" }> // IR: @"$s4test9metatype8AA10MyProtocol_AA0C9Protocol2pXpvp" = {{.*}}constant <{ ptr, ptr, ptr }> <{ ptr getelementptr inbounds{{.*}} ({{.*}}, ptr @"$s4test1SVMf", {{.*}}), ptr @"$s4test1SVAA10MyProtocolAAWP", ptr @"$s4test1SVAA11MyProtocol2AAWP" }> -// IR: @"$s4test10metatype15ypXpvp" = {{.*}}constant ptr getelementptr inbounds{{.*}} ({{.*}}, ptr @"$sSaySiGMf", {{.*}}) From 802442b1077d72874950e968d8cf5aad34162767 Mon Sep 17 00:00:00 2001 From: Kuba Mracek Date: Wed, 12 Nov 2025 10:59:12 -0800 Subject: [PATCH 06/11] Minimize ASTBridging.h / ASTBridgingImpl.h #includes --- include/swift/AST/ASTBridging.h | 1 - include/swift/AST/ASTBridgingImpl.h | 2 -- 2 files changed, 3 deletions(-) diff --git a/include/swift/AST/ASTBridging.h b/include/swift/AST/ASTBridging.h index 4f794e0a82280..1606ccfa44389 100644 --- a/include/swift/AST/ASTBridging.h +++ b/include/swift/AST/ASTBridging.h @@ -17,7 +17,6 @@ /// include here and keep these includes minimal! /// /// See include guidelines and caveats in `BasicBridging.h`. -#include "swift/ABI/InvertibleProtocols.h" #include "swift/AST/AccessorKind.h" #include "swift/AST/AttrKind.h" #include "swift/AST/DiagnosticKind.h" diff --git a/include/swift/AST/ASTBridgingImpl.h b/include/swift/AST/ASTBridgingImpl.h index 4fa140dbeb5f9..c08787810b021 100644 --- a/include/swift/AST/ASTBridgingImpl.h +++ b/include/swift/AST/ASTBridgingImpl.h @@ -22,7 +22,6 @@ #include "swift/AST/GenericSignature.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/IfConfigClauseRangeInfo.h" -#include "swift/AST/KnownProtocols.h" #include "swift/AST/MacroDeclaration.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/ProtocolConformanceRef.h" @@ -30,7 +29,6 @@ #include "swift/AST/Stmt.h" #include "swift/Basic/Assertions.h" #include "swift/Basic/Fingerprint.h" -#include SWIFT_BEGIN_NULLABILITY_ANNOTATIONS From 44afbf3aa65a981c3429b8798eaf1664cf8af4a5 Mon Sep 17 00:00:00 2001 From: Kuba Mracek Date: Fri, 14 Nov 2025 14:41:46 -0800 Subject: [PATCH 07/11] Further restrict MetatypeInst constant folding to not constant fold classes --- .../Optimizer/Utilities/OptUtils.swift | 3 +- .../SILOptimizer/static_init_metatypes2.swift | 38 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 test/SILOptimizer/static_init_metatypes2.swift diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift b/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift index 5987f1c249069..0c2f788d2bde2 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift +++ b/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift @@ -505,9 +505,10 @@ extension Instruction { return context.canMakeStaticObjectReadOnly(objectType: gvi.type) // Metatypes (and upcasts of them to existentials) can be used as static initializers for SE-0492, as long as they - // are not generic or resilient + // do not require an accessor to reference the metadata (i.e. are not generic, not resilient and not a class). case let mti as MetatypeInst: if !mti.type.isGenericAtAnyLevel, + !mti.type.canonicalType.instanceTypeOfMetatype.isClass, let nominal = mti.type.canonicalType.instanceTypeOfMetatype.nominal, !nominal.isGenericAtAnyLevel, !nominal.isResilient(in: mti.parentFunction) { diff --git a/test/SILOptimizer/static_init_metatypes2.swift b/test/SILOptimizer/static_init_metatypes2.swift new file mode 100644 index 0000000000000..a9f657971dcb8 --- /dev/null +++ b/test/SILOptimizer/static_init_metatypes2.swift @@ -0,0 +1,38 @@ +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s + +// RUN: %target-swift-frontend -parse-as-library -emit-module -o %t/MyModule.swiftmodule %t/MyModule.swift -O +// XXX: %target-swift-frontend -parse-as-library -I %t %t/Main.swift -O -emit-sil | %FileCheck %s --check-prefix SIL +// RUN: %target-swift-frontend -parse-as-library -I %t %t/Main.swift -O -emit-ir | %FileCheck %s --check-prefix IR + +// BEGIN MyModule.swift + +public class MyClass {} +public struct MyStruct {} + +// BEGIN Main.swift + +import MyModule + +public let classMetatype: Any.Type = MyClass.self +public let classMetatypeArray: [Any.Type] = [classMetatype] + +public let structMetatype: Any.Type = MyStruct.self +public let structMetatypeArray: [Any.Type] = [structMetatype] + +// SIL: sil_global [let] @$s4Main13classMetatypeypXpvp : $@thick any Any.Type +// SIL-EMPTY: +// SIL: sil_global [let] @$s4Main18classMetatypeArraySayypXpGvp : $Array +// SIL-EMPTY: +// SIL: sil_global [let] @$s4Main14structMetatypeypXpvp : $@thick any Any.Type = { +// SIL-NEXT: %0 = metatype $@thick MyStruct.Type +// SIL-NEXT: %initval = init_existential_metatype %0, $@thick any Any.Type +// SIL-NEXT: } +// SIL: sil_global [let] @$s4Main19structMetatypeArraySayypXpGvp : $Array +// SIL-EMPTY: + +// IR: @"$s4Main13classMetatypeypXpvp" = {{.*}}global { ptr } zeroinitializer +// IR: @"$s4Main18classMetatypeArraySayypXpGvp" = {{.*}}global %TSa zeroinitializer +// IR: @"$s8MyModule0A6StructVN" = {{.*}}global %swift.type +// IR: @"$s4Main14structMetatypeypXpvp" = {{.*}}constant ptr @"$s8MyModule0A6StructVN" +// IR: @"$s4Main19structMetatypeArraySayypXpGvp" = {{.*}}global %TSa zeroinitializer From 8097cd9de7262c5021ac227a9c586be6d52b1957 Mon Sep 17 00:00:00 2001 From: Kuba Mracek Date: Sat, 15 Nov 2025 10:19:05 -0800 Subject: [PATCH 08/11] Don't constant fold metatypes that indirectly contain resilient types --- .../Optimizer/Utilities/OptUtils.swift | 9 ++++--- SwiftCompilerSources/Sources/SIL/Type.swift | 4 +++ include/swift/SIL/SILBridging.h | 1 + include/swift/SIL/SILBridgingImpl.h | 4 +++ .../SILOptimizer/static_init_metatypes3.swift | 25 +++++++++++++++++++ 5 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 test/SILOptimizer/static_init_metatypes3.swift diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift b/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift index 0c2f788d2bde2..bdfef5487f9cd 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift +++ b/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift @@ -506,12 +506,15 @@ extension Instruction { // Metatypes (and upcasts of them to existentials) can be used as static initializers for SE-0492, as long as they // do not require an accessor to reference the metadata (i.e. are not generic, not resilient and not a class). + // See also irgen::isCanonicalCompleteTypeMetadataStaticallyAddressable. case let mti as MetatypeInst: + let silType = mti.type.loweredInstanceTypeOfMetatype(in: parentFunction) + let instanceType = mti.type.canonicalType.instanceTypeOfMetatype if !mti.type.isGenericAtAnyLevel, - !mti.type.canonicalType.instanceTypeOfMetatype.isClass, - let nominal = mti.type.canonicalType.instanceTypeOfMetatype.nominal, + !instanceType.isClass, + let nominal = instanceType.nominal, !nominal.isGenericAtAnyLevel, - !nominal.isResilient(in: mti.parentFunction) { + silType.isFixedABI(in: mti.parentFunction) { return true } return false diff --git a/SwiftCompilerSources/Sources/SIL/Type.swift b/SwiftCompilerSources/Sources/SIL/Type.swift index 26bd2b9c144ab..1cc4bc2b74736 100644 --- a/SwiftCompilerSources/Sources/SIL/Type.swift +++ b/SwiftCompilerSources/Sources/SIL/Type.swift @@ -103,6 +103,10 @@ public struct Type : TypeProperties, CustomStringConvertible, NoReflectionChildr bridged.isAddressableForDeps(function.bridged) } + public func isFixedABI(in function: Function) -> Bool { + bridged.isFixedABI(function.bridged) + } + /// If this is a raw layout type, returns the substituted like-type. public var rawLayoutSubstitutedLikeType: AST.`Type`? { .init(bridgedOrNil: bridged.getRawLayoutSubstitutedLikeType()) diff --git a/include/swift/SIL/SILBridging.h b/include/swift/SIL/SILBridging.h index b3c38ea06f914..bd204426a3544 100644 --- a/include/swift/SIL/SILBridging.h +++ b/include/swift/SIL/SILBridging.h @@ -291,6 +291,7 @@ struct BridgedType { BRIDGED_INLINE bool isExactSuperclassOf(BridgedType t) const; BRIDGED_INLINE bool isMarkedAsImmortal() const; BRIDGED_INLINE bool isAddressableForDeps(BridgedFunction f) const; + BRIDGED_INLINE bool isFixedABI(BridgedFunction f) const; BRIDGED_INLINE SWIFT_IMPORT_UNSAFE BridgedASTType getRawLayoutSubstitutedLikeType() const; BRIDGED_INLINE SWIFT_IMPORT_UNSAFE BridgedASTType getRawLayoutSubstitutedCountType() const; BRIDGED_INLINE bool mayHaveCustomDeinit(BridgedFunction f) const; diff --git a/include/swift/SIL/SILBridgingImpl.h b/include/swift/SIL/SILBridgingImpl.h index bcac1dbdea3ad..aeb59300dc4a2 100644 --- a/include/swift/SIL/SILBridgingImpl.h +++ b/include/swift/SIL/SILBridgingImpl.h @@ -402,6 +402,10 @@ bool BridgedType::isAddressableForDeps(BridgedFunction f) const { return unbridged().isAddressableForDeps(*f.getFunction()); } +bool BridgedType::isFixedABI(BridgedFunction f) const { + return unbridged().isFixedABI(*f.getFunction()); +} + BridgedASTType BridgedType::getRawLayoutSubstitutedLikeType() const { return {unbridged().getRawLayoutSubstitutedLikeType().getPointer()}; } diff --git a/test/SILOptimizer/static_init_metatypes3.swift b/test/SILOptimizer/static_init_metatypes3.swift new file mode 100644 index 0000000000000..14bd63844ac5f --- /dev/null +++ b/test/SILOptimizer/static_init_metatypes3.swift @@ -0,0 +1,25 @@ +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s + +// RUN: %target-swift-frontend -parse-as-library -emit-module -o %t/MyModule.swiftmodule %t/MyModule.swift -O +// RUN: %target-swift-frontend -parse-as-library -I %t %t/Main.swift -O -emit-ir | %FileCheck %s + +// BEGIN MyModule.swift + +public struct NonResilientTypeContainingAResilientType { + var m: Mirror? = nil +} + +// BEGIN Main.swift + +import MyModule + +public protocol MyProtocol {} + +public struct MyStruct: MyProtocol { + public var x: NonResilientTypeContainingAResilientType? = nil +} + +public let glob: [any MyProtocol.Type] = [MyStruct.self] + +// CHECK: @"$s4Main4globSayAA10MyProtocol_pXpGvp" = {{.*}}global %TSa zeroinitializer From 83368bfed16eae98765e43e7de412758d84ee50d Mon Sep 17 00:00:00 2001 From: Kuba Mracek Date: Wed, 19 Nov 2025 07:32:53 -0800 Subject: [PATCH 09/11] XFAIL test/SILOptimizer/static_init_metatypes.swift on Windows --- test/SILOptimizer/static_init_metatypes.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/SILOptimizer/static_init_metatypes.swift b/test/SILOptimizer/static_init_metatypes.swift index 6da326946714f..c13b18d145ee1 100644 --- a/test/SILOptimizer/static_init_metatypes.swift +++ b/test/SILOptimizer/static_init_metatypes.swift @@ -9,6 +9,9 @@ // REQUIRES: executable_test +// TODO: Resolve link-time error (unresolved external symbol $sSiN) to Windows +// XFAIL: OS=windows-msvc + public protocol MyProtocol {} public protocol MyProtocol2 {} From 2f151c1d2f810b9fc38ec1e01c81172852e7bf52 Mon Sep 17 00:00:00 2001 From: "Kuba (Brecka) Mracek" Date: Sat, 13 Dec 2025 06:02:31 -0800 Subject: [PATCH 10/11] Update build-presets.ini --- utils/build-presets.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/utils/build-presets.ini b/utils/build-presets.ini index 040cbccaacee9..f61371c43cc2c 100644 --- a/utils/build-presets.ini +++ b/utils/build-presets.ini @@ -946,6 +946,7 @@ mixin-preset= buildbot_linux_base skip-test-swiftdocc +verbose-build [preset: buildbot_linux,release_foundation_tests] mixin-preset=buildbot_linux From 38f2cc7486fb23a88cd4c6f267d6e55d3f0eacfc Mon Sep 17 00:00:00 2001 From: "Kuba (Brecka) Mracek" Date: Sat, 13 Dec 2025 18:02:18 -0800 Subject: [PATCH 11/11] Update GenDecl.cpp --- lib/IRGen/GenDecl.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index 053679c62d415..55d04086ac6a9 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -2967,7 +2967,12 @@ llvm::Constant *IRGenModule::getGlobalInitValue(SILGlobalVariable *var, if (SILInstruction *initInst = var->getStaticInitializerValue()) { Explosion initExp = emitConstantValue(*this, cast(initInst)); - return getConstantValue(std::move(initExp), /*paddingBytes=*/ 0); + llvm::Constant *initVal = getConstantValue(std::move(initExp), /*paddingBytes=*/ 0); + llvm::errs() << "getGlobalInitValue:\n"; + var->dump(); + initVal->dump(); + llvm::errs() << "\n"; + return initVal; } return nullptr; }