Skip to content

Commit b5e79d5

Browse files
committed
IRGen: Capture NecessaryBindings in fixed boxes that may invoke deinits.
Noncopyable types may have user-defined code in their `deinit`s that requires passing the type's generic parameters, so a box for a captured noncopyable type needs to capture the generic environment even when the captured type is fixed- layout. Fixes rdar://138958210.
1 parent ffb22d7 commit b5e79d5

File tree

7 files changed

+381
-33
lines changed

7 files changed

+381
-33
lines changed

lib/IRGen/GenHeap.cpp

Lines changed: 108 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -568,8 +568,10 @@ llvm::Value *IRGenFunction::emitUnmanagedAlloc(const HeapLayout &layout,
568568
const llvm::Twine &name,
569569
llvm::Constant *captureDescriptor,
570570
const HeapNonFixedOffsets *offsets) {
571-
if (layout.isKnownEmpty())
571+
if (layout.isKnownEmpty()
572+
&& layout.isTriviallyDestroyable()) {
572573
return IGM.RefCountedNull;
574+
}
573575

574576
llvm::Value *metadata = layout.getPrivateMetadata(IGM, captureDescriptor);
575577
llvm::Value *size, *alignMask;
@@ -1470,7 +1472,7 @@ class BoxTypeInfo : public HeapTypeInfo<BoxTypeInfo> {
14701472

14711473
/// Allocate a box of the given type.
14721474
virtual OwnedAddress
1473-
allocate(IRGenFunction &IGF, SILType boxedType, GenericEnvironment *env,
1475+
allocate(IRGenFunction &IGF, GenericEnvironment *env, SILBoxType *box,
14741476
const llvm::Twine &name) const = 0;
14751477

14761478
/// Deallocate an uninitialized box.
@@ -1488,8 +1490,11 @@ class EmptyBoxTypeInfo final : public BoxTypeInfo {
14881490
EmptyBoxTypeInfo(IRGenModule &IGM) : BoxTypeInfo(IGM) {}
14891491

14901492
OwnedAddress
1491-
allocate(IRGenFunction &IGF, SILType boxedType, GenericEnvironment *env,
1493+
allocate(IRGenFunction &IGF, GenericEnvironment *env, SILBoxType *box,
14921494
const llvm::Twine &name) const override {
1495+
auto boxedType = getSILBoxFieldType(
1496+
IGF.IGM.getMaximalTypeExpansionContext(),
1497+
box, IGF.IGM.getSILModule().Types, 0);
14931498
return OwnedAddress(IGF.getTypeInfo(boxedType).getUndefAddress(),
14941499
IGF.emitAllocEmptyBoxCall());
14951500
}
@@ -1513,8 +1518,11 @@ class NonFixedBoxTypeInfo final : public BoxTypeInfo {
15131518
NonFixedBoxTypeInfo(IRGenModule &IGM) : BoxTypeInfo(IGM) {}
15141519

15151520
OwnedAddress
1516-
allocate(IRGenFunction &IGF, SILType boxedType, GenericEnvironment *env,
1521+
allocate(IRGenFunction &IGF, GenericEnvironment *env, SILBoxType *boxType,
15171522
const llvm::Twine &name) const override {
1523+
auto boxedType = getSILBoxFieldType(
1524+
IGF.IGM.getMaximalTypeExpansionContext(),
1525+
boxType, IGF.IGM.getSILModule().Types, 0);
15181526
auto &ti = IGF.getTypeInfo(boxedType);
15191527
// Use the runtime to allocate a box of the appropriate size.
15201528
auto metadata = IGF.emitTypeMetadataRefForLayout(boxedType);
@@ -1552,14 +1560,18 @@ class FixedBoxTypeInfoBase : public BoxTypeInfo {
15521560
FixedBoxTypeInfoBase(IRGenModule &IGM, HeapLayout &&layout)
15531561
: BoxTypeInfo(IGM), layout(std::move(layout))
15541562
{
1555-
// Empty layouts should always use EmptyBoxTypeInfo instead
1556-
assert(!layout.isKnownEmpty());
1563+
// Trivial empty layouts should always use EmptyBoxTypeInfo instead
1564+
assert(!layout.isKnownEmpty()
1565+
|| !layout.isTriviallyDestroyable());
15571566
}
15581567

15591568
OwnedAddress
1560-
allocate(IRGenFunction &IGF, SILType boxedType, GenericEnvironment *env,
1569+
allocate(IRGenFunction &IGF, GenericEnvironment *env, SILBoxType *box,
15611570
const llvm::Twine &name)
15621571
const override {
1572+
auto boxedType = getSILBoxFieldType(
1573+
IGF.IGM.getMaximalTypeExpansionContext(),
1574+
box, IGF.IGM.getSILModule().Types, 0);
15631575
// Allocate a new object using the layout.
15641576
auto boxedInterfaceType = boxedType;
15651577
if (env) {
@@ -1581,13 +1593,21 @@ class FixedBoxTypeInfoBase : public BoxTypeInfo {
15811593
boxedInterfaceType = SILType::getPrimitiveType(
15821594
astType, boxedInterfaceType.getCategory());
15831595
}
1584-
1596+
15851597
auto boxDescriptor = IGF.IGM.getAddrOfBoxDescriptor(
15861598
boxedInterfaceType,
15871599
env ? env->getGenericSignature().getCanonicalSignature()
15881600
: CanGenericSignature());
15891601
llvm::Value *allocation = IGF.emitUnmanagedAlloc(layout, name,
15901602
boxDescriptor);
1603+
// Store metadata for the necessary bindings if present.
1604+
if (layout.hasBindings()) {
1605+
auto allocationAddr = layout.emitCastTo(IGF, allocation);
1606+
auto bindingsAddr = layout.getElement(layout.getBindingsIndex())
1607+
.project(IGF, allocationAddr, nullptr);
1608+
layout.getBindings().save(IGF, bindingsAddr, box->getSubstitutions());
1609+
}
1610+
15911611
Address rawAddr = project(IGF, allocation, boxedType);
15921612
return {rawAddr, allocation};
15931613
}
@@ -1637,22 +1657,87 @@ class SingleRefcountedBoxTypeInfo final : public FixedBoxTypeInfoBase {
16371657

16381658
/// Implementation of a box for a specific type.
16391659
class FixedBoxTypeInfo final : public FixedBoxTypeInfoBase {
1660+
static SILType getFieldType(IRGenModule &IGM, SILBoxType *T) {
1661+
return getSILBoxFieldType(IGM.getMaximalTypeExpansionContext(),
1662+
T, IGM.getSILModule().Types, 0);
1663+
}
1664+
1665+
static HeapLayout getHeapLayout(IRGenModule &IGM, SILBoxType *T) {
1666+
SmallVector<SILType> fieldTypes;
1667+
fieldTypes.push_back(getFieldType(IGM, T));
1668+
1669+
auto bindings = NecessaryBindings::forFixedBox(IGM, T);
1670+
unsigned bindingsIndex = 0;
1671+
SmallVector<const TypeInfo *, 4> fields;
1672+
fields.push_back(&IGM.getTypeInfo(fieldTypes[0]));
1673+
1674+
if (!bindings.empty()) {
1675+
bindingsIndex = 1;
1676+
auto bindingsSize = bindings.getBufferSize(IGM);
1677+
auto &bindingsTI = IGM.getOpaqueStorageTypeInfo(bindingsSize,
1678+
IGM.getPointerAlignment());
1679+
fieldTypes.push_back(SILType());
1680+
fields.push_back(&bindingsTI);
1681+
}
1682+
1683+
return HeapLayout(IGM, LayoutStrategy::Optimal,
1684+
fieldTypes, fields,
1685+
/* type to fill */ nullptr,
1686+
std::move(bindings), bindingsIndex);
1687+
}
1688+
16401689
public:
1641-
FixedBoxTypeInfo(IRGenModule &IGM, SILType T)
1642-
: FixedBoxTypeInfoBase(IGM,
1643-
HeapLayout(IGM, LayoutStrategy::Optimal, T, &IGM.getTypeInfo(T)))
1690+
FixedBoxTypeInfo(IRGenModule &IGM, SILBoxType *T)
1691+
: FixedBoxTypeInfoBase(IGM, getHeapLayout(IGM, T))
16441692
{}
16451693
};
16461694

16471695
} // end anonymous namespace
16481696

1697+
NecessaryBindings
1698+
NecessaryBindings::forFixedBox(IRGenModule &IGM, SILBoxType *box) {
1699+
// Don't need to bind metadata if the type is concrete.
1700+
if (!box->hasArchetype() && !box->hasTypeParameter()) {
1701+
return {};
1702+
}
1703+
1704+
auto fieldTy = getSILBoxFieldType(IGM.getMaximalTypeExpansionContext(),
1705+
box, IGM.getSILModule().Types, 0);
1706+
auto fieldTI = cast<FixedTypeInfo>(&IGM.getTypeInfo(fieldTy));
1707+
1708+
// If the type is trivially destroyable, or it's fixed-layout and copyable,
1709+
// then we can always destroy it without binding type metadata.
1710+
if (fieldTI->isTriviallyDestroyable(ResilienceExpansion::Maximal)
1711+
|| fieldTI->isCopyable(ResilienceExpansion::Maximal)) {
1712+
return {};
1713+
}
1714+
1715+
NecessaryBindings bindings(box->getSubstitutions(),
1716+
/*no escape*/ false);
1717+
1718+
// Collect bindings needed by a deinit-shaped function.
1719+
auto deinitParam = SILParameterInfo(
1720+
box->getLayout()->getFields()[0].getLoweredType(),
1721+
ParameterConvention::Indirect_In);
1722+
auto deinitFnTy = SILFunctionType::get(box->getLayout()->getGenericSignature(),
1723+
SILExtInfo(),
1724+
SILCoroutineKind::None,
1725+
ParameterConvention::Direct_Guaranteed,
1726+
deinitParam,
1727+
{}, {}, std::nullopt,
1728+
{}, {}, IGM.Context);
1729+
bindings.computeBindings(IGM, deinitFnTy, /*consider param sources*/ false);
1730+
return bindings;
1731+
}
1732+
16491733
const TypeInfo *TypeConverter::convertBoxType(SILBoxType *T) {
16501734
// We can share a type info for all dynamic-sized heap metadata.
16511735
// TODO: Multi-field boxes
16521736
assert(T->getLayout()->getFields().size() == 1
16531737
&& "multi-field boxes not implemented yet");
1654-
auto &eltTI = IGM.getTypeInfoForLowered(getSILBoxFieldLoweredType(
1655-
IGM.getMaximalTypeExpansionContext(), T, IGM.getSILModule().Types, 0));
1738+
auto eltTy = getSILBoxFieldLoweredType(
1739+
IGM.getMaximalTypeExpansionContext(), T, IGM.getSILModule().Types, 0);
1740+
auto &eltTI = IGM.getTypeInfoForLowered(eltTy);
16561741
if (!eltTI.isFixedSize()) {
16571742
if (!NonFixedBoxTI)
16581743
NonFixedBoxTI = new NonFixedBoxTypeInfo(IGM);
@@ -1662,12 +1747,16 @@ const TypeInfo *TypeConverter::convertBoxType(SILBoxType *T) {
16621747
// For fixed-sized types, we can emit concrete box metadata.
16631748
auto &fixedTI = cast<FixedTypeInfo>(eltTI);
16641749

1665-
// Because we assume in enum's that payloads with a Builtin.NativeReference
1750+
// Because we assume in enums that payloads with a Builtin.NativeReference
16661751
// which is also the type for indirect enum cases have extra inhabitants of
16671752
// pointers we can't have a nil pointer as a representation for an empty box
16681753
// type -- nil conflicts with the extra inhabitants. We return a static
16691754
// singleton empty box object instead.
1670-
if (fixedTI.isKnownEmpty(ResilienceExpansion::Maximal)) {
1755+
//
1756+
// (If the box needs no storage, but the type still carries a deinit,
1757+
// then we still need to trigger that deinit when the box is freed.)
1758+
if (fixedTI.isKnownEmpty(ResilienceExpansion::Maximal)
1759+
&& fixedTI.isTriviallyDestroyable(ResilienceExpansion::Maximal)) {
16711760
if (!EmptyBoxTI)
16721761
EmptyBoxTI = new EmptyBoxTypeInfo(IGM);
16731762
return EmptyBoxTI;
@@ -1701,9 +1790,8 @@ const TypeInfo *TypeConverter::convertBoxType(SILBoxType *T) {
17011790
// Produce a tailored box metadata for the type.
17021791
assert(T->getLayout()->getFields().size() == 1
17031792
&& "multi-field boxes not implemented yet");
1704-
return new FixedBoxTypeInfo(
1705-
IGM, getSILBoxFieldType(IGM.getMaximalTypeExpansionContext(),
1706-
T, IGM.getSILModule().Types, 0));
1793+
1794+
return new FixedBoxTypeInfo(IGM, T);
17071795
}
17081796

17091797
OwnedAddress
@@ -1713,12 +1801,7 @@ irgen::emitAllocateBox(IRGenFunction &IGF, CanSILBoxType boxType,
17131801
auto &boxTI = IGF.getTypeInfoForLowered(boxType).as<BoxTypeInfo>();
17141802
assert(boxType->getLayout()->getFields().size() == 1
17151803
&& "multi-field boxes not implemented yet");
1716-
return boxTI.allocate(
1717-
IGF,
1718-
getSILBoxFieldType(
1719-
IGF.IGM.getMaximalTypeExpansionContext(),
1720-
boxType, IGF.IGM.getSILModule().Types, 0),
1721-
env, name);
1804+
return boxTI.allocate(IGF, env, boxType, name);
17221805
}
17231806

17241807
void irgen::emitDeallocateBox(IRGenFunction &IGF,
@@ -1751,7 +1834,7 @@ Address irgen::emitAllocateExistentialBoxInBuffer(
17511834
// Get a box for the boxed value.
17521835
auto boxType = SILBoxType::get(boxedType.getASTType());
17531836
auto &boxTI = IGF.getTypeInfoForLowered(boxType).as<BoxTypeInfo>();
1754-
OwnedAddress owned = boxTI.allocate(IGF, boxedType, env, name);
1837+
OwnedAddress owned = boxTI.allocate(IGF, env, boxType, name);
17551838
Explosion box;
17561839
box.add(owned.getOwner());
17571840
boxTI.initialize(IGF, box,

lib/IRGen/GenProto.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3648,9 +3648,13 @@ void NecessaryBindings::restore(IRGenFunction &IGF, Address buffer,
36483648
metadataState, SubMap);
36493649
}
36503650

3651-
void NecessaryBindings::save(IRGenFunction &IGF, Address buffer) const {
3651+
void NecessaryBindings::save(IRGenFunction &IGF, Address buffer,
3652+
std::optional<SubstitutionMap> replacementSubstitutions) const {
3653+
SubstitutionMap subsToPass = replacementSubstitutions.has_value()
3654+
? replacementSubstitutions.value()
3655+
: SubMap;
36523656
emitInitOfGenericRequirementsBuffer(IGF, getRequirements(), buffer,
3653-
MetadataState::Complete, SubMap,
3657+
MetadataState::Complete, subsToPass,
36543658
/*onHeapPacks=*/!NoEscape);
36553659
}
36563660

lib/IRGen/NecessaryBindings.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ class NecessaryBindings {
6363
bool noEscape,
6464
bool considerParameterSources);
6565

66+
/// Collect the necessary bindings to be able to destroy a value inside of a
67+
/// fixed-layout boxed allocation.
68+
static NecessaryBindings forFixedBox(IRGenModule &IGM,
69+
SILBoxType *box);
70+
6671
void addRequirement(GenericRequirement requirement) {
6772
auto type = requirement.getTypeParameter().subst(SubMap);
6873
if (!type->hasArchetype())
@@ -86,7 +91,12 @@ class NecessaryBindings {
8691
Size getBufferSize(IRGenModule &IGM) const;
8792

8893
/// Save the necessary bindings to the given buffer.
89-
void save(IRGenFunction &IGF, Address buffer) const;
94+
///
95+
/// If `replacementSubs` has a value, then the bindings saved are taken from
96+
/// the given substitution map instead of the substitutions
97+
void save(IRGenFunction &IGF, Address buffer,
98+
std::optional<SubstitutionMap> replacementSubs = std::nullopt)
99+
const;
90100

91101
/// Restore the necessary bindings from the given buffer.
92102
void restore(IRGenFunction &IGF, Address buffer, MetadataState state) const;

lib/IRGen/StructLayout.cpp

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -190,23 +190,32 @@ StructLayout::StructLayout(IRGenModule &IGM, std::optional<CanType> type,
190190
Ty = (typeToFill ? typeToFill : IGM.OpaqueTy);
191191
}
192192
} else {
193+
// A heap object containing an empty but non-trivially-destroyable
194+
// noncopyable type needs to exist in order to run deinits when the
193195
bool nonEmpty = builder.addFields(Elements, strategy);
194196

197+
IsKnownTriviallyDestroyable
198+
= triviallyDestroyable & builder.isTriviallyDestroyable();
199+
IsKnownCopyable = copyable & builder.isCopyable();
200+
195201
// Special-case: there's nothing to store.
196202
// In this case, produce an opaque type; this tends to cause lovely
197203
// assertions.
198-
if (!nonEmpty) {
204+
//
205+
// If a heap object contains an empty but non-trivially-destroyable type,
206+
// then we still want to create a non-empty heap object, since the heap
207+
// object's destructor will run deinits for the value.
208+
if (!nonEmpty
209+
&& (IsKnownTriviallyDestroyable || !requiresHeapHeader(layoutKind))) {
199210
assert(!builder.empty() == requiresHeapHeader(layoutKind));
200211
MinimumAlign = Alignment(1);
201212
MinimumSize = Size(0);
202213
headerSize = builder.getHeaderSize();
203214
SpareBits.clear();
204215
IsFixedLayout = true;
205216
IsLoadable = true;
206-
IsKnownTriviallyDestroyable = triviallyDestroyable & builder.isTriviallyDestroyable();
207217
IsKnownBitwiseTakable = builder.isBitwiseTakable();
208218
IsKnownAlwaysFixedSize = builder.isAlwaysFixedSize();
209-
IsKnownCopyable = copyable & builder.isCopyable();
210219
Ty = (typeToFill ? typeToFill : IGM.OpaqueTy);
211220
} else {
212221
MinimumAlign = builder.getAlignment();
@@ -215,10 +224,8 @@ StructLayout::StructLayout(IRGenModule &IGM, std::optional<CanType> type,
215224
SpareBits = builder.getSpareBits();
216225
IsFixedLayout = builder.isFixedLayout();
217226
IsLoadable = builder.isLoadable();
218-
IsKnownTriviallyDestroyable = triviallyDestroyable & builder.isTriviallyDestroyable();
219227
IsKnownBitwiseTakable = bitwiseTakable & builder.isBitwiseTakable();
220228
IsKnownAlwaysFixedSize = builder.isAlwaysFixedSize();
221-
IsKnownCopyable = copyable & builder.isCopyable();
222229
if (typeToFill) {
223230
builder.setAsBodyOfStruct(typeToFill);
224231
Ty = typeToFill;

0 commit comments

Comments
 (0)