Merge pull request #85551 from aschwaighofer/wip_embedded_exit

Preliminary support for existential in embedded Swift
This commit is contained in:
Arnold Schwaighofer
2025-11-18 15:10:40 -05:00
committed by GitHub
21 changed files with 721 additions and 45 deletions

View File

@@ -89,11 +89,18 @@ enum class TypeMetadataAddress {
inline bool isEmbedded(CanType t) {
return t->getASTContext().LangOpts.hasFeature(Feature::Embedded);
}
inline bool isEmbeddedWithoutEmbeddedExitentials(CanType t) {
auto &langOpts = t->getASTContext().LangOpts;
return langOpts.hasFeature(Feature::Embedded) &&
!langOpts.hasFeature(Feature::EmbeddedExistentials);
}
// Metadata is not generated and not allowed to be referenced in Embedded Swift,
// expect for classes (both generic and non-generic), dynamic self, and
// class-bound existentials.
inline bool isMetadataAllowedInEmbedded(CanType t) {
bool embeddedExistentials =
t->getASTContext().LangOpts.hasFeature(Feature::EmbeddedExistentials);
if (isa<ClassType>(t) || isa<BoundGenericClassType>(t) ||
isa<DynamicSelfType>(t)) {
return true;
@@ -106,6 +113,9 @@ inline bool isMetadataAllowedInEmbedded(CanType t) {
if (archeTy->requiresClass())
return true;
}
if (embeddedExistentials)
return true;
return false;
}
@@ -117,6 +127,11 @@ inline bool isEmbedded(const ProtocolConformance *c) {
return c->getType()->getASTContext().LangOpts.hasFeature(Feature::Embedded);
}
inline bool isEmbeddedWithoutEmbeddedExitentials(const ProtocolConformance *c) {
return isEmbedded(c) && !c->getType()->getASTContext().
LangOpts.hasFeature(Feature::EmbeddedExistentials);
}
/// A link entity is some sort of named declaration, combined with all
/// the information necessary to distinguish specific implementations
/// of the declaration from each other.
@@ -1098,7 +1113,7 @@ public:
}
static LinkEntity forValueWitnessTable(CanType type) {
assert(!isEmbedded(type));
assert(!isEmbeddedWithoutEmbeddedExitentials(type));
LinkEntity entity;
entity.setForType(Kind::ValueWitnessTable, type);
return entity;
@@ -1128,7 +1143,7 @@ public:
}
static LinkEntity forProtocolWitnessTable(const ProtocolConformance *C) {
if (isEmbedded(C)) {
if (isEmbeddedWithoutEmbeddedExitentials(C)) {
assert(C->getProtocol()->requiresClass());
}

View File

@@ -236,6 +236,14 @@ FUNCTION(NativeStrongReleaseDirect, Swift, swift_releaseDirect, SwiftDirectRR_CC
EFFECT(RuntimeEffect::RefCounting, RuntimeEffect::Deallocating),
UNKNOWN_MEMEFFECTS)
// void swift_releaseBox(void *ptr);
FUNCTION(ReleaseBox, Swift, swift_releaseBox, C_CC, AlwaysAvailable,
RETURNS(VoidTy),
ARGS(RefCountedPtrTy),
ATTRS(NoUnwind),
EFFECT(RuntimeEffect::RefCounting, RuntimeEffect::Deallocating),
UNKNOWN_MEMEFFECTS)
// void *swift_retain_n(void *ptr, int32_t n);
FUNCTION(NativeStrongRetainN, Swift, swift_retain_n, C_CC, AlwaysAvailable,
RETURNS(RefCountedPtrTy),

View File

@@ -77,6 +77,8 @@ public:
// The regular `layout` method can be used for layout tasks for which the
// actual superclass pointer is not relevant.
void layoutEmbedded(CanType classTy) {
if (IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials))
asImpl().addValueWitnessTable();
asImpl().noteAddressPoint();
asImpl().addEmbeddedSuperclass(classTy);
asImpl().addDestructorFunction();
@@ -89,6 +91,8 @@ public:
"Adjustment index must be synchronized with this layout");
if (IGM.Context.LangOpts.hasFeature(Feature::Embedded)) {
if (IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials))
asImpl().addValueWitnessTable();
asImpl().noteAddressPoint();
asImpl().addSuperclass();
asImpl().addDestructorFunction();

View File

@@ -43,6 +43,14 @@ protected:
: super(IGM), Target(target) {}
public:
void embeddedLayout() {
// The embedded layout consists of:
// + // -1 : vwt
// + // 0 : metadata flags
super::layout();
}
void layout() {
static_assert(MetadataAdjustmentIndex::ValueType == 2,
"Adjustment index must be synchronized with this layout");

View File

@@ -1361,7 +1361,9 @@ deleteAndReenqueueForEmissionValuesDependentOnCanonicalPrespecializedMetadataRec
void IRGenerator::emitLazyDefinitions() {
if (SIL.getASTContext().LangOpts.hasFeature(Feature::Embedded)) {
// In embedded Swift, the compiler cannot emit any metadata, etc.
assert(LazyTypeMetadata.empty());
// Other than to support existentials.
assert(LazyTypeMetadata.empty() ||
SIL.getASTContext().LangOpts.hasFeature(Feature::EmbeddedExistentials));
assert(LazySpecializedTypeMetadataRecords.empty());
assert(LazyTypeContextDescriptors.empty());
assert(LazyOpaqueTypeDescriptors.empty());
@@ -1388,7 +1390,8 @@ void IRGenerator::emitLazyDefinitions() {
!LazyCanonicalSpecializedMetadataAccessors.empty() ||
!LazyMetadataAccessors.empty() ||
!LazyClassMetadata.empty() ||
!LazySpecializedClassMetadata.empty()
!LazySpecializedClassMetadata.empty() ||
!LazySpecializedValueMetadata.empty()
) {
// Emit any lazy type metadata we require.
while (!LazyTypeMetadata.empty()) {
@@ -1514,6 +1517,12 @@ void IRGenerator::emitLazyDefinitions() {
CurrentIGMPtr IGM = getGenModule(classType->getClassOrBoundGenericClass());
emitLazySpecializedClassMetadata(*IGM.get(), classType);
}
while(!LazySpecializedValueMetadata.empty()) {
CanType valueType = LazySpecializedValueMetadata.pop_back_val();
CurrentIGMPtr IGM = getGenModule(valueType->getNominalOrBoundGenericNominal());
emitLazySpecializedValueMetadata(*IGM.get(), valueType);
}
}
FinishedEmittingLazyDefinitions = true;
@@ -1580,6 +1589,14 @@ bool IRGenerator::hasLazyMetadata(TypeDecl *type) {
if (found != HasLazyMetadata.end())
return found->second;
if (SIL.getASTContext().LangOpts.hasFeature(Feature::EmbeddedExistentials) &&
(isa<StructDecl>(type) || isa<EnumDecl>(type))) {
bool isGeneric = cast<NominalTypeDecl>(type)->isGenericContext();
HasLazyMetadata[type] = !isGeneric;
return !isGeneric;
}
auto canBeLazy = [&]() -> bool {
auto *dc = type->getDeclContext();
if (isa<ClangModuleUnit>(dc->getModuleScopeContext())) {
@@ -1628,11 +1645,17 @@ void IRGenerator::noteUseOfClassMetadata(CanType classType) {
}
void IRGenerator::noteUseOfSpecializedClassMetadata(CanType classType) {
if (LazilyEmittedSpecializedClassMetadata.insert(classType.getPointer()).second) {
if (LazilyEmittedSpecializedMetadata.insert(classType.getPointer()).second) {
LazySpecializedClassMetadata.push_back(classType);
}
}
void IRGenerator::noteUseOfSpecializedValueMetadata(CanType valueType) {
if (LazilyEmittedSpecializedMetadata.insert(valueType.getPointer()).second) {
LazySpecializedValueMetadata.push_back(valueType);
}
}
void IRGenerator::noteUseOfTypeGlobals(NominalTypeDecl *type,
bool isUseOfMetadata,
RequireMetadata_t requireMetadata) {
@@ -5298,6 +5321,9 @@ llvm::GlobalValue *IRGenModule::defineTypeMetadata(
if (Context.LangOpts.hasFeature(Feature::Embedded)) {
entity = LinkEntity::forTypeMetadata(concreteType,
TypeMetadataAddress::AddressPoint);
if (Context.LangOpts.hasFeature(Feature::EmbeddedExistentials))
entity = LinkEntity::forTypeMetadata(concreteType,
TypeMetadataAddress::FullMetadata);
}
auto DbgTy = DebugTypeInfo::getGlobalMetadata(MetatypeType::get(concreteType),
@@ -5320,7 +5346,8 @@ llvm::GlobalValue *IRGenModule::defineTypeMetadata(
LinkInfo link = LinkInfo::get(*this, entity, ForDefinition);
markGlobalAsUsedBasedOnLinkage(*this, link, var);
if (Context.LangOpts.hasFeature(Feature::Embedded)) {
if (Context.LangOpts.hasFeature(Feature::Embedded) &&
!Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
return var;
}
@@ -5331,12 +5358,15 @@ llvm::GlobalValue *IRGenModule::defineTypeMetadata(
if (auto nominal = concreteType->getAnyNominal()) {
// Keep type metadata around for all types (except @_objcImplementation,
// since we're using ObjC metadata for that).
if (!isObjCImpl)
if (!isObjCImpl &&
!Context.LangOpts.hasFeature(Feature::EmbeddedExistentials))
addRuntimeResolvableType(nominal);
// Don't define the alias for foreign type metadata, prespecialized
// generic metadata, or @_objcImplementation classes, since they're not ABI.
if (requiresForeignTypeMetadata(nominal) || isPrespecialized || isObjCImpl)
if (requiresForeignTypeMetadata(nominal) ||
(isPrespecialized && !Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) ||
isObjCImpl)
return var;
// Native Swift class metadata has a destructor before the address point.
@@ -5349,6 +5379,10 @@ llvm::GlobalValue *IRGenModule::defineTypeMetadata(
}
}
if (Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
adjustmentIndex = MetadataAdjustmentIndex::EmbeddedWithExistentials;
}
llvm::Constant *indices[] = {
llvm::ConstantInt::get(Int32Ty, 0),
llvm::ConstantInt::get(Int32Ty, adjustmentIndex)};
@@ -5390,7 +5424,10 @@ IRGenModule::getAddrOfTypeMetadata(CanType concreteType,
llvm::Type *defaultVarTy;
unsigned adjustmentIndex;
if (concreteType->isAny() || concreteType->isAnyObject() || concreteType->isVoid() || concreteType->is<TupleType>() || concreteType->is<BuiltinType>()) {
if (Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
adjustmentIndex = 0;
defaultVarTy = EmbeddedExistentialsMetadataStructTy;
} else if (concreteType->isAny() || concreteType->isAnyObject() || concreteType->isVoid() || concreteType->is<TupleType>() || concreteType->is<BuiltinType>()) {
defaultVarTy = FullExistentialTypeMetadataStructTy;
adjustmentIndex = MetadataAdjustmentIndex::NoTypeLayoutString;
} else if (fullMetadata) {
@@ -5433,6 +5470,18 @@ IRGenModule::getAddrOfTypeMetadata(CanType concreteType,
}
}
}
if (Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
if ((isa<StructDecl>(nominal) || isa<EnumDecl>(nominal)) &&
nominal->isGenericContext()) {
IRGen.noteUseOfSpecializedValueMetadata(concreteType);
}
}
}
if (Context.LangOpts.hasFeature(Feature::EmbeddedExistentials) &&
isa<TupleType>(concreteType)) {
IRGen.noteUseOfSpecializedValueMetadata(concreteType);
}
if (shouldPrespecializeGenericMetadata()) {

View File

@@ -2314,7 +2314,21 @@ Address irgen::emitAllocateBoxedOpaqueExistentialBuffer(
if (fixedTI->getFixedPacking(IGF.IGM) == FixedPacking::OffsetZero) {
return valueTI.getAddressForPointer(IGF.Builder.CreateBitCast(
existentialBuffer.getAddress(), IGF.IGM.PtrTy));
} else if (IGF.IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
llvm::Value *box, *address;
auto *metadata = existLayout.loadMetadataRef(IGF, existentialContainer);
IGF.emitAllocBoxCall(metadata, box, address);
llvm::Value *addressInBox =
IGF.Builder.CreateBitCast(address, IGF.IGM.OpaquePtrTy);
IGF.Builder.CreateStore(
box, Address(IGF.Builder.CreateBitCast(
existentialBuffer.getAddress(), IGF.IGM.PtrTy),
IGF.IGM.RefCountedPtrTy,
existLayout.getAlignment(IGF.IGM)));
return valueTI.getAddressForPointer(addressInBox);
}
// Otherwise, allocate a box with enough storage.
Address addr = emitAllocateExistentialBoxInBuffer(
IGF, valueType, existentialBuffer, genericEnv, "exist.box.addr",
@@ -2883,7 +2897,11 @@ static llvm::Function *getDestroyBoxedOpaqueExistentialBufferFunction(
Builder.CreateBitCast(buffer.getAddress(), IGM.PtrTy);
auto *reference = Builder.CreateLoad(Address(
referenceAddr, IGM.RefCountedPtrTy, buffer.getAlignment()));
IGF.emitNativeStrongRelease(reference, IGF.getDefaultAtomicity());
if (IGF.IGM.Context.LangOpts
.hasFeature(Feature::EmbeddedExistentials)) {
IGF.emitReleaseBox(reference);
} else
IGF.emitNativeStrongRelease(reference, IGF.getDefaultAtomicity());
Builder.CreateRetVoid();
}

View File

@@ -1274,6 +1274,12 @@ void IRGenFunction::emitNativeStrongRelease(llvm::Value *value,
emitUnaryRefCountCall(*this, function, value);
}
void IRGenFunction::emitReleaseBox(llvm::Value *value) {
if (doesNotRequireRefCounting(value))
return;
emitUnaryRefCountCall(*this, IGM.getReleaseBoxFn(), value);
}
void IRGenFunction::emitNativeSetDeallocating(llvm::Value *value) {
if (doesNotRequireRefCounting(value)) return;
emitUnaryRefCountCall(*this, IGM.getNativeSetDeallocatingFn(), value);

View File

@@ -75,6 +75,7 @@
#include "ScalarTypeInfo.h"
#include "StructLayout.h"
#include "StructMetadataVisitor.h"
#include "TupleMetadataVisitor.h"
#include "GenMeta.h"
@@ -2969,7 +2970,10 @@ void irgen::emitLazyTypeContextDescriptor(IRGenModule &IGM,
}
void irgen::emitLazyTypeMetadata(IRGenModule &IGM, NominalTypeDecl *type) {
eraseExistingTypeContextDescriptor(IGM, type);
// Embedded existentials emit very spares metadata records and don't have type
// context descriptors.
if (!type->getASTContext().LangOpts.hasFeature(Feature::EmbeddedExistentials))
eraseExistingTypeContextDescriptor(IGM, type);
if (requiresForeignTypeMetadata(type)) {
emitForeignTypeMetadata(IGM, type);
@@ -4265,6 +4269,9 @@ namespace {
auto type = (Target->checkAncestry(AncestryFlags::ObjC)
? IGM.Context.getAnyObjectType()
: IGM.Context.TheNativeObjectType);
if (IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
return irgen::emitValueWitnessTable(IGM, type, false, false);
}
auto wtable = IGM.getAddrOfValueWitnessTable(type);
return ConstantReference(wtable,
swift::irgen::ConstantReference::Direct);
@@ -5570,6 +5577,9 @@ void irgen::emitLazyClassMetadata(IRGenModule &IGM, CanType classTy) {
// Might already be emitted, skip if that's the case.
auto entity =
LinkEntity::forTypeMetadata(classTy, TypeMetadataAddress::AddressPoint);
if (IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
entity = LinkEntity::forTypeMetadata(classTy, TypeMetadataAddress::FullMetadata);
}
auto *existingVar = cast<llvm::GlobalVariable>(
IGM.getAddrOfLLVMVariable(entity, ConstantInit(), DebugTypeInfo()));
if (!existingVar->isDeclaration()) {
@@ -5597,6 +5607,23 @@ void irgen::emitLazySpecializedClassMetadata(IRGenModule &IGM,
emitEmbeddedVTable(IGM, classTy, vtable);
}
void irgen::emitLazySpecializedValueMetadata(IRGenModule &IGM,
CanType valueTy) {
auto &context = valueTy->getASTContext();
PrettyStackTraceType stackTraceRAII(
context, "emitting lazy specialized value metadata for", valueTy);
if (isa<TupleType>(valueTy)) {
emitLazyTupleMetadata(IGM, valueTy);
} else if (valueTy->getStructOrBoundGenericStruct()) {
emitSpecializedGenericStructMetadata(IGM, valueTy,
*valueTy.getStructOrBoundGenericStruct());
} else {
emitSpecializedGenericEnumMetadata(IGM, valueTy,
*valueTy.getEnumOrBoundGenericEnum());
}
}
void irgen::emitSpecializedGenericClassMetadata(IRGenModule &IGM, CanType type,
ClassDecl &decl) {
assert(decl.isGenericContext());
@@ -6189,7 +6216,10 @@ void irgen::emitStructMetadata(IRGenModule &IGM, StructDecl *structDecl) {
bool isPattern;
bool canBeConstant;
bool hasEmbeddedExistentialFeature =
IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials);
if (structDecl->isGenericContext()) {
assert(!hasEmbeddedExistentialFeature);
GenericStructMetadataBuilder builder(IGM, structDecl, init);
builder.layout();
isPattern = true;
@@ -6198,11 +6228,16 @@ void irgen::emitStructMetadata(IRGenModule &IGM, StructDecl *structDecl) {
builder.createMetadataAccessFunction();
} else {
StructMetadataBuilder builder(IGM, structDecl, init);
builder.layout();
if (hasEmbeddedExistentialFeature) {
builder.embeddedLayout();
} else {
builder.layout();
}
isPattern = false;
canBeConstant = builder.canBeConstant();
builder.createMetadataAccessFunction();
if (!hasEmbeddedExistentialFeature)
builder.createMetadataAccessFunction();
}
CanType declaredType = structDecl->getDeclaredType()->getCanonicalType();
@@ -6224,13 +6259,74 @@ void irgen::emitSpecializedGenericStructMetadata(IRGenModule &IGM, CanType type,
bool isPattern = false;
SpecializedGenericStructMetadataBuilder builder(IGM, type, decl, init);
builder.layout();
if (IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
builder.embeddedLayout();
} else {
builder.layout();
}
bool canBeConstant = builder.canBeConstant();
IGM.defineTypeMetadata(type, isPattern, canBeConstant,
init.finishAndCreateFuture());
}
// Tuples (currently only used in embedded existentials mode)
//
namespace {
class TupleMetadataBuilder : public TupleMetadataVisitor<TupleMetadataBuilder> {
using super = TupleMetadataVisitor<TupleMetadataBuilder>;
ConstantStructBuilder &B;
protected:
using super::asImpl;
using super::IGM;
using super::Target;
public:
TupleMetadataBuilder(IRGenModule &IGM, CanType tupleTy, ConstantStructBuilder &B) :
super(IGM, cast<TupleType>(tupleTy)), B(B) {}
ConstantReference emitValueWitnessTable(bool relativeReference) {
return irgen::emitValueWitnessTable(IGM, Target->getCanonicalType(),
false, relativeReference);
}
void addMetadataFlags() {
B.addInt(IGM.MetadataKindTy, unsigned(MetadataKind::Tuple));
}
void addValueWitnessTable() {
auto vwtPointer = emitValueWitnessTable(false).getValue();
B.addSignedPointer(vwtPointer,
IGM.getOptions().PointerAuth.ValueWitnessTable,
PointerAuthEntity());
}
};
} // end anonymous namespace
void irgen::emitLazyTupleMetadata(IRGenModule &IGM, CanType tupleTy) {
assert(IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials));
assert(isa<TupleType>(tupleTy));
Type ty = tupleTy.getPointer();
auto &context = ty->getASTContext();
PrettyStackTraceType stackTraceRAII(
context, "emitting prespecialized metadata for", ty);
ConstantInitBuilder initBuilder(IGM);
auto init = initBuilder.beginStruct();
init.setPacked(true);
bool isPattern = false;
bool canBeConstant = true;
TupleMetadataBuilder builder(IGM, tupleTy, init);
builder.embeddedLayout();
IGM.defineTypeMetadata(tupleTy, isPattern, canBeConstant,
init.finishAndCreateFuture());
}
// Enums
static std::optional<Size>
@@ -6606,7 +6702,10 @@ void irgen::emitEnumMetadata(IRGenModule &IGM, EnumDecl *theEnum) {
bool isPattern;
bool canBeConstant;
bool hasEmbeddedExistentialFeature =
IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials);
if (theEnum->isGenericContext()) {
assert(!hasEmbeddedExistentialFeature);
GenericEnumMetadataBuilder builder(IGM, theEnum, init);
builder.layout();
isPattern = true;
@@ -6615,11 +6714,16 @@ void irgen::emitEnumMetadata(IRGenModule &IGM, EnumDecl *theEnum) {
builder.createMetadataAccessFunction();
} else {
EnumMetadataBuilder builder(IGM, theEnum, init);
builder.layout();
if (hasEmbeddedExistentialFeature) {
builder.embeddedLayout();
} else {
builder.layout();
}
isPattern = false;
canBeConstant = builder.canBeConstant();
builder.createMetadataAccessFunction();
if (!hasEmbeddedExistentialFeature)
builder.createMetadataAccessFunction();
}
CanType declaredType = theEnum->getDeclaredType()->getCanonicalType();
@@ -6640,7 +6744,11 @@ void irgen::emitSpecializedGenericEnumMetadata(IRGenModule &IGM, CanType type,
init.setPacked(true);
SpecializedGenericEnumMetadataBuilder builder(IGM, type, decl, init);
builder.layout();
if (IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
builder.embeddedLayout();
} else {
builder.layout();
}
bool canBeConstant = builder.canBeConstant();
IGM.defineTypeMetadata(type, /*isPattern=*/false, canBeConstant,

View File

@@ -86,6 +86,7 @@ namespace irgen {
void emitLazyClassMetadata(IRGenModule &IGM, CanType classType);
void emitLazySpecializedClassMetadata(IRGenModule &IGM, CanType classType);
void emitLazySpecializedValueMetadata(IRGenModule &IGM, CanType valueTy);
void emitLazyCanonicalSpecializedMetadataAccessor(IRGenModule &IGM,
CanType theType);
@@ -110,6 +111,9 @@ namespace irgen {
void emitSpecializedGenericEnumMetadata(IRGenModule &IGM, CanType type,
EnumDecl &decl);
/// Emit the metadata associated with a given tuple type.
void emitLazyTupleMetadata(IRGenModule &IGM, CanType type);
/// Emit the metadata associated with a given instantiation of a generic
// class.
void emitSpecializedGenericClassMetadata(IRGenModule &IGM, CanType type,
@@ -194,6 +198,12 @@ namespace irgen {
// Class metadata has two words of head-allocated data: the destructor
// and the value witness table.
Class = 3,
// In Embedded with existentials all metadata is a record with a value
// witness prepended.
// -1: vwt
// 0: metadata flags (unused)
EmbeddedWithExistentials = 1,
// Struct and enum metadata have one word of head-allocated data:
// the value witness table.

View File

@@ -962,13 +962,17 @@ namespace {
void addAssociatedType(AssociatedTypeDecl *assocType) {
// In Embedded Swift witness tables don't have associated-types entries.
if (assocType->getASTContext().LangOpts.hasFeature(Feature::Embedded))
auto &langOpts = assocType->getASTContext().LangOpts;
if (langOpts.hasFeature(Feature::Embedded) &&
!langOpts.hasFeature(Feature::EmbeddedExistentials))
return;
Entries.push_back(WitnessTableEntry::forAssociatedType(assocType));
}
void addAssociatedConformance(const AssociatedConformance &req) {
if (req.getAssociation()->getASTContext().LangOpts.hasFeature(Feature::Embedded) &&
auto &langOpts = req.getAssociation()->getASTContext().LangOpts;
if (langOpts.hasFeature(Feature::Embedded) &&
!langOpts.hasFeature(Feature::EmbeddedExistentials) &&
!req.getAssociatedRequirement()->requiresClass()) {
// If it's not a class protocol, the associated type can never be used to create
// an existential. Therefore this witness entry is never used at runtime
@@ -1728,7 +1732,9 @@ public:
SILEntries = SILEntries.slice(1);
// In Embedded Swift witness tables don't have associated-types entries.
if (IGM.Context.LangOpts.hasFeature(Feature::Embedded))
auto &langOpts = IGM.Context.LangOpts;
if (langOpts.hasFeature(Feature::Embedded) &&
!langOpts.hasFeature(Feature::EmbeddedExistentials))
return;
#ifndef NDEBUG
@@ -1745,6 +1751,17 @@ public:
#endif
auto typeWitness = Conformance.getTypeWitness(assocType);
if (IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
// In Embedded Swift associated type witness point to the metadata.
llvm::Constant *witnessEntry = IGM.getAddrOfTypeMetadata(
typeWitness->getCanonicalType());
auto &schema = IGM.getOptions().PointerAuth
.ProtocolAssociatedTypeAccessFunctions;
Table.addSignedPointer(witnessEntry, schema, assocType);
return;
}
llvm::Constant *typeWitnessAddr =
IGM.getAssociatedTypeWitness(
typeWitness,
@@ -1768,8 +1785,9 @@ public:
auto &entry = SILEntries.front();
(void)entry;
SILEntries = SILEntries.slice(1);
if (IGM.Context.LangOpts.hasFeature(Feature::Embedded) &&
auto &langOpts = IGM.Context.LangOpts;
if (langOpts.hasFeature(Feature::Embedded) &&
!langOpts.hasFeature(Feature::EmbeddedExistentials) &&
!requirement.getAssociatedRequirement()->requiresClass()) {
// If it's not a class protocol, the associated type can never be used to create
// an existential. Therefore this witness entry is never used at runtime
@@ -2731,7 +2749,8 @@ static void addWTableTypeMetadata(IRGenModule &IGM,
void IRGenModule::emitSILWitnessTable(SILWitnessTable *wt) {
if (Context.LangOpts.hasFeature(Feature::Embedded)) {
// In Embedded Swift, only class-bound wtables are allowed.
if (!wt->getConformance()->getProtocol()->requiresClass())
if (!wt->getConformance()->getProtocol()->requiresClass() &&
!Context.LangOpts.hasFeature(Feature::EmbeddedExistentials))
return;
}
@@ -3752,7 +3771,9 @@ llvm::Value *irgen::emitWitnessTableRef(IRGenFunction &IGF,
auto proto = conformance.getProtocol();
// In Embedded Swift, only class-bound wtables are allowed.
if (srcType->getASTContext().LangOpts.hasFeature(Feature::Embedded)) {
auto &langOpts = srcType->getASTContext().LangOpts;
if (langOpts.hasFeature(Feature::Embedded) &&
!langOpts.hasFeature(Feature::EmbeddedExistentials)) {
assert(proto->requiresClass());
}
@@ -4531,6 +4552,28 @@ irgen::emitAssociatedTypeMetadataRef(IRGenFunction &IGF,
DynamicMetadataRequest request) {
auto &IGM = IGF.IGM;
// Im embedded with existentials mode the type metadata is directly referenced
// by the witness table.
if (IGF.IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
auto proto = assocType->getProtocol();
assert(!IGF.IGM.isResilient(proto, ResilienceExpansion::Maximal));
assert(!IGF.IGM.IRGen.Opts.UseRelativeProtocolWitnessTables);
auto &protoInfo = IGF.IGM.getProtocolInfo(proto, ProtocolInfoKind::Full);
auto index = protoInfo.getAssociatedTypeIndex(IGM, assocType);
auto slot =
slotForLoadOfOpaqueWitness(IGF, wtable, index.forProtocolWitnessTable(),
false/*isRelativeTable*/);
llvm::Value *assocTypeMetadata = IGF.emitInvariantLoad(slot);
if (auto &schema = IGF.getOptions().PointerAuth.ProtocolAssociatedTypeAccessFunctions) {
auto authInfo = PointerAuthInfo::emit(IGF, schema, slot.getAddress(), assocType);
assocTypeMetadata = emitPointerAuthAuth(IGF, assocTypeMetadata, authInfo);
}
return MetadataResponse::forComplete(assocTypeMetadata);
}
// Extract the requirements base descriptor.
auto reqBaseDescriptor =
IGM.getAddrOfProtocolRequirementsBaseDescriptor(

View File

@@ -1380,17 +1380,24 @@ static void addValueWitnessesForAbstractType(IRGenModule &IGM,
CanType abstractType,
bool &canBeConstant) {
std::optional<BoundGenericTypeCharacteristics> boundGenericCharacteristics;
if (auto boundGenericType = dyn_cast<BoundGenericType>(abstractType)) {
CanType concreteFormalType = getFormalTypeInPrimaryContext(abstractType);
// Embedded existentials need fully "specialized" value witness table
// functions: i.e the metadata argument must remain unused.
// For the non-embedded existentials path, when there is a bound generic type,
// that is we have a specialized generic type, we have decided for code size
// reasons to continue using "generic" value witness table functions i.e the
// same once used for runtime instantiated generic metadata.
if (!IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
if (auto boundGenericType = dyn_cast<BoundGenericType>(abstractType)) {
CanType concreteFormalType = getFormalTypeInPrimaryContext(abstractType);
auto concreteLoweredType = IGM.getLoweredType(concreteFormalType);
const auto *boundConcreteTI = &IGM.getTypeInfo(concreteLoweredType);
auto packing = boundConcreteTI->getFixedPacking(IGM);
boundGenericCharacteristics = {concreteLoweredType, boundConcreteTI,
packing};
abstractType =
boundGenericType->getDecl()->getDeclaredType()->getCanonicalType();
auto concreteLoweredType = IGM.getLoweredType(concreteFormalType);
const auto *boundConcreteTI = &IGM.getTypeInfo(concreteLoweredType);
auto packing = boundConcreteTI->getFixedPacking(IGM);
boundGenericCharacteristics = {concreteLoweredType, boundConcreteTI,
packing};
abstractType =
boundGenericType->getDecl()->getDeclaredType()->getCanonicalType();
}
}
CanType concreteFormalType = getFormalTypeInPrimaryContext(abstractType);
@@ -1533,13 +1540,22 @@ ConstantReference irgen::emitValueWitnessTable(IRGenModule &IGM,
bool isPattern,
bool relativeReference) {
// See if we can use a prefab witness table from the runtime.
if (!isPattern) {
if (!isPattern &&
!IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
if (auto known = getAddrOfKnownValueWitnessTable(IGM, abstractType,
relativeReference)) {
return known;
}
}
// There might already be a definition emitted in embedded mode.
if (IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
auto addr = IGM.getAddrOfValueWitnessTable(abstractType);
if (!cast<llvm::GlobalVariable>(addr)->isDeclaration()) {
return {addr, ConstantReference::Direct};
}
}
// We should never be making a pattern if the layout isn't fixed.
// The reverse can be true for types whose layout depends on
// resilient types.

View File

@@ -626,6 +626,9 @@ public:
void emitNativeStrongRelease(llvm::Value *value, Atomicity atomicity);
void emitNativeSetDeallocating(llvm::Value *value);
// Routines to deal with box (embedded) runtime calls.
void emitReleaseBox(llvm::Value *value);
// Routines for the ObjC reference-counting style.
void emitObjCStrongRetain(llvm::Value *value);
llvm::Value *emitObjCRetainCall(llvm::Value *value);

View File

@@ -392,6 +392,11 @@ IRGenModule::IRGenModule(IRGenerator &irgen,
TypeMetadataStructTy
});
EmbeddedExistentialsMetadataStructTy = createStructType(*this, "swift.embedded_existential_type", {
WitnessTablePtrTy,
TypeMetadataStructTy
});
// A full heap metadata is basically just an additional small prefix
// on a full metadata, used for metadata corresponding to heap
// allocations.
@@ -1481,7 +1486,9 @@ bool IRGenerator::canEmitWitnessTableLazily(SILWitnessTable *wt) {
void IRGenerator::addLazyWitnessTable(const ProtocolConformance *Conf) {
// In Embedded Swift, only class-bound wtables are allowed.
if (SIL.getASTContext().LangOpts.hasFeature(Feature::Embedded)) {
auto &langOpts = SIL.getASTContext().LangOpts;
if (langOpts.hasFeature(Feature::Embedded) &&
!langOpts.hasFeature(Feature::EmbeddedExistentials)) {
assert(Conf->getProtocol()->requiresClass());
}

View File

@@ -349,8 +349,9 @@ private:
llvm::SmallPtrSet<TypeBase *, 4> LazilyEmittedClassMetadata;
llvm::SmallVector<CanType, 4> LazySpecializedClassMetadata;
llvm::SmallVector<CanType, 4> LazySpecializedValueMetadata;
llvm::SmallPtrSet<TypeBase *, 4> LazilyEmittedSpecializedClassMetadata;
llvm::SmallPtrSet<TypeBase *, 4> LazilyEmittedSpecializedMetadata;
llvm::SmallVector<ClassDecl *, 4> ClassesForEagerInitialization;
@@ -513,6 +514,7 @@ public:
void noteUseOfClassMetadata(CanType classType);
void noteUseOfSpecializedClassMetadata(CanType classType);
void noteUseOfSpecializedValueMetadata(CanType valueType);
void noteUseOfTypeMetadata(NominalTypeDecl *type) {
noteUseOfTypeGlobals(type, true, RequireMetadata);
@@ -832,6 +834,7 @@ public:
llvm::StructType *TupleTypeMetadataTy; /// %swift.tuple_type
llvm::StructType *FullHeapMetadataStructTy; /// %swift.full_heapmetadata = type { ... }
llvm::StructType *FullBoxMetadataStructTy; /// %swift.full_boxmetadata = type { ... }
llvm::StructType *EmbeddedExistentialsMetadataStructTy;
llvm::StructType *FullTypeMetadataStructTy; /// %swift.full_type = type { ... }
llvm::StructType *FullExistentialTypeMetadataStructTy; /// %swift.full_existential_type = type { ... }
llvm::StructType *FullForeignTypeMetadataStructTy; /// %swift.full_foreign_type = type { ... }

View File

@@ -696,6 +696,12 @@ SILLinkage LinkEntity::getLinkage(ForDefinition_t forDefinition) const {
if (isForcedShared())
return SILLinkage::Shared;
// In embedded existenitals mode we generate metadata for tuple types.
if (getType()->getASTContext().LangOpts.hasFeature(Feature::EmbeddedExistentials) &&
isa<TupleType>(getType())) {
return SILLinkage::Shared;
}
auto *nominal = getType().getAnyNominal();
switch (getMetadataAddress()) {
case TypeMetadataAddress::FullMetadata:
@@ -1120,6 +1126,9 @@ llvm::Type *LinkEntity::getDefaultDeclarationType(IRGenModule &IGM) const {
case Kind::NoncanonicalSpecializedGenericTypeMetadata:
switch (getMetadataAddress()) {
case TypeMetadataAddress::FullMetadata:
if (IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
return IGM.EmbeddedExistentialsMetadataStructTy;
}
if (getType().getClassOrBoundGenericClass())
return IGM.FullHeapMetadataStructTy;
else

View File

@@ -886,6 +886,17 @@ bool irgen::isCompleteSpecializedNominalTypeMetadataStaticallyAddressable(
return false;
}
if (IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials) &&
(isa<StructType>(type) || isa<BoundGenericStructType>(type) ||
isa<EnumType>(type) || isa<BoundGenericEnumType>(type))) {
if (type->hasArchetype()) {
llvm::errs() << "Cannot get metadata of generic class for type "
<< type << "\n";
llvm::report_fatal_error("cannot get metadata for type with archetype");
}
return true;
}
// Prespecialized struct/enum metadata gets no dedicated accessor yet and so
// cannot do the work of registering the generic arguments which are classes
// with the ObjC runtime. Concretely, the following cannot be prespecialized
@@ -1252,6 +1263,10 @@ static MetadataResponse emitFixedArrayMetadataRef(IRGenFunction &IGF,
static MetadataResponse emitTupleTypeMetadataRef(IRGenFunction &IGF,
CanTupleType type,
DynamicMetadataRequest request) {
if (IGF.IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
return MetadataResponse::forComplete(IGF.IGM.getAddrOfTypeMetadata(type));
}
if (type->containsPackExpansionType())
return emitDynamicTupleTypeMetadataRef(IGF, type, request);
@@ -3372,7 +3387,9 @@ llvm::Value *IRGenFunction::emitTypeMetadataRef(CanType type) {
MetadataResponse
IRGenFunction::emitTypeMetadataRef(CanType type,
DynamicMetadataRequest request) {
if (type->getASTContext().LangOpts.hasFeature(Feature::Embedded) &&
auto &langOpts = type->getASTContext().LangOpts;
if (langOpts.hasFeature(Feature::Embedded) &&
!langOpts.hasFeature(Feature::EmbeddedExistentials) &&
!isMetadataAllowedInEmbedded(type)) {
llvm::errs() << "Metadata pointer requested in embedded Swift for type "
<< type << "\n";
@@ -3392,7 +3409,7 @@ IRGenFunction::emitTypeMetadataRef(CanType type,
if (type->hasArchetype() ||
!shouldTypeMetadataAccessUseAccessor(IGM, type) ||
isa<PackType>(type) ||
type->getASTContext().LangOpts.hasFeature(Feature::Embedded)) {
langOpts.hasFeature(Feature::Embedded)) {
return emitDirectTypeMetadataRef(*this, type, request);
}

View File

@@ -43,6 +43,14 @@ protected:
: super(IGM), Target(target) {}
public:
void embeddedLayout() {
// The embedded layout consists of:
// -1 : vwt
// 0 : metadata flags
super::layout();
}
void layout() {
static_assert(MetadataAdjustmentIndex::ValueType == 2,
"Adjustment index must be synchronized with this layout");

View File

@@ -39,6 +39,14 @@ protected:
: super(IGM), Target(target) {}
public:
void embeddedLayout() {
// The embedded layout consists of:
// -1 : vwt
// 0 : metadata flags
super::layout();
}
void layout() {
super::layout();

View File

@@ -83,6 +83,73 @@ static inline void _swift_embedded_set_heap_object_metadata_pointer(void *object
((EmbeddedHeapObject *)object)->metadata = metadata;
}
typedef struct {
void *initializeBufferWithCopyOfBufferFn;
#if __has_feature(ptrauth_calls)
void (* __ptrauth(0, 1, 0x04f8) destroyFn)(void *, void*);
#else
void (*destroyFn)(void *, void*);
#endif
#if __has_feature(ptrauth_calls)
void* (* __ptrauth(0, 1, 0xe3ba) initializeWithCopyFn)(void*, void*, void*);
#else
void* (*initializeWithCopyFn)(void*, void*, void*);
#endif
void *assignWithCopyFn;
void *initializeWithTakeFn;
void *assignWithTakeFn;
void *getEnumTagSinglePayloadFn;
void *storeEnumTagSinglePayload;
__swift_size_t size;
__swift_size_t stride;
unsigned flags;
} EmbeddedValueWitnessTable;
typedef struct {
#if __has_feature(ptrauth_calls)
EmbeddedValueWitnessTable * __ptrauth(2, 1, 0x2e3f) vwt;
#else
EmbeddedValueWitnessTable *vwt;
#endif
} EmbeddedMetaDataPrefix;
static inline __swift_size_t
_swift_embedded_metadata_get_size(void *metadata) {
EmbeddedMetaDataPrefix *fullmeta = (EmbeddedMetaDataPrefix*)&((void **)metadata)[-1];
return fullmeta->vwt->size;
}
static inline __swift_size_t
_swift_embedded_metadata_get_align_mask_impl(EmbeddedMetaDataPrefix *fullMetadata) {
unsigned flags = fullMetadata->vwt->flags;
unsigned embeddedValueWitnessTableFlagsMask = 0xFF;
return flags & embeddedValueWitnessTableFlagsMask;
}
static inline __swift_size_t
_swift_embedded_metadata_get_align_mask(void *metadata) {
EmbeddedMetaDataPrefix *fullmeta = (EmbeddedMetaDataPrefix*)&((void **)metadata)[-1];
return _swift_embedded_metadata_get_align_mask_impl(fullmeta);
}
static inline void
_swift_embedded_invoke_box_destroy(void *object) {
void *metadata = ((EmbeddedHeapObject *)object)->metadata;
EmbeddedMetaDataPrefix *fullmeta = (EmbeddedMetaDataPrefix*)&((void **)metadata)[-1];
__swift_size_t alignMask = _swift_embedded_metadata_get_align_mask_impl(fullmeta);
__swift_size_t headerSize = sizeof(void*) + sizeof(__swift_size_t);
__swift_size_t startOfBoxedValue = (headerSize + alignMask) & ~alignMask;
void *addrInBox = (void *)(((unsigned char *)object) + startOfBoxedValue);
fullmeta->vwt->destroyFn(addrInBox, metadata);
}
static inline void
_swift_embedded_initialize_box(void *metadata, void *newObjectAddr, void *oldObjectAddr) {
EmbeddedMetaDataPrefix *fullmeta = (EmbeddedMetaDataPrefix*)&((void **)metadata)[-1];
fullmeta->vwt->initializeWithCopyFn(newObjectAddr, oldObjectAddr, metadata);
}
#ifdef __cplusplus
} // extern "C"
#endif

View File

@@ -261,6 +261,60 @@ public func swift_allocEmptyBox() -> Builtin.RawPointer {
return box
}
/// The embedded swift_allocBox version is different to the standad one in that
/// we want to avoid building metadata for the box type. Instead we store the
/// metadata of the contained type in the heap object. To make this work when
/// destroying the box the release needs to be special i.e `swift_releaseBox`.
/// It does not call the the heap object metadata's destroy function. Rather, it
/// knows that the allocBox's metadata is the contained objects and calls an
/// appropriate implementation: `_swift_embedded_invoke_box_destroy`.
/// Therefore, one cannot not use `swift_release` but rather must use
/// `swift_releaseBox` to implement the "release" function of a box object.
@_silgen_name("swift_allocBox")
public func swift_allocBox(_ metadata: Builtin.RawPointer) -> (Builtin.RawPointer, Builtin.RawPointer) {
let alignMask = Int(unsafe _swift_embedded_metadata_get_align_mask(UnsafeMutableRawPointer(metadata)))
let size = Int(unsafe _swift_embedded_metadata_get_size(UnsafeMutableRawPointer(metadata)))
let headerSize = unsafe MemoryLayout<Int>.size + MemoryLayout<UnsafeRawPointer>.size
let headerAlignMask = unsafe MemoryLayout<UnsafeRawPointer>.alignment - 1
let startOfBoxedValue = ((headerSize + alignMask) & ~alignMask)
let requiredSize: Int = startOfBoxedValue + size
let requiredAlignmentMask: Int = alignMask | headerAlignMask
let p = unsafe swift_slowAlloc(requiredSize, requiredAlignmentMask)!
let object = unsafe p.assumingMemoryBound(to: HeapObject.self)
unsafe _swift_embedded_set_heap_object_metadata_pointer(object, UnsafeMutableRawPointer(metadata))
unsafe object.pointee.refcount = 1
let boxedValueAddr = unsafe UnsafeMutableRawPointer(p).advanced(by: startOfBoxedValue)
return (object._rawValue, boxedValueAddr._rawValue)
}
@c
public func swift_deallocBox(_ object: Builtin.RawPointer) {
unsafe free(UnsafeMutableRawPointer(object))
}
@_silgen_name("swift_makeBoxUnique")
public func swifft_makeBoxUnique(buffer: Builtin.RawPointer, metadata: Builtin.RawPointer, alignMask: Int) -> (Builtin.RawPointer, Builtin.RawPointer){
let addrOfHeapObjectPtr = unsafe UnsafeMutablePointer<Builtin.RawPointer>(buffer)
let box = unsafe addrOfHeapObjectPtr.pointee
let headerSize = unsafe MemoryLayout<Int>.size + MemoryLayout<UnsafeRawPointer>.size
let startOfBoxedValue = ((headerSize + alignMask) & ~alignMask)
let oldObjectAddr = unsafe UnsafeMutableRawPointer(box) + startOfBoxedValue
if !swift_isUniquelyReferenced_native(object: box) {
let refAndObjectAddr = swift_allocBox(metadata)
unsafe _swift_embedded_initialize_box(UnsafeMutableRawPointer(metadata), UnsafeMutableRawPointer(refAndObjectAddr.1), oldObjectAddr)
swift_releaseBox(box)
unsafe addrOfHeapObjectPtr.pointee = refAndObjectAddr.0
return refAndObjectAddr
} else {
return (box, oldObjectAddr._rawValue)
}
}
/// Refcounting
@@ -388,7 +442,7 @@ public func swift_release_n(object: Builtin.RawPointer, n: UInt32) {
unsafe swift_release_n_(object: o, n: n)
}
func swift_release_n_(object: UnsafeMutablePointer<HeapObject>?, n: UInt32) {
func swift_release_n_(object: UnsafeMutablePointer<HeapObject>?, n: UInt32, isBoxRelease: Bool = false) {
guard let object = unsafe object else {
return
}
@@ -413,12 +467,25 @@ func swift_release_n_(object: UnsafeMutablePointer<HeapObject>?, n: UInt32) {
let doNotFree = unsafe (loadedRefcount & HeapObject.doNotFreeBit) != 0
unsafe storeRelaxed(refcount, newValue: HeapObject.immortalRefCount | (doNotFree ? HeapObject.doNotFreeBit : 0))
unsafe _swift_embedded_invoke_heap_object_destroy(object)
if isBoxRelease {
unsafe _swift_embedded_invoke_box_destroy(object)
} else {
unsafe _swift_embedded_invoke_heap_object_destroy(object)
}
} else if resultingRefcount < 0 {
fatalError("negative refcount")
}
}
@c
public func swift_releaseBox(_ object: Builtin.RawPointer) {
if !isValidPointerForNativeRetain(object: object) {
fatalError("not a valid pointer for releaseBox")
}
let o = unsafe UnsafeMutablePointer<HeapObject>(object)
unsafe swift_release_n_(object: o, n: 1, isBoxRelease: true)
}
@c
public func swift_bridgeObjectRelease(object: Builtin.RawPointer) {
swift_bridgeObjectRelease_n(object: object, n: 1)

View File

@@ -1,23 +1,225 @@
// RUN: %target-swift-frontend -enable-experimental-feature EmbeddedExistentials -enable-experimental-feature Embedded -parse-as-library -wmo -emit-sil %s | %FileCheck %s
// RUN: %target-run-simple-swift(-enable-experimental-feature EmbeddedExistentials -enable-experimental-feature Embedded -parse-as-library -wmo) | %FileCheck %s --check-prefix=OUTPUT
// RUN: %target-run-simple-swift(-enable-experimental-feature EmbeddedExistentials -enable-experimental-feature Embedded -parse-as-library -wmo -O) | %FileCheck %s --check-prefix=OUTPUT
// REQUIRES: swift_in_compiler
// REQUIRES: executable_test
// REQUIRES: optimized_stdlib
// REQUIRES: swift_feature_Embedded
// REQUIRES: swift_feature_EmbeddedExistentials
class C {}
class CP {
}
class C : CP {
func foo() { }
}
class GC<T> {
var x: T? = nil
func foo() {}
deinit {
print("deinit called")
}
}
struct StructWithClass {
var c = GC<Int>()
}
struct GenericStructWithClass<T> {
var c = GC<T>()
var d = GC<T>()
}
enum EnumWithClass {
case a
case c(GC<Int>)
}
enum GenericEnumWithClass<T> {
case a
case c(GC<T>)
}
// CHECK: sil @$e11existential4testyyF
// CHECK: init_existential_addr
// CHECK: } // end sil function '$e11existential4testyyF'
// There are 8 class instances that are destroyed.
// OUTPUT: deinit called
// OUTPUT: deinit called
// OUTPUT: deinit called
// OUTPUT: deinit called
// OUTPUT: deinit called
// OUTPUT: deinit called
// OUTPUT: deinit called
// OUTPUT: deinit called
// OUTPUT: deinit called
// OUTPUT: deinit called
// OUTPUT: deinit called
// OUTPUT: deinit called
// OUTPUT-NOT: deinit called
func test() {
let any: any Any = C()
let _: any Any = GC<Int>()
let _: any Any = 3
let _: any Any = StructWithClass()
let _: any Any = GenericStructWithClass<Int>()
let _: any Any = EnumWithClass.c(GC<Int>())
let _: any Any = GenericEnumWithClass.c(GC<Int>())
let _: any Any = (3, 4)
let _: any Any = (StructWithClass(), StructWithClass())
// outline storage case
let _: any Any = (StructWithClass(), StructWithClass(), StructWithClass(), StructWithClass())
}
protocol Basic {
func a()
}
protocol Derived : Basic {
func b()
}
class Implementor : Derived {
func a() { print("a") }
func b() { print("b") }
}
extension Int : Derived {
func a() { print("a Int \(self)") }
func b() { print("b Int \(self)") }
}
struct MyStruct : Derived {
var x = 5
func a() { print("a MyStruct \(self.x)") }
func b() { print("b MyStruct") }
}
struct LargeMyStruct : Derived {
var x = (1, 2, 3, 4, 5)
var refCounted = StructWithClass()
func a() { print("a LargeMyStruct \(self.x.4)") }
func b() { print("b LargeMyStruct") }
}
enum MyEnum : Derived {
case a
case b(Int)
func a() {
print("a MyEnum ")
switch self {
case .a: break
case .b(let x):
print(x)
}
}
func b() {
print("b MyEnum ")
}
}
func test2(_ p: any Derived) {
p.a()
p.b()
}
protocol ValuePrinter {
func printValue()
mutating func mutate()
}
protocol WithAssoc {
associatedtype Assoc : ValuePrinter
func a() -> Assoc
}
extension Int : ValuePrinter {
func printValue() {
print("my value: \(self)")
}
mutating func mutate() {
self = 8
print("my value (mutating expect 8): \(self)")
}
}
extension LargeMyStruct : ValuePrinter {
func printValue() {
print("my value of LargeMyStruct: \(self.x.4)")
}
mutating func mutate() {
self.x = (6, 7, 8, 9, 10)
print("my value of LargeMyStruct (mutating expect 10): \(self.x.4)")
}
}
struct ConformWithAssoc : WithAssoc {
var x = 1
func a() -> Int {
return x
}
}
struct ConformWithLargeAssoc : WithAssoc {
var x = LargeMyStruct()
func a() -> LargeMyStruct {
return x
}
}
func test3(_ p: any WithAssoc) {
let x = p.a()
x.printValue()
}
func test4(_ p: any WithAssoc) {
var x = p.a()
let c = x
x.mutate()
c.printValue()
}
@main
struct Main {
static func main() {
test()
test2(Implementor())
// OUTPUT: a
// OUTPUT: b
test2(5)
// OUTPUT: a Int 5
// OUTPUT: b Int 5
test2(MyStruct())
// OUTPUT: a MyStruct 5
// OUTPUT: b MyStruct
test2(MyEnum.b(5))
// OUTPUT: a MyEnum
// OUTPUT: 5
// OUTPUT: b MyEnum
test3(ConformWithAssoc())
// OUTPUT: my value: 1
test3(ConformWithLargeAssoc())
// OUTPUT: my value of LargeMyStruct: 5
// OUTPUT: deinit called
test4(ConformWithAssoc())
// OUTPUT: my value (mutating expect 8): 8
// OUTPUT: my value: 1
test4(ConformWithLargeAssoc())
// OUTPUT: my value of LargeMyStruct (mutating expect 10): 10
// OUTPUT: my value of LargeMyStruct: 5
// OUTPUT: deinit called
// OUTPUT-NOT: deinit called
}
}