Skip to content

Commit c922f68

Browse files
authored
Merge pull request #19038 from jckarter/mutating-opened-existential-covariant-return-4.2
[4.2] Fix order of operations when a mutating method invoked on an existential returns Self.
2 parents 6598f91 + 70c7b5c commit c922f68

25 files changed

+448
-111
lines changed

include/swift/SIL/SILType.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,9 @@ class SILType {
492492
/// Returns the underlying referent SILType of an @sil_unowned or @sil_weak
493493
/// Type.
494494
SILType getReferentType(SILModule &M) const;
495+
496+
/// Returns a SILType with any archetypes mapped out of context.
497+
SILType mapTypeOutOfContext() const;
495498

496499
/// Given two SIL types which are representations of the same type,
497500
/// 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
@@ -1558,10 +1558,7 @@ class FixedBoxTypeInfoBase : public BoxTypeInfo {
15581558
// Allocate a new object using the layout.
15591559
auto boxedInterfaceType = boxedType;
15601560
if (env) {
1561-
boxedInterfaceType = SILType::getPrimitiveType(
1562-
boxedType.getSwiftRValueType()->mapTypeOutOfContext()
1563-
->getCanonicalType(),
1564-
boxedType.getCategory());
1561+
boxedInterfaceType = boxedType.mapTypeOutOfContext();
15651562
}
15661563

15671564
auto boxDescriptor = IGF.IGM.getAddrOfBoxDescriptor(

lib/SIL/SILType.cpp

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

393+
SILType SILType::mapTypeOutOfContext() const {
394+
return SILType::getPrimitiveType(getSwiftRValueType()->mapTypeOutOfContext()
395+
->getCanonicalType(),
396+
getCategory());
397+
}
398+
393399
CanType
394400
SILBoxType::getFieldLoweredType(SILModule &M, unsigned index) const {
395401
auto fieldTy = getLayout()->getFields()[index].getLoweredType();

lib/SILGen/Cleanup.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,10 @@ class LLVM_LIBRARY_VISIBILITY CleanupManager {
142142
assert(!stack.empty());
143143
return stack.stable_begin();
144144
}
145+
146+
Cleanup &getCleanup(CleanupHandle iter) {
147+
return *stack.find(iter);
148+
}
145149

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

lib/SILGen/ResultPlan.cpp

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

151292
/// A result plan which calls copyOrInitValueInto on an Initialization
152293
/// using a temporary buffer initialized by a sub-plan.
153-
class InitValueFromTemporaryResultPlan : public ResultPlan {
294+
class InitValueFromTemporaryResultPlan final : public ResultPlan {
154295
Initialization *init;
155296
ResultPlanPtr subPlan;
156297
std::unique_ptr<TemporaryInitialization> temporary;
@@ -184,7 +325,7 @@ class InitValueFromTemporaryResultPlan : public ResultPlan {
184325

185326
/// A result plan which calls copyOrInitValueInto using the result of
186327
/// a sub-plan.
187-
class InitValueFromRValueResultPlan : public ResultPlan {
328+
class InitValueFromRValueResultPlan final : public ResultPlan {
188329
Initialization *init;
189330
ResultPlanPtr subPlan;
190331

@@ -212,7 +353,7 @@ class InitValueFromRValueResultPlan : public ResultPlan {
212353

213354
/// A result plan which produces a larger RValue from a bunch of
214355
/// components.
215-
class TupleRValueResultPlan : public ResultPlan {
356+
class TupleRValueResultPlan final : public ResultPlan {
216357
SmallVector<ResultPlanPtr, 4> eltPlans;
217358

218359
public:
@@ -254,7 +395,7 @@ class TupleRValueResultPlan : public ResultPlan {
254395

255396
/// A result plan which evaluates into the sub-components
256397
/// of a splittable tuple initialization.
257-
class TupleInitializationResultPlan : public ResultPlan {
398+
class TupleInitializationResultPlan final : public ResultPlan {
258399
Initialization *tupleInit;
259400
SmallVector<InitializationPtr, 4> eltInitsBuffer;
260401
MutableArrayRef<InitializationPtr> eltInits;
@@ -305,7 +446,7 @@ class TupleInitializationResultPlan : public ResultPlan {
305446
}
306447
};
307448

308-
class ForeignErrorInitializationPlan : public ResultPlan {
449+
class ForeignErrorInitializationPlan final : public ResultPlan {
309450
SILLocation loc;
310451
LValue lvalue;
311452
ResultPlanPtr subPlan;
@@ -466,6 +607,16 @@ ResultPlanPtr ResultPlanBuilder::build(Initialization *init,
466607
// - store it to the destination
467608
// We could break this down into different ResultPlan implementations,
468609
// but it's easier not to.
610+
611+
// If the result type involves an indirectly-returned opened existential,
612+
// then we need to evaluate the arguments first in order to have access to
613+
// the opened Self type. A special result plan defers allocating the stack
614+
// slot to the point the call is emitted.
615+
if (result.getType()->hasOpenedExistential()
616+
&& SGF.silConv.isSILIndirect(result)) {
617+
return ResultPlanPtr(
618+
new IndirectOpenedSelfResultPlan(SGF, origType, substType));
619+
}
469620

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

lib/SILGen/SILGenApply.cpp

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

44104410
auto directResultsArray = makeArrayRef(directResults);
44114411
RValue result =
4412-
resultPlan->finish(*this, loc, substResultType, directResultsArray);
4412+
resultPlan->finish(*this, loc, substResultType, directResultsArray);
44134413
assert(directResultsArray.empty() && "didn't claim all direct results");
44144414

44154415
return result;

0 commit comments

Comments
 (0)