Skip to content

Commit ee0e38a

Browse files
authored
Merge pull request #64273 from rjmccall/variadic-generic-type-metadata
Omnibus fixes to IRGen for variadic generic type metadata
2 parents 8a16cdc + 0e93232 commit ee0e38a

17 files changed

+564
-93
lines changed

include/swift/IRGen/GenericRequirement.h

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,10 @@ class GenericRequirement {
5555
CanType type;
5656
ProtocolDecl *proto;
5757

58+
public:
5859
GenericRequirement(Kind kind, CanType type, ProtocolDecl *proto)
5960
: kind(kind), type(type), proto(proto) {}
6061

61-
public:
6262
Kind getKind() const {
6363
return kind;
6464
}
@@ -76,35 +76,46 @@ class GenericRequirement {
7676
}
7777

7878
static GenericRequirement forShape(CanType type) {
79-
assert(type->isParameterPack());
79+
assert(!isa<PackType>(type));
80+
assert(type->isParameterPack() || isa<PackArchetypeType>(type));
8081
return GenericRequirement(Kind::Shape, type, nullptr);
8182
}
8283

8384
bool isMetadata() const {
84-
return kind == Kind::Metadata || kind == Kind::MetadataPack;
85+
return kind == Kind::Metadata;
8586
}
86-
87-
static GenericRequirement forMetadata(CanType type, bool isPack) {
88-
auto kind = isPack ? Kind::MetadataPack : Kind::Metadata;
89-
return GenericRequirement(kind, type, nullptr);
87+
bool isMetadataPack() const {
88+
return kind == Kind::MetadataPack;
89+
}
90+
bool isAnyMetadata() const {
91+
return kind == Kind::Metadata || kind == Kind::MetadataPack;
9092
}
9193

9294
static GenericRequirement forMetadata(CanType type) {
93-
return forMetadata(type, type->hasParameterPack());
95+
assert(!isa<PackType>(type));
96+
auto kind = ((type->isParameterPack() ||
97+
isa<PackArchetypeType>(type))
98+
? Kind::MetadataPack : Kind::Metadata);
99+
return GenericRequirement(kind, type, nullptr);
94100
}
95101

96102
bool isWitnessTable() const {
97-
return kind == Kind::WitnessTable || kind == Kind::WitnessTablePack;
103+
return kind == Kind::WitnessTable;
98104
}
99-
100-
static GenericRequirement forWitnessTable(CanType type, ProtocolDecl *proto,
101-
bool isPack) {
102-
auto kind = isPack ? Kind::WitnessTablePack : Kind::WitnessTable;
103-
return GenericRequirement(kind, type, proto);
105+
bool isWitnessTablePack() const {
106+
return kind == Kind::WitnessTablePack;
107+
}
108+
bool isAnyWitnessTable() const {
109+
return kind == Kind::WitnessTable || kind == Kind::WitnessTablePack;
104110
}
105111

106112
static GenericRequirement forWitnessTable(CanType type, ProtocolDecl *proto) {
107-
return forWitnessTable(type, proto, type->hasParameterPack());
113+
assert(!isa<PackType>(type));
114+
auto kind = ((type->isParameterPack() ||
115+
isa<PackArchetypeType>(type))
116+
? Kind::WitnessTablePack
117+
: Kind::WitnessTable);
118+
return GenericRequirement(kind, type, proto);
108119
}
109120

110121
static llvm::Type *typeForKind(irgen::IRGenModule &IGM,
@@ -142,12 +153,14 @@ template <> struct DenseMapInfo<swift::GenericRequirement> {
142153
using GenericRequirement = swift::GenericRequirement;
143154
using CanTypeInfo = llvm::DenseMapInfo<swift::CanType>;
144155
static GenericRequirement getEmptyKey() {
145-
return GenericRequirement::forMetadata(CanTypeInfo::getEmptyKey(),
146-
/*isPack=*/false);
156+
return GenericRequirement(GenericRequirement::Kind::Metadata,
157+
CanTypeInfo::getEmptyKey(),
158+
nullptr);
147159
}
148160
static GenericRequirement getTombstoneKey() {
149-
return GenericRequirement::forMetadata(CanTypeInfo::getTombstoneKey(),
150-
/*isPack=*/false);
161+
return GenericRequirement(GenericRequirement::Kind::Metadata,
162+
CanTypeInfo::getTombstoneKey(),
163+
nullptr);
151164
}
152165
static llvm::hash_code getHashValue(GenericRequirement req) {
153166
return hash_combine(CanTypeInfo::getHashValue(req.getTypeParameter()),

lib/IRGen/Fulfillment.cpp

Lines changed: 118 additions & 17 deletions
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,
@@ -291,33 +331,94 @@ bool FulfillmentMap::searchNominalTypeMetadata(IRGenModule &IGM,
291331
if (!keys.hasInterestingType(arg))
292332
continue;
293333

294-
// If the fulfilled value is type metadata, refine the path.
295-
if (requirement.isMetadata()) {
334+
switch (requirement.getKind()) {
335+
case GenericRequirement::Kind::Shape: {
336+
// If the fulfilled value is a shape class, refine the path.
337+
MetadataPath argPath = path;
338+
argPath.addNominalTypeArgumentShapeComponent(reqtIndex);
339+
340+
hadFulfillment |= searchShapeRequirement(IGM, arg, source,
341+
std::move(argPath));
342+
break;
343+
}
344+
case GenericRequirement::Kind::Metadata:
345+
case GenericRequirement::Kind::MetadataPack: {
346+
// If the fulfilled value is type metadata, refine the path.
296347
auto argState =
297348
getPresumedMetadataStateForTypeArgument(metadataState);
298349
MetadataPath argPath = path;
299350
argPath.addNominalTypeArgumentComponent(reqtIndex);
300-
hadFulfillment |= searchTypeMetadata(
301-
IGM, arg, IsExact, argState, source, std::move(argPath), keys);
302-
continue;
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);
360+
break;
303361
}
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+
}
304378

305-
// Otherwise, it's a conformance.
306-
assert(requirement.isWitnessTable());
379+
// Refine the path.
380+
MetadataPath argPath = path;
381+
argPath.addNominalTypeArgumentConformanceComponent(reqtIndex);
382+
if (packExpansionComponent)
383+
argPath.addPackExpansionPatternComponent(*packExpansionComponent);
384+
385+
// This code just handles packs directly.
386+
hadFulfillment |=
387+
searchWitnessTable(IGM, arg, requirement.getProtocol(),
388+
source, std::move(argPath), keys);
389+
break;
390+
}
391+
}
392+
}
307393

308-
// Ignore it unless the type itself is interesting.
309-
if (!keys.isInterestingType(arg))
310-
continue;
394+
return hadFulfillment;
395+
}
311396

312-
// Refine the path.
313-
MetadataPath argPath = path;
314-
argPath.addNominalTypeArgumentConformanceComponent(reqtIndex);
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;
315414

316-
hadFulfillment |= searchWitnessTable(IGM, arg, requirement.getProtocol(),
317-
source, std::move(argPath), keys);
318-
}
415+
path.addPackExpansionCountComponent(0);
319416

320-
return hadFulfillment;
417+
auto parameter = expansion.getCountType();
418+
419+
// Add the fulfillment.
420+
return addFulfillment(GenericRequirement::forShape(parameter),
421+
source, std::move(path), MetadataState::Complete);
321422
}
322423

323424
/// Testify that there's a fulfillment at the given path.

lib/IRGen/Fulfillment.h

Lines changed: 17 additions & 0 deletions
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

Lines changed: 2 additions & 2 deletions
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

Lines changed: 2 additions & 2 deletions
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

Lines changed: 2 additions & 2 deletions
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

Lines changed: 27 additions & 0 deletions
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,24 @@ 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+
151+
/// Given a reference to nominal type metadata of the given type,
152+
/// derive a reference to a the pack shape stored in the nth
153+
/// requirement slot. The type must have generic arguments.
154+
llvm::Value *emitArgumentPackShapeRef(IRGenFunction &IGF,
155+
NominalTypeDecl *theDecl,
156+
const GenericTypeRequirements &reqts,
157+
unsigned reqtIndex,
158+
llvm::Value *metadata);
159+
133160
/// Given a metatype value, read its instance type.
134161
llvm::Value *emitMetatypeInstanceType(IRGenFunction &IGF,
135162
llvm::Value *metatypeMetadata);

0 commit comments

Comments
 (0)