Skip to content

Commit 0e93232

Browse files
committed
Fix fulfillments for type metadata and witness table packs
A lot of the fixes here are adjustments to compensate in the fulfillment and metadata-path subsystems for the recent pack substitutions representation change. I think these adjustments really make the case for why the change was the right one to make: the code was clearly not considering the possibility of packs in these positions, and the need to handle packs makes everything work out much more cleanly. There's still some work that needs to happen around type packs; in particular, we're not caching them or fulfilling them as a whole, and we do have the setup to do that properly now.
1 parent de73859 commit 0e93232

16 files changed

+395
-52
lines changed

include/swift/IRGen/GenericRequirement.h

+12
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ class GenericRequirement {
8282
}
8383

8484
bool isMetadata() const {
85+
return kind == Kind::Metadata;
86+
}
87+
bool isMetadataPack() const {
88+
return kind == Kind::MetadataPack;
89+
}
90+
bool isAnyMetadata() const {
8591
return kind == Kind::Metadata || kind == Kind::MetadataPack;
8692
}
8793

@@ -94,6 +100,12 @@ class GenericRequirement {
94100
}
95101

96102
bool isWitnessTable() const {
103+
return kind == Kind::WitnessTable;
104+
}
105+
bool isWitnessTablePack() const {
106+
return kind == Kind::WitnessTablePack;
107+
}
108+
bool isAnyWitnessTable() const {
97109
return kind == Kind::WitnessTable || kind == Kind::WitnessTablePack;
98110
}
99111

lib/IRGen/Fulfillment.cpp

+100-13
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,46 @@ bool FulfillmentMap::searchTypeMetadata(IRGenModule &IGM, CanType type,
179179
return false;
180180
}
181181

182+
static CanType getSingletonPackExpansionParameter(CanPackType packType,
183+
const FulfillmentMap::InterestingKeysCallback &keys,
184+
Optional<unsigned> &packExpansionComponent) {
185+
if (packType->getNumElements() != 1)
186+
return CanType();
187+
auto expansion = dyn_cast<PackExpansionType>(packType.getElementType(0));
188+
if (!expansion || !keys.isInterestingPackExpansion(expansion))
189+
return CanType();
190+
191+
packExpansionComponent = 0;
192+
return expansion.getPatternType();
193+
}
194+
195+
bool FulfillmentMap::searchTypeMetadataPack(IRGenModule &IGM,
196+
CanPackType packType,
197+
IsExact_t isExact,
198+
MetadataState metadataState,
199+
unsigned source,
200+
MetadataPath &&path,
201+
const InterestingKeysCallback &keys) {
202+
// We can fulfill pack parameters if the pack is a singleton pack
203+
// expansion over one.
204+
// TODO: we can also fulfill pack expansions if we can slice away
205+
// constant-sized prefixes and suffixes.
206+
Optional<unsigned> packExpansionComponent;
207+
if (auto parameter = getSingletonPackExpansionParameter(packType, keys,
208+
packExpansionComponent)) {
209+
MetadataPath singletonPath = path;
210+
singletonPath.addPackExpansionPatternComponent(*packExpansionComponent);
211+
return addFulfillment(GenericRequirement::forMetadata(parameter),
212+
source, std::move(singletonPath), metadataState);
213+
}
214+
215+
// TODO: fulfill non-expansion metadata out of the pack
216+
217+
// TODO: fulfill the pack type itself
218+
219+
return false;
220+
}
221+
182222
bool FulfillmentMap::searchConformance(
183223
IRGenModule &IGM, const ProtocolConformance *conformance,
184224
unsigned sourceIndex, MetadataPath &&path,
@@ -297,10 +337,8 @@ bool FulfillmentMap::searchNominalTypeMetadata(IRGenModule &IGM,
297337
MetadataPath argPath = path;
298338
argPath.addNominalTypeArgumentShapeComponent(reqtIndex);
299339

300-
// Add the fulfillment.
301-
hadFulfillment |= addFulfillment(GenericRequirement::forShape(arg),
302-
source, std::move(argPath),
303-
MetadataState::Complete);
340+
hadFulfillment |= searchShapeRequirement(IGM, arg, source,
341+
std::move(argPath));
304342
break;
305343
}
306344
case GenericRequirement::Kind::Metadata:
@@ -310,22 +348,44 @@ bool FulfillmentMap::searchNominalTypeMetadata(IRGenModule &IGM,
310348
getPresumedMetadataStateForTypeArgument(metadataState);
311349
MetadataPath argPath = path;
312350
argPath.addNominalTypeArgumentComponent(reqtIndex);
313-
hadFulfillment |= searchTypeMetadata(
314-
IGM, arg, IsExact, argState, source, std::move(argPath), keys);
351+
352+
if (requirement.getKind() == GenericRequirement::Kind::Metadata)
353+
hadFulfillment |=
354+
searchTypeMetadata(IGM, arg, IsExact, argState,
355+
source, std::move(argPath), keys);
356+
else
357+
hadFulfillment |=
358+
searchTypeMetadataPack(IGM, cast<PackType>(arg), IsExact, argState,
359+
source, std::move(argPath), keys);
315360
break;
316361
}
317-
case GenericRequirement::Kind::WitnessTable:
318-
case GenericRequirement::Kind::WitnessTablePack: {
319-
// Ignore it unless the type itself is interesting.
320-
if (!keys.isInterestingType(arg))
321-
continue;
362+
case GenericRequirement::Kind::WitnessTablePack:
363+
case GenericRequirement::Kind::WitnessTable: {
364+
Optional<unsigned> packExpansionComponent;
365+
if (requirement.getKind() == GenericRequirement::Kind::WitnessTable) {
366+
// Ignore it unless the type itself is interesting.
367+
if (!keys.isInterestingType(arg))
368+
continue;
369+
} else {
370+
// Ignore it unless the pack is a singleton pack expansion of a
371+
// type parameter, in which case use that type below.
372+
auto param =
373+
getSingletonPackExpansionParameter(cast<PackType>(arg), keys,
374+
packExpansionComponent);
375+
if (!param) continue;
376+
arg = param;
377+
}
322378

323379
// Refine the path.
324380
MetadataPath argPath = path;
325381
argPath.addNominalTypeArgumentConformanceComponent(reqtIndex);
382+
if (packExpansionComponent)
383+
argPath.addPackExpansionPatternComponent(*packExpansionComponent);
326384

327-
hadFulfillment |= searchWitnessTable(IGM, arg, requirement.getProtocol(),
328-
source, std::move(argPath), keys);
385+
// This code just handles packs directly.
386+
hadFulfillment |=
387+
searchWitnessTable(IGM, arg, requirement.getProtocol(),
388+
source, std::move(argPath), keys);
329389
break;
330390
}
331391
}
@@ -334,6 +394,33 @@ bool FulfillmentMap::searchNominalTypeMetadata(IRGenModule &IGM,
334394
return hadFulfillment;
335395
}
336396

397+
bool FulfillmentMap::searchShapeRequirement(IRGenModule &IGM, CanType argType,
398+
unsigned source, MetadataPath &&path) {
399+
// argType is the substitution for a pack parameter, so it should always
400+
// be a pack.
401+
auto packType = cast<PackType>(argType);
402+
403+
// For now, don't try to fulfill shapes if this isn't a singleton
404+
// pack containing a pack expansion. In theory, though, as long as
405+
// there aren't expansions over pack parameters with different shapes,
406+
// we should always be able to turn this into the equation
407+
// `ax + b = <fulfilled count>` and then solve that.
408+
if (packType->getNumElements() != 1)
409+
return false;
410+
auto expansion =
411+
dyn_cast<PackExpansionType>(packType.getElementType(0));
412+
if (!expansion)
413+
return false;
414+
415+
path.addPackExpansionCountComponent(0);
416+
417+
auto parameter = expansion.getCountType();
418+
419+
// Add the fulfillment.
420+
return addFulfillment(GenericRequirement::forShape(parameter),
421+
source, std::move(path), MetadataState::Complete);
422+
}
423+
337424
/// Testify that there's a fulfillment at the given path.
338425
bool FulfillmentMap::addFulfillment(GenericRequirement key,
339426
unsigned source,

lib/IRGen/Fulfillment.h

+17
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ class FulfillmentMap {
6262
/// It's okay to conservatively return true here.
6363
virtual bool hasInterestingType(CanType type) const = 0;
6464

65+
/// Is the given pack expansion a simple expansion of an interesting
66+
/// type?
67+
virtual bool isInterestingPackExpansion(CanPackExpansionType type) const = 0;
68+
6569
/// Are we only interested in a subset of the conformances for a
6670
/// given type?
6771
virtual bool hasLimitedInterestingConformances(CanType type) const = 0;
@@ -98,6 +102,15 @@ class FulfillmentMap {
98102
unsigned sourceIndex, MetadataPath &&path,
99103
const InterestingKeysCallback &interestingKeys);
100104

105+
/// Search the given type metadata pack for useful fulfillments.
106+
///
107+
/// \return true if any fulfillments were added by this search.
108+
bool searchTypeMetadataPack(IRGenModule &IGM, CanPackType type,
109+
IsExact_t isExact,
110+
MetadataState metadataState,
111+
unsigned sourceIndex, MetadataPath &&path,
112+
const InterestingKeysCallback &interestingKeys);
113+
101114
bool searchConformance(IRGenModule &IGM,
102115
const ProtocolConformance *conformance,
103116
unsigned sourceIndex, MetadataPath &&path,
@@ -110,6 +123,10 @@ class FulfillmentMap {
110123
unsigned sourceIndex, MetadataPath &&path,
111124
const InterestingKeysCallback &interestingKeys);
112125

126+
/// Consider a shape requirement for the given type.
127+
bool searchShapeRequirement(IRGenModule &IGM, CanType type,
128+
unsigned sourceIndex, MetadataPath &&path);
129+
113130
/// Register a fulfillment for the given key.
114131
///
115132
/// \return true if the fulfillment was added, which won't happen if there's

lib/IRGen/GenArchetype.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -442,13 +442,13 @@ withOpaqueTypeGenericArgs(IRGenFunction &IGF,
442442
[&](GenericRequirement reqt) {
443443
auto ty = reqt.getTypeParameter().subst(archetype->getSubstitutions())
444444
->getReducedType(opaqueDecl->getGenericSignature());
445-
if (reqt.isWitnessTable()) {
445+
if (reqt.isAnyWitnessTable()) {
446446
auto ref =
447447
ProtocolConformanceRef(reqt.getProtocol())
448448
.subst(reqt.getTypeParameter(), archetype->getSubstitutions());
449449
args.push_back(emitWitnessTableRef(IGF, ty, ref));
450450
} else {
451-
assert(reqt.isMetadata());
451+
assert(reqt.isAnyMetadata());
452452
args.push_back(IGF.emitAbstractTypeMetadataRef(ty));
453453
}
454454
types.push_back(args.back()->getType());

lib/IRGen/GenKeyPath.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -896,12 +896,12 @@ emitKeyPathComponent(IRGenModule &IGM,
896896
return None;
897897
})->getCanonicalType();
898898

899-
if (reqt.isMetadata()) {
899+
if (reqt.isAnyMetadata()) {
900900
// Type requirement.
901901
externalSubArgs.push_back(emitMetadataTypeRefForKeyPath(
902902
IGM, substType, componentCanSig));
903903
} else {
904-
assert(reqt.isWitnessTable());
904+
assert(reqt.isAnyWitnessTable());
905905

906906
// Protocol requirement.
907907
auto conformance = subs.lookupConformance(

lib/IRGen/GenMeta.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -4432,15 +4432,15 @@ namespace {
44324432
}
44334433

44344434
void addGenericRequirement(GenericRequirement requirement) {
4435-
if (requirement.isMetadata()) {
4435+
if (requirement.isAnyMetadata()) {
44364436
auto t = requirement.getTypeParameter().subst(genericSubstitutions());
44374437
ConstantReference ref = IGM.getAddrOfTypeMetadata(
44384438
CanType(t), SymbolReferenceKind::Relative_Direct);
44394439
this->B.add(ref.getDirectValue());
44404440
return;
44414441
}
44424442

4443-
assert(requirement.isWitnessTable());
4443+
assert(requirement.isAnyWitnessTable());
44444444
auto conformance = genericSubstitutions().lookupConformance(
44454445
requirement.getTypeParameter()->getCanonicalType(),
44464446
requirement.getProtocol());

lib/IRGen/GenMeta.h

+18
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,15 @@ namespace irgen {
121121
unsigned reqtIndex,
122122
llvm::Value *metadata);
123123

124+
/// Given a reference to nominal type metadata of the given type,
125+
/// derive a reference to the type metadata pack stored in the nth
126+
/// requirement slot. The type must have generic arguments.
127+
llvm::Value *emitArgumentMetadataPackRef(IRGenFunction &IGF,
128+
NominalTypeDecl *theDecl,
129+
const GenericTypeRequirements &reqts,
130+
unsigned reqtIndex,
131+
llvm::Value *metadata);
132+
124133
/// Given a reference to nominal type metadata of the given type,
125134
/// derive a reference to a protocol witness table stored in the nth
126135
/// requirement slot. The type must have generic arguments.
@@ -130,6 +139,15 @@ namespace irgen {
130139
unsigned reqtIndex,
131140
llvm::Value *metadata);
132141

142+
/// Given a reference to nominal type metadata of the given type,
143+
/// derive a reference to a protocol witness table pack stored in the nth
144+
/// requirement slot. The type must have generic arguments.
145+
llvm::Value *emitArgumentWitnessTablePackRef(IRGenFunction &IGF,
146+
NominalTypeDecl *theDecl,
147+
const GenericTypeRequirements &reqts,
148+
unsigned reqtIndex,
149+
llvm::Value *metadata);
150+
133151
/// Given a reference to nominal type metadata of the given type,
134152
/// derive a reference to a the pack shape stored in the nth
135153
/// requirement slot. The type must have generic arguments.

0 commit comments

Comments
 (0)