Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,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,
Expand Down
9 changes: 9 additions & 0 deletions include/swift/SIL/SILBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -2149,6 +2149,11 @@ class SILBuilder {
createInitExistentialMetatype(SILLocation Loc, SILValue metatype,
SILType existentialType,
ArrayRef<ProtocolConformanceRef> conformances) {
if (isInsertingIntoGlobal()) {
return insert(InitExistentialMetatypeInst::create(
getSILDebugLocation(Loc), existentialType, metatype, conformances,
getModule()));
}
return insert(InitExistentialMetatypeInst::create(
getSILDebugLocation(Loc), existentialType, metatype, conformances,
&getFunction()));
Expand Down Expand Up @@ -2283,6 +2288,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()));
}
Expand Down
8 changes: 8 additions & 0 deletions include/swift/SIL/SILInstruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -7431,6 +7431,9 @@ class MetatypeInst final
ArrayRef<SILValue> TypeDependentOperands)
: NullaryInstructionWithTypeDependentOperandsBase(DebugLoc,
TypeDependentOperands, Metatype) {}

static MetatypeInst *create(SILDebugLocation DebugLoc, SILType Metatype,
SILModule &Mod);

static MetatypeInst *create(SILDebugLocation DebugLoc, SILType Metatype,
SILFunction *F);
Expand Down Expand Up @@ -8108,6 +8111,11 @@ class InitExistentialMetatypeInst final
ArrayRef<SILValue> TypeDependentOperands,
ArrayRef<ProtocolConformanceRef> conformances);

static InitExistentialMetatypeInst *
create(SILDebugLocation DebugLoc, SILType existentialMetatypeType,
SILValue metatype, ArrayRef<ProtocolConformanceRef> conformances,
SILModule &mod);

static InitExistentialMetatypeInst *
create(SILDebugLocation DebugLoc, SILType existentialMetatypeType,
SILValue metatype, ArrayRef<ProtocolConformanceRef> conformances,
Expand Down
21 changes: 21 additions & 0 deletions lib/IRGen/GenConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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;
Expand Down Expand Up @@ -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<MetatypeInst>(operand)) {
auto metaTy = mti->getType().castTo<MetatypeType>();
auto type = metaTy.getInstanceType();
return IGM.getAddrOfTypeMetadata(type);
} else if (auto *iemi = dyn_cast<InitExistentialMetatypeInst>(operand)) {
auto *mti =
dyn_cast<MetatypeInst>(iemi->getOperand().getDefiningInstruction());
ASSERT(mti != nullptr && "couldn't constant fold initializer expression");

auto metaTy = mti->getType().castTo<MetatypeType>();
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<AddressToPointerInst>(operand)) {
auto *val = emitConstantValue(IGM, atp->getOperand()).claimNextConstant();
return val;
Expand Down
34 changes: 34 additions & 0 deletions lib/IRGen/GenExistential.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<ProtocolConformanceRef> conformances) {
assert(outType.is<ExistentialMetatypeType>());
auto &destTI = IGM.getTypeInfo(outType).as<ExistentialMetatypeTypeInfo>();
out.add(metatype);

auto srcType = metatypeType.castTo<MetatypeType>().getInstanceType();
auto destType = outType.castTo<ExistentialMetatypeType>().getInstanceType();
while (auto destMetatypeType = dyn_cast<ExistentialMetatypeType>(destType)) {
destType = destMetatypeType.getInstanceType();
srcType = cast<AnyMetatypeType>(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());
Expand Down
25 changes: 15 additions & 10 deletions lib/IRGen/GenExistential.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -44,16 +45,20 @@ namespace irgen {
SILType loweredSrcType,
ArrayRef<ProtocolConformanceRef> 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<ProtocolConformanceRef> 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<ProtocolConformanceRef> 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<ProtocolConformanceRef> conformances);

/// Emit a class existential container from a class instance value
/// as an explosion.
void emitClassExistentialContainer(IRGenFunction &IGF,
Expand Down
18 changes: 18 additions & 0 deletions lib/SIL/IR/SILInstructions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2327,6 +2327,17 @@ InitExistentialMetatypeInst::InitExistentialMetatypeInst(
getTrailingObjects<ProtocolConformanceRef>());
}

InitExistentialMetatypeInst *InitExistentialMetatypeInst::create(
SILDebugLocation Loc, SILType existentialMetatypeType, SILValue metatype,
ArrayRef<ProtocolConformanceRef> conformances, SILModule &M) {
unsigned size = totalSizeToAlloc<swift::Operand, ProtocolConformanceRef>(
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<ProtocolConformanceRef> conformances, SILFunction *F) {
Expand Down Expand Up @@ -2737,6 +2748,13 @@ CheckedCastBranchInst *CheckedCastBranchInst::create(
Target2Count, forwardingOwnershipKind, preservesOwnership);
}

MetatypeInst *MetatypeInst::create(SILDebugLocation Loc, SILType Ty,
SILModule &Mod) {
auto Size = totalSizeToAlloc<swift::Operand>(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();
Expand Down
21 changes: 21 additions & 0 deletions lib/Sema/LegalConstExprVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -261,6 +263,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<ErasureExpr>(expr)) {
if (const DotSelfExpr *dotSelfExpr = dyn_cast<DotSelfExpr>(erasureExpr->getSubExpr())) {
if (const TypeExpr *typeExpr = dyn_cast<TypeExpr>(dotSelfExpr->getSubExpr())) {
auto baseType = typeExpr->getType();
if (baseType && baseType->is<MetatypeType>()) {
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<ApplyExpr>(expr))
Expand Down
16 changes: 14 additions & 2 deletions test/ConstValues/SectionIR.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,14 @@ func bar(x: Int) -> String { return "test" }
@section("mysection") let funcRef1 = foo // ok
@section("mysection") let funcRef2 = bar // ok

// 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add tests for bound generic types, e.g. Array<Int>.self?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...but I did add that as a SIL test into test/SILOptimizer/static_init_metatypes.swift.


// tuples
@section("mysection") let tuple1 = (1, 2, 3, 2.718, true) // ok
Expand All @@ -56,6 +62,12 @@ func bar(x: Int) -> String { return "test" }
// CHECK: @"$s9SectionIR12boolLiteral2Sbvp" = {{.*}}constant %TSb zeroinitializer, section "mysection"
// CHECK: @"$s9SectionIR8funcRef1Siycvp" = {{.*}}constant %swift.function { ptr @"$s9SectionIR3fooSiyF{{.*}}", ptr null }, section "mysection"
// CHECK: @"$s9SectionIR8funcRef2ySSSicvp" = {{.*}}constant %swift.function { ptr @"$s9SectionIR3bar1xSSSi_tF{{.*}}", ptr null }, 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"
Expand Down
3 changes: 3 additions & 0 deletions test/ConstValues/SectionSyntactic.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,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
Expand Down
57 changes: 57 additions & 0 deletions test/SILOptimizer/static_init_metatypes.swift
Original file line number Diff line number Diff line change
@@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also add a -emit-ir invocation to make sure that IRGen can deal with this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that reasonable

Yes, that should be fine. As long as IRGen can handle those SIL initializer instructions.


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<Int>.self
public let metatype7: any (Hashable & Sendable).Type = Array<Int>.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<Int>.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<Int>.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<Int>.self : Array<Bool>.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) -> () {