Skip to content

Commit 15e4094

Browse files
authored
Merge pull request #18937 from jckarter/mutating-opened-existential-covariant-return
SILGen: Fix order of operations when a mutating existential method returns Self.
2 parents 01ecfda + 7f14a3b commit 15e4094

26 files changed

+443
-113
lines changed

include/swift/SIL/SILType.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,9 @@ class SILType {
484484
/// Returns the underlying referent SILType of an @sil_unowned or @sil_weak
485485
/// Type.
486486
SILType getReferentType(SILModule &M) const;
487+
488+
/// Returns a SILType with any archetypes mapped out of context.
489+
SILType mapTypeOutOfContext() const;
487490

488491
/// Given two SIL types which are representations of the same type,
489492
/// check whether they have an abstraction difference.

lib/IRGen/GenHeap.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1449,10 +1449,7 @@ class FixedBoxTypeInfoBase : public BoxTypeInfo {
14491449
// Allocate a new object using the layout.
14501450
auto boxedInterfaceType = boxedType;
14511451
if (env) {
1452-
boxedInterfaceType = SILType::getPrimitiveType(
1453-
boxedType.getASTType()->mapTypeOutOfContext()
1454-
->getCanonicalType(),
1455-
boxedType.getCategory());
1452+
boxedInterfaceType = boxedType.mapTypeOutOfContext();
14561453
}
14571454

14581455
auto boxDescriptor = IGF.IGM.getAddrOfBoxDescriptor(

lib/SIL/SILType.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,12 @@ SILType SILType::getReferentType(SILModule &M) const {
385385
return M.Types.getLoweredType(Ty->getReferentType()->getCanonicalType());
386386
}
387387

388+
SILType SILType::mapTypeOutOfContext() const {
389+
return SILType::getPrimitiveType(getASTType()->mapTypeOutOfContext()
390+
->getCanonicalType(),
391+
getCategory());
392+
}
393+
388394
CanType
389395
SILBoxType::getFieldLoweredType(SILModule &M, unsigned index) const {
390396
auto fieldTy = getLayout()->getFields()[index].getLoweredType();

lib/SILGen/Cleanup.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,10 @@ class LLVM_LIBRARY_VISIBILITY CleanupManager {
158158
assert(!stack.empty());
159159
return stack.stable_begin();
160160
}
161+
162+
Cleanup &getCleanup(CleanupHandle iter) {
163+
return *stack.find(iter);
164+
}
161165

162166
/// \brief Emit a branch to the given jump destination,
163167
/// threading out through any cleanups we need to run. This does not pop the

lib/SILGen/ResultPlan.cpp

Lines changed: 163 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "LValue.h"
1818
#include "RValue.h"
1919
#include "SILGenFunction.h"
20+
#include "swift/AST/GenericEnvironment.h"
2021

2122
using namespace swift;
2223
using namespace Lowering;
@@ -29,7 +30,7 @@ namespace {
2930

3031
/// A result plan for evaluating an indirect result into the address
3132
/// associated with an initialization.
32-
class InPlaceInitializationResultPlan : public ResultPlan {
33+
class InPlaceInitializationResultPlan final : public ResultPlan {
3334
Initialization *init;
3435

3536
public:
@@ -47,10 +48,155 @@ class InPlaceInitializationResultPlan : public ResultPlan {
4748
}
4849
};
4950

51+
/// A cleanup that handles the delayed emission of an indirect buffer for opened
52+
/// Self arguments.
53+
class IndirectOpenedSelfCleanup final : public Cleanup {
54+
SILValue box;
55+
public:
56+
IndirectOpenedSelfCleanup()
57+
: box()
58+
{}
59+
60+
void setBox(SILValue b) {
61+
assert(!box && "buffer already set?!");
62+
box = b;
63+
}
64+
65+
void emit(SILGenFunction &SGF, CleanupLocation loc, ForUnwind_t forUnwind)
66+
override {
67+
assert(box && "buffer never emitted before activating cleanup?!");
68+
SGF.B.createDeallocBox(loc, box);
69+
}
70+
71+
void dump(SILGenFunction &SGF) const override {
72+
llvm::errs() << "IndirectOpenedSelfCleanup\n";
73+
if (box)
74+
box->dump();
75+
}
76+
};
77+
78+
/// Map a type expressed in terms of opened archetypes into a context-free
79+
/// dependent type, returning the type, a generic signature with parameters
80+
/// corresponding to each opened type,
81+
static std::tuple<CanType, CanGenericSignature, SubstitutionMap>
82+
mapTypeOutOfOpenedExistentialContext(CanType t) {
83+
SmallVector<ArchetypeType *, 4> openedTypes;
84+
t->getOpenedExistentials(openedTypes);
85+
86+
ArrayRef<Type> openedTypesAsTypes(
87+
reinterpret_cast<const Type *>(openedTypes.data()),
88+
openedTypes.size());
89+
90+
SmallVector<GenericTypeParamType *, 4> params;
91+
for (unsigned i : indices(openedTypes)) {
92+
params.push_back(GenericTypeParamType::get(0, i, t->getASTContext()));
93+
}
94+
95+
auto mappedSig = GenericSignature::get(params, {});
96+
auto mappedSubs = SubstitutionMap::get(mappedSig, openedTypesAsTypes, {});
97+
98+
auto mappedTy = t.subst(
99+
[&](SubstitutableType *t) -> Type {
100+
auto index = std::find(openedTypes.begin(), openedTypes.end(), t)
101+
- openedTypes.begin();
102+
assert(index != openedTypes.end() - openedTypes.begin());
103+
return params[index];
104+
},
105+
MakeAbstractConformanceForGenericType());
106+
107+
return std::make_tuple(mappedTy->getCanonicalType(mappedSig),
108+
mappedSig->getCanonicalSignature(),
109+
mappedSubs);
110+
}
111+
112+
/// A result plan for an indirectly-returned opened existential value.
113+
///
114+
/// This defers allocating the temporary for the result to a later point so that
115+
/// it happens after the arguments are evaluated.
116+
class IndirectOpenedSelfResultPlan final : public ResultPlan {
117+
AbstractionPattern origType;
118+
CanType substType;
119+
CleanupHandle handle = CleanupHandle::invalid();
120+
mutable SILValue resultBox, resultBuf;
121+
122+
public:
123+
IndirectOpenedSelfResultPlan(SILGenFunction &SGF,
124+
AbstractionPattern origType,
125+
CanType substType)
126+
: origType(origType), substType(substType)
127+
{
128+
// Create a cleanup to deallocate the stack buffer at the proper scope.
129+
// We won't emit the buffer till later, after arguments have been opened,
130+
// though.
131+
SGF.Cleanups.pushCleanupInState<IndirectOpenedSelfCleanup>(
132+
CleanupState::Dormant);
133+
handle = SGF.Cleanups.getCleanupsDepth();
134+
}
135+
136+
void
137+
gatherIndirectResultAddrs(SILGenFunction &SGF, SILLocation loc,
138+
SmallVectorImpl<SILValue> &outList) const override {
139+
assert(!resultBox && "already created temporary?!");
140+
141+
// We allocate the buffer as a box because the scope nesting won't clean
142+
// this up with good stack discipline relative to any stack allocations that
143+
// occur during argument emission. Escape analysis during mandatory passes
144+
// ought to clean this up.
145+
146+
auto resultTy = SGF.getLoweredType(origType, substType).getASTType();
147+
CanType layoutTy;
148+
CanGenericSignature layoutSig;
149+
SubstitutionMap layoutSubs;
150+
std::tie(layoutTy, layoutSig, layoutSubs)
151+
= mapTypeOutOfOpenedExistentialContext(resultTy);
152+
153+
auto boxLayout = SILLayout::get(SGF.getASTContext(),
154+
layoutSig->getCanonicalSignature(),
155+
SILField(layoutTy->getCanonicalType(layoutSig), true));
156+
157+
resultBox = SGF.B.createAllocBox(loc,
158+
SILBoxType::get(SGF.getASTContext(),
159+
boxLayout,
160+
layoutSubs));
161+
162+
// Complete the cleanup to deallocate this buffer later, after we're
163+
// finished with the argument.
164+
static_cast<IndirectOpenedSelfCleanup&>(SGF.Cleanups.getCleanup(handle))
165+
.setBox(resultBox);
166+
SGF.Cleanups.setCleanupState(handle, CleanupState::Active);
167+
168+
resultBuf = SGF.B.createProjectBox(loc, resultBox, 0);
169+
outList.emplace_back(resultBuf);
170+
}
171+
172+
RValue finish(SILGenFunction &SGF, SILLocation loc, CanType substType,
173+
ArrayRef<ManagedValue> &directResults) override {
174+
assert(resultBox && "never emitted temporary?!");
175+
176+
// Lower the unabstracted result type.
177+
auto &substTL = SGF.getTypeLowering(substType);
178+
179+
ManagedValue value;
180+
// If the value isn't address-only, go ahead and load.
181+
if (!substTL.isAddressOnly()) {
182+
auto load = substTL.emitLoad(SGF.B, loc, resultBuf,
183+
LoadOwnershipQualifier::Take);
184+
value = SGF.emitManagedRValueWithCleanup(load);
185+
} else {
186+
value = SGF.emitManagedRValueWithCleanup(resultBuf);
187+
}
188+
189+
// A Self return should never be further abstracted. It's also never emitted
190+
// into context; we disable that optimization because Self may not even
191+
// be available to pre-allocate a stack buffer before we prepare a call.
192+
return RValue(SGF, loc, substType, value);
193+
}
194+
};
195+
50196
/// A result plan for working with a single value and potentially
51197
/// reabstracting it. The value can actually be a tuple if the
52198
/// abstraction is opaque.
53-
class ScalarResultPlan : public ResultPlan {
199+
class ScalarResultPlan final : public ResultPlan {
54200
std::unique_ptr<TemporaryInitialization> temporary;
55201
AbstractionPattern origType;
56202
Initialization *init;
@@ -150,7 +296,7 @@ class ScalarResultPlan : public ResultPlan {
150296

151297
/// A result plan which calls copyOrInitValueInto on an Initialization
152298
/// using a temporary buffer initialized by a sub-plan.
153-
class InitValueFromTemporaryResultPlan : public ResultPlan {
299+
class InitValueFromTemporaryResultPlan final : public ResultPlan {
154300
Initialization *init;
155301
ResultPlanPtr subPlan;
156302
std::unique_ptr<TemporaryInitialization> temporary;
@@ -184,7 +330,7 @@ class InitValueFromTemporaryResultPlan : public ResultPlan {
184330

185331
/// A result plan which calls copyOrInitValueInto using the result of
186332
/// a sub-plan.
187-
class InitValueFromRValueResultPlan : public ResultPlan {
333+
class InitValueFromRValueResultPlan final : public ResultPlan {
188334
Initialization *init;
189335
ResultPlanPtr subPlan;
190336

@@ -212,7 +358,7 @@ class InitValueFromRValueResultPlan : public ResultPlan {
212358

213359
/// A result plan which produces a larger RValue from a bunch of
214360
/// components.
215-
class TupleRValueResultPlan : public ResultPlan {
361+
class TupleRValueResultPlan final : public ResultPlan {
216362
SmallVector<ResultPlanPtr, 4> eltPlans;
217363

218364
public:
@@ -254,7 +400,7 @@ class TupleRValueResultPlan : public ResultPlan {
254400

255401
/// A result plan which evaluates into the sub-components
256402
/// of a splittable tuple initialization.
257-
class TupleInitializationResultPlan : public ResultPlan {
403+
class TupleInitializationResultPlan final : public ResultPlan {
258404
Initialization *tupleInit;
259405
SmallVector<InitializationPtr, 4> eltInitsBuffer;
260406
MutableArrayRef<InitializationPtr> eltInits;
@@ -305,7 +451,7 @@ class TupleInitializationResultPlan : public ResultPlan {
305451
}
306452
};
307453

308-
class ForeignErrorInitializationPlan : public ResultPlan {
454+
class ForeignErrorInitializationPlan final : public ResultPlan {
309455
SILLocation loc;
310456
LValue lvalue;
311457
ResultPlanPtr subPlan;
@@ -466,6 +612,16 @@ ResultPlanPtr ResultPlanBuilder::build(Initialization *init,
466612
// - store it to the destination
467613
// We could break this down into different ResultPlan implementations,
468614
// but it's easier not to.
615+
616+
// If the result type involves an indirectly-returned opened existential,
617+
// then we need to evaluate the arguments first in order to have access to
618+
// the opened Self type. A special result plan defers allocating the stack
619+
// slot to the point the call is emitted.
620+
if (result.getType()->hasOpenedExistential()
621+
&& SGF.silConv.isSILIndirect(result)) {
622+
return ResultPlanPtr(
623+
new IndirectOpenedSelfResultPlan(SGF, origType, substType));
624+
}
469625

470626
// Create a temporary if the result is indirect.
471627
std::unique_ptr<TemporaryInitialization> temporary;

lib/SILGen/SILGenApply.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4578,7 +4578,7 @@ RValue SILGenFunction::emitApply(ResultPlanPtr &&resultPlan,
45784578

45794579
auto directResultsArray = makeArrayRef(directResults);
45804580
RValue result =
4581-
resultPlan->finish(*this, loc, substResultType, directResultsArray);
4581+
resultPlan->finish(*this, loc, substResultType, directResultsArray);
45824582
assert(directResultsArray.empty() && "didn't claim all direct results");
45834583

45844584
return result;

0 commit comments

Comments
 (0)