Skip to content

Commit c041d10

Browse files
committed
Perform component-wise substitution of pack expansions immediately.
Substitution of a pack expansion type may now produce a pack type. We immediately expand that pack when transforming a tuple, a function parameter, or a pack. I had to duplicate the component-wise transformation logic in the simplifyType transform, which I'm not pleased about, but a little code duplication seemed a lot better than trying to unify the code in two very different places. I think we're very close to being able to assert that pack expansion shapes are either pack archetypes or pack parameters; unfortunately, the pack matchers intentionally produce expansions of packs, and I didn't want to add that to an already-large patch.
1 parent d16fed6 commit c041d10

File tree

9 files changed

+399
-387
lines changed

9 files changed

+399
-387
lines changed

include/swift/AST/InFlightSubstitution.h

Lines changed: 70 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ class InFlightSubstitution {
3232
TypeSubstitutionFn BaselineSubstType;
3333
LookupConformanceFn BaselineLookupConformance;
3434

35+
struct ActivePackExpansion {
36+
bool isSubstExpansion = false;
37+
unsigned expansionIndex = 0;
38+
};
39+
SmallVector<ActivePackExpansion, 4> ActivePackExpansions;
40+
3541
public:
3642
InFlightSubstitution(TypeSubstitutionFn substType,
3743
LookupConformanceFn lookupConformance,
@@ -43,16 +49,72 @@ class InFlightSubstitution {
4349
InFlightSubstitution(const InFlightSubstitution &) = delete;
4450
InFlightSubstitution &operator=(const InFlightSubstitution &) = delete;
4551

46-
Type substType(SubstitutableType *ty) {
47-
return BaselineSubstType(ty);
48-
}
49-
52+
// TODO: when we add PackElementType, we should recognize it during
53+
// substitution and either call different methods on this class or
54+
// pass an extra argument for the pack-expansion depth D. We should
55+
// be able to rely on that to mark a pack-element reference instead
56+
// of checking whether the original type was a pack. Substitution
57+
// should use the D'th entry from the end of ActivePackExpansions to
58+
// guide the element substitution:
59+
// - project the given index of the pack substitution
60+
// - wrap it in a PackElementType if it's a subst expansion
61+
// - the depth of that PackElementType is the number of subst
62+
// expansions between the depth entry and the end of
63+
// ActivePackExpansions
64+
65+
/// Perform primitive substitution on the given type. Returns Type()
66+
/// if the type should not be substituted as a whole.
67+
Type substType(SubstitutableType *origType);
68+
69+
/// Perform primitive conformance lookup on the given type.
5070
ProtocolConformanceRef lookupConformance(CanType dependentType,
5171
Type conformingReplacementType,
52-
ProtocolDecl *conformedProtocol) {
53-
return BaselineLookupConformance(dependentType,
54-
conformingReplacementType,
55-
conformedProtocol);
72+
ProtocolDecl *conformedProtocol);
73+
74+
/// Given the shape type of a pack expansion, invoke the given callback
75+
/// for each expanded component of it. If the substituted component
76+
/// is an expansion component, the desired shape of that expansion
77+
/// is passed as the argument; otherwise, the argument is Type().
78+
/// In either case, an active expansion is entered on this IFS for
79+
/// the duration of the call to handleComponent, and subsequent
80+
/// pack-element type references will substitute to the corresponding
81+
/// element of the substitution of the pack.
82+
void expandPackExpansionShape(Type origShape,
83+
llvm::function_ref<void(Type substComponentShape)> handleComponent);
84+
85+
/// Call the given function for each expanded component type of the
86+
/// given pack expansion type. The function will be invoked with the
87+
/// active expansion still active.
88+
void expandPackExpansionType(PackExpansionType *origExpansionType,
89+
llvm::function_ref<void(Type substType)> handleComponentType) {
90+
expandPackExpansionShape(origExpansionType->getCountType(),
91+
[&](Type substComponentShape) {
92+
auto origPatternType = origExpansionType->getPatternType();
93+
auto substEltType = origPatternType.subst(*this);
94+
95+
auto substComponentType =
96+
(substComponentShape
97+
? PackExpansionType::get(substEltType, substComponentShape)
98+
: substEltType);
99+
handleComponentType(substComponentType);
100+
});
101+
}
102+
103+
/// Return a list of component types that the pack expansion expands to.
104+
SmallVector<Type, 8>
105+
expandPackExpansionType(PackExpansionType *origExpansionType) {
106+
SmallVector<Type, 8> substComponentTypes;
107+
expandPackExpansionType(origExpansionType, substComponentTypes);
108+
return substComponentTypes;
109+
}
110+
111+
/// Expand the list of component types that the pack expansion expands
112+
/// to into the given array.
113+
void expandPackExpansionType(PackExpansionType *origExpansionType,
114+
SmallVectorImpl<Type> &substComponentTypes) {
115+
expandPackExpansionType(origExpansionType, [&](Type substComponentType) {
116+
substComponentTypes.push_back(substComponentType);
117+
});
56118
}
57119

58120
class OptionsAdjustmentScope {

lib/AST/ASTContext.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3257,6 +3257,12 @@ CanPackExpansionType::get(CanType patternType, CanType countType) {
32573257
}
32583258

32593259
PackExpansionType *PackExpansionType::get(Type patternType, Type countType) {
3260+
assert(!patternType->is<PackExpansionType>());
3261+
assert(!countType->is<PackExpansionType>());
3262+
// FIXME: stop doing this deliberately in PackExpansionMatcher
3263+
//assert(!patternType->is<PackType>());
3264+
//assert(!countType->is<PackType>());
3265+
32603266
auto properties = patternType->getRecursiveProperties();
32613267
properties |= countType->getRecursiveProperties();
32623268

lib/AST/PackConformance.cpp

Lines changed: 41 additions & 183 deletions
Original file line numberDiff line numberDiff line change
@@ -167,207 +167,65 @@ ProtocolConformanceRef PackConformance::subst(SubstitutionMap subMap,
167167
return subst(IFS);
168168
}
169169

170-
// TODO: Move this elsewhere since it's generally useful
171-
static bool arePackShapesEqual(PackType *lhs, PackType *rhs) {
172-
if (lhs->getNumElements() != rhs->getNumElements())
173-
return false;
174-
175-
for (unsigned i = 0, e = lhs->getNumElements(); i < e; ++i) {
176-
auto lhsElt = lhs->getElementType(i);
177-
auto rhsElt = rhs->getElementType(i);
178-
179-
if (lhsElt->is<PackExpansionType>() != rhsElt->is<PackExpansionType>())
180-
return false;
181-
}
182-
183-
return true;
184-
}
185-
186-
static bool isRootParameterPack(Type t) {
187-
if (auto *paramTy = t->getAs<GenericTypeParamType>()) {
188-
return paramTy->isParameterPack();
189-
} else if (auto *archetypeTy = t->getAs<PackArchetypeType>()) {
190-
return archetypeTy->isRoot();
191-
}
192-
193-
return false;
194-
}
195-
196-
static bool isRootedInParameterPack(Type t) {
197-
if (auto *archetypeTy = t->getAs<PackArchetypeType>()) {
198-
return true;
199-
}
200-
201-
return t->getRootGenericParam()->isParameterPack();
202-
}
203-
204170
namespace {
205171

206-
template<typename ImplClass>
207-
class PackExpander {
208-
protected:
172+
struct PackConformanceExpander {
209173
InFlightSubstitution &IFS;
174+
ArrayRef<ProtocolConformanceRef> origConformances;
210175

211-
PackExpander(InFlightSubstitution &IFS) : IFS(IFS) {}
212-
213-
ImplClass *asImpl() {
214-
return static_cast<ImplClass *>(this);
215-
}
216-
217-
/// We're replacing a pack expansion type with a pack -- flatten the pack
218-
/// using the pack expansion's pattern.
219-
void addExpandedExpansion(Type origPatternType, PackType *expandedCountType,
220-
unsigned i) {
221-
222-
// Get all pack parameters referenced from the pattern.
223-
SmallVector<Type, 2> rootParameterPacks;
224-
origPatternType->getTypeParameterPacks(rootParameterPacks);
225-
226-
// Each pack parameter referenced from the pattern must be replaced
227-
// with a pack type, and all pack types must have the same shape as
228-
// the expanded count pack type.
229-
llvm::SmallDenseMap<Type, PackType *, 2> expandedPacks;
230-
for (auto origParamType : rootParameterPacks) {
231-
auto substParamType = origParamType.subst(IFS);
232-
233-
if (auto expandedParamType = substParamType->template getAs<PackType>()) {
234-
assert(arePackShapesEqual(expandedParamType, expandedCountType) &&
235-
"TODO: Return an invalid conformance if this fails");
236-
237-
auto inserted = expandedPacks.insert(
238-
std::make_pair(origParamType->getCanonicalType(),
239-
expandedParamType)).second;
240-
assert(inserted &&
241-
"getTypeParameterPacks() should not return duplicates");
242-
} else {
243-
assert(false &&
244-
"TODO: Return an invalid conformance if this fails");
245-
}
246-
}
247-
248-
// For each element of the expanded count, compute the substituted
249-
// pattern type.
250-
for (unsigned j = 0, ee = expandedCountType->getNumElements(); j < ee; ++j) {
251-
auto projectedSubs = [&](SubstitutableType *type) -> Type {
252-
// Nested sequence archetypes get passed in here, but we must
253-
// handle them via the standard nested type path.
254-
if (auto *archetypeType = dyn_cast<ArchetypeType>(type)) {
255-
if (!archetypeType->isRoot())
256-
return Type();
257-
}
258-
259-
// Compute the substituted type using our parent substitutions.
260-
auto substType = Type(type).subst(IFS);
261-
262-
// If the substituted type is a pack, project the jth element.
263-
if (isRootParameterPack(type)) {
264-
// FIXME: What if you have something like G<T...>... where G<> is
265-
// variadic?
266-
assert(substType->template is<PackType>() &&
267-
"TODO: Return an invalid conformance if this fails");
268-
auto *packType = substType->template castTo<PackType>();
269-
assert(arePackShapesEqual(packType, expandedCountType) &&
270-
"TODO: Return an invalid conformance if this fails");
271-
272-
return packType->getElementType(j);
273-
}
274-
275-
return IFS.substType(type);
276-
};
277-
278-
auto projectedConformances = [&](CanType origType, Type substType,
279-
ProtocolDecl *proto) -> ProtocolConformanceRef {
280-
auto substConformance =
281-
IFS.lookupConformance(origType, substType, proto);
282-
283-
// If the substituted conformance is a pack, project the jth element.
284-
if (isRootedInParameterPack(origType)) {
285-
return substConformance.getPack()->getPatternConformances()[j];
286-
}
287-
288-
return substConformance;
289-
};
290-
291-
auto origCountElement = expandedCountType->getElementType(j);
292-
auto substCountElement = origCountElement.subst(
293-
projectedSubs, projectedConformances, IFS.getOptions());
294-
295-
asImpl()->add(origCountElement, substCountElement, i);
296-
}
297-
}
298-
299-
/// A pack expansion remains unexpanded, so we substitute the pattern and
300-
/// form a new pack expansion.
301-
void addUnexpandedExpansion(Type origPatternType, Type substCountType,
302-
unsigned i) {
303-
auto substPatternType = origPatternType.subst(IFS);
304-
auto substExpansion = PackExpansionType::get(substPatternType, substCountType);
176+
public:
177+
// Results built up by the expansion.
178+
SmallVector<Type, 4> substElementTypes;
179+
SmallVector<ProtocolConformanceRef, 4> substConformances;
305180

306-
asImpl()->add(origPatternType, substExpansion, i);
307-
}
181+
PackConformanceExpander(InFlightSubstitution &IFS,
182+
ArrayRef<ProtocolConformanceRef> origConformances)
183+
: IFS(IFS), origConformances(origConformances) {}
308184

309-
/// Scalar elements of the original pack are substituted and added to the
310-
/// flattened pack.
311-
void addScalar(Type origElement, unsigned i) {
312-
auto substElement = origElement.subst(IFS);
185+
private:
186+
/// Substitute a scalar element of the original pack.
187+
void substScalar(Type origElementType,
188+
ProtocolConformanceRef origConformance) {
189+
auto substElementType = origElementType.subst(IFS);
190+
auto substConformance = origConformance.subst(origElementType, IFS);
313191

314-
asImpl()->add(origElement, substElement, i);
192+
substElementTypes.push_back(substElementType);
193+
substConformances.push_back(substConformance);
315194
}
316195

317-
/// Potentially expand an element of the original pack.
318-
void maybeExpandExpansion(PackExpansionType *origExpansion, unsigned i) {
319-
auto origPatternType = origExpansion->getPatternType();
320-
auto origCountType = origExpansion->getCountType();
321-
322-
auto substCountType = origCountType.subst(IFS);
323-
324-
// If the substituted count type is a pack, we're expanding the
325-
// original element.
326-
if (auto *expandedCountType = substCountType->template getAs<PackType>()) {
327-
addExpandedExpansion(origPatternType, expandedCountType, i);
328-
return;
329-
}
330-
331-
addUnexpandedExpansion(origPatternType, substCountType, i);
196+
/// Substitute and expand an expansion element of the original pack.
197+
void substExpansion(PackExpansionType *origExpansionType,
198+
ProtocolConformanceRef origConformance) {
199+
IFS.expandPackExpansionType(origExpansionType,
200+
[&](Type substComponentType) {
201+
auto origPatternType = origExpansionType->getPatternType();
202+
203+
// Just substitute the conformance. We don't directly represent
204+
// pack expansion conformances here; it's sort of implicit in the
205+
// corresponding pack element type.
206+
auto substConformance = origConformance.subst(origPatternType, IFS);
207+
208+
substElementTypes.push_back(substComponentType);
209+
substConformances.push_back(substConformance);
210+
});
332211
}
333212

334213
public:
335214
void expand(PackType *origPackType) {
336-
for (unsigned i = 0, e = origPackType->getNumElements(); i < e; ++i) {
337-
auto origElement = origPackType->getElementType(i);
215+
assert(origPackType->getNumElements() == origConformances.size());
338216

339-
// Check if the original element is potentially being expanded.
340-
if (auto *origExpansion = origElement->getAs<PackExpansionType>()) {
341-
maybeExpandExpansion(origExpansion, i);
342-
continue;
217+
for (auto i : range(origPackType->getNumElements())) {
218+
auto origElementType = origPackType->getElementType(i);
219+
if (auto *origExpansion = origElementType->getAs<PackExpansionType>()) {
220+
substExpansion(origExpansion, origConformances[i]);
221+
} else {
222+
substScalar(origElementType, origConformances[i]);
343223
}
344-
345-
addScalar(origElement, i);
346224
}
347225
}
348226
};
349227

350-
class PackConformanceExpander : public PackExpander<PackConformanceExpander> {
351-
public:
352-
SmallVector<Type, 4> substElements;
353-
SmallVector<ProtocolConformanceRef, 4> substConformances;
354-
355-
ArrayRef<ProtocolConformanceRef> origConformances;
356-
357-
PackConformanceExpander(InFlightSubstitution &IFS,
358-
ArrayRef<ProtocolConformanceRef> origConformances)
359-
: PackExpander(IFS), origConformances(origConformances) {}
360-
361-
void add(Type origType, Type substType, unsigned i) {
362-
substElements.push_back(substType);
363-
364-
// FIXME: Pass down projection callbacks
365-
substConformances.push_back(origConformances[i].subst(
366-
origType, IFS));
367-
}
368-
};
369-
370-
}
228+
} // end anonymous namespace
371229

372230
ProtocolConformanceRef PackConformance::subst(TypeSubstitutionFn subs,
373231
LookupConformanceFn conformances,
@@ -382,7 +240,7 @@ PackConformance::subst(InFlightSubstitution &IFS) const {
382240
expander.expand(ConformingType);
383241

384242
auto &ctx = Protocol->getASTContext();
385-
auto *substConformingType = PackType::get(ctx, expander.substElements);
243+
auto *substConformingType = PackType::get(ctx, expander.substElementTypes);
386244

387245
auto substConformance = PackConformance::get(substConformingType, Protocol,
388246
expander.substConformances);

lib/AST/ParameterPack.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -449,15 +449,15 @@ PackType *PackType::get(const ASTContext &C,
449449
auto arg = args[i];
450450

451451
if (params[i]->isParameterPack()) {
452-
wrappedArgs.push_back(PackExpansionType::get(
453-
arg, arg->getReducedShape()));
452+
auto argPackElements = arg->castTo<PackType>()->getElementTypes();
453+
wrappedArgs.append(argPackElements.begin(), argPackElements.end());
454454
continue;
455455
}
456456

457457
wrappedArgs.push_back(arg);
458458
}
459459

460-
return get(C, wrappedArgs)->flattenPackTypes();
460+
return get(C, wrappedArgs);
461461
}
462462

463463
PackType *PackType::getSingletonPackExpansion(Type param) {

0 commit comments

Comments
 (0)