Skip to content

Commit 18e6ea7

Browse files
authored
Merge pull request #19486 from DougGregor/runtime-metadata-subst-same-type-extensions
[Runtime] Handle non-key arguments when substituting from metadata.
2 parents 662073e + 3ea4877 commit 18e6ea7

File tree

5 files changed

+206
-53
lines changed

5 files changed

+206
-53
lines changed

stdlib/public/runtime/MetadataLookup.cpp

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -798,6 +798,23 @@ Optional<unsigned> findAssociatedTypeByName(const ProtocolDescriptor *protocol,
798798
swift_runtime_unreachable("associated type names don't line up");
799799
}
800800

801+
/// Retrieve the generic parameters introduced in this context.
802+
static ArrayRef<GenericParamDescriptor> getLocalGenericParams(
803+
const ContextDescriptor *context) {
804+
if (!context->isGeneric())
805+
return { };
806+
807+
// Determine where to start looking at generic parameters.
808+
unsigned startParamIndex;
809+
if (auto parent = context->Parent.get())
810+
startParamIndex = parent->getNumGenericParams();
811+
else
812+
startParamIndex = 0;
813+
814+
auto genericContext = context->getGenericContext();
815+
return genericContext->getGenericParams().slice(startParamIndex);
816+
}
817+
801818
/// Constructs metadata by decoding a mangled type name, for use with
802819
/// \c TypeDecoder.
803820
class DecodedMetadataBuilder {
@@ -1223,5 +1240,108 @@ swift_getTypeByMangledNameImpl(const char *typeNameStart, size_t typeNameLength,
12231240
return swift_checkMetadataState(MetadataState::Complete, metadata).Value;
12241241
}
12251242

1243+
unsigned SubstGenericParametersFromMetadata::
1244+
buildDescriptorPath(const ContextDescriptor *context) const {
1245+
// Terminating condition: we don't have a context.
1246+
if (!context)
1247+
return 0;
1248+
1249+
// Add the parent's contributino to the descriptor path.
1250+
unsigned numKeyGenericParamsInParent =
1251+
buildDescriptorPath(context->Parent.get());
1252+
1253+
// If this context is non-generic, we're done.
1254+
if (!context->isGeneric())
1255+
return numKeyGenericParamsInParent;
1256+
1257+
// Count the number of key generic params at this level.
1258+
unsigned numKeyGenericParamsHere = 0;
1259+
bool hasNonKeyGenericParams = false;
1260+
for (const auto &genericParam : getLocalGenericParams(context)) {
1261+
if (genericParam.hasKeyArgument())
1262+
++numKeyGenericParamsHere;
1263+
else
1264+
hasNonKeyGenericParams = true;
1265+
}
1266+
1267+
// Form the path element.
1268+
descriptorPath.push_back(PathElement{context, numKeyGenericParamsInParent,
1269+
numKeyGenericParamsHere,
1270+
hasNonKeyGenericParams});
1271+
return numKeyGenericParamsInParent + numKeyGenericParamsHere;
1272+
}
1273+
1274+
void SubstGenericParametersFromMetadata::setup() const {
1275+
if (!descriptorPath.empty() || !base)
1276+
return;
1277+
1278+
buildDescriptorPath(base->getTypeContextDescriptor());
1279+
}
1280+
1281+
const Metadata *
1282+
SubstGenericParametersFromMetadata::operator()(unsigned flatIndex) const {
1283+
// On first access, compute the descriptor path.
1284+
setup();
1285+
1286+
// Find the depth at which this parameter occurs.
1287+
unsigned depth = descriptorPath.size();
1288+
unsigned index = flatIndex;
1289+
for (const auto &pathElement : descriptorPath) {
1290+
// If the flat index is beyond the element at this position, we're done.
1291+
if (flatIndex >= pathElement.context->getNumGenericParams()) {
1292+
// Subtract off the number of parameters.
1293+
index -= pathElement.context->getNumGenericParams();
1294+
break;
1295+
}
1296+
1297+
--depth;
1298+
}
1299+
1300+
// Perform the access based on depth/index.
1301+
return (*this)(depth, index);
1302+
}
1303+
1304+
const Metadata *
1305+
SubstGenericParametersFromMetadata::operator()(
1306+
unsigned depth, unsigned index) const {
1307+
// On first access, compute the descriptor path.
1308+
setup();
1309+
1310+
// If the depth is too great, there is nothing to do.
1311+
if (depth >= descriptorPath.size())
1312+
return nullptr;
1313+
1314+
/// Retrieve the descriptor path element at this depth.
1315+
auto &pathElement = descriptorPath[depth];
1316+
auto currentContext = pathElement.context;
1317+
1318+
// Check whether the index is clearly out of bounds.
1319+
if (index >= currentContext->getNumGenericParams())
1320+
return nullptr;
1321+
1322+
// Compute the flat index.
1323+
unsigned flatIndex = pathElement.numKeyGenericParamsInParent;
1324+
if (pathElement.hasNonKeyGenericParams > 0) {
1325+
// We have non-key generic parameters at this level, so the index needs to
1326+
// be checked more carefully.
1327+
auto genericParams = getLocalGenericParams(currentContext);
1328+
1329+
// Make sure that the requested parameter itself has a key argument.
1330+
if (!genericParams[index].hasKeyArgument())
1331+
return nullptr;
1332+
1333+
// Increase the flat index for each parameter with a key argument, up to
1334+
// the given index.
1335+
for (const auto &genericParam : genericParams.slice(0, index)) {
1336+
if (genericParam.hasKeyArgument())
1337+
++flatIndex;
1338+
}
1339+
} else {
1340+
flatIndex += index;
1341+
}
1342+
1343+
return base->getGenericArgs()[flatIndex];
1344+
}
1345+
12261346
#define OVERRIDE_METADATALOOKUP COMPATIBILITY_OVERRIDE
12271347
#include "CompatibilityOverride.def"

stdlib/public/runtime/Private.h

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,51 @@ class TypeInfo {
235235
using SubstGenericParameterFn =
236236
llvm::function_ref<const Metadata *(unsigned depth, unsigned index)>;
237237

238+
/// Function object that produces substitutions for the generic parameters
239+
/// that occur within a mangled name, using the generic arguments from
240+
/// the given metadata.
241+
///
242+
/// Use with \c _getTypeByMangledName to decode potentially-generic types.
243+
class SWIFT_RUNTIME_LIBRARY_VISIBILITY SubstGenericParametersFromMetadata {
244+
const Metadata *base;
245+
246+
/// An element in the descriptor path.
247+
struct PathElement {
248+
/// The context described by this path element.
249+
const ContextDescriptor *context;
250+
251+
/// The number of key parameters in the parent.
252+
unsigned numKeyGenericParamsInParent;
253+
254+
/// The number of key parameters locally introduced here.
255+
unsigned numKeyGenericParamsHere;
256+
257+
/// Whether this context has any non-key generic parameters.
258+
bool hasNonKeyGenericParams;
259+
};
260+
261+
/// Information about the generic context descriptors that make up \c
262+
/// descriptor, from the outermost to the innermost.
263+
mutable std::vector<PathElement> descriptorPath;
264+
265+
/// Builds the descriptor path.
266+
///
267+
/// \returns a pair containing the number of key generic parameters in
268+
/// the path up to this point.
269+
unsigned buildDescriptorPath(const ContextDescriptor *context) const;
270+
271+
// Set up the state we need to compute substitutions.
272+
void setup() const;
273+
274+
public:
275+
/// Produce substitutions entirely from the given metadata.
276+
explicit SubstGenericParametersFromMetadata(const Metadata *base)
277+
: base(base) { }
278+
279+
const Metadata *operator()(unsigned flatIndex) const;
280+
const Metadata *operator()(unsigned depth, unsigned index) const;
281+
};
282+
238283
/// Retrieve the type metadata described by the given type name.
239284
///
240285
/// \p substGenericParam Function that provides generic argument metadata

stdlib/public/runtime/ProtocolConformance.cpp

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -177,26 +177,11 @@ ProtocolConformanceDescriptor::getWitnessTable(const Metadata *type) const {
177177

178178
case ConformanceFlags::ConformanceKind::ConditionalWitnessTableAccessor: {
179179
// Check the conditional requirements.
180-
std::vector<unsigned> genericParamCounts;
181-
(void)_gatherGenericParameterCounts(type->getTypeContextDescriptor(),
182-
genericParamCounts);
183-
auto genericArgs = type->getGenericArgs();
180+
SubstGenericParametersFromMetadata substitutions(type);
184181
std::vector<const void *> conditionalArgs;
185182
bool failed =
186183
_checkGenericRequirements(getConditionalRequirements(), conditionalArgs,
187-
[&](unsigned flatIndex) {
188-
// FIXME: Adjust for non-key type parameters.
189-
return genericArgs[flatIndex];
190-
},
191-
[&](unsigned depth, unsigned index) -> const Metadata * {
192-
// FIXME: Adjust for non-key type parameters.
193-
if (auto flatIndex = _depthIndexToFlatIndex(depth, index,
194-
genericParamCounts)) {
195-
return genericArgs[*flatIndex];
196-
}
197-
198-
return nullptr;
199-
});
184+
substitutions, substitutions);
200185
if (failed) return nullptr;
201186

202187
return getWitnessTableAccessor()(

stdlib/public/runtime/ReflectionMirror.mm

Lines changed: 2 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -332,44 +332,10 @@ static bool _shouldReportMissingReflectionMetadataWarnings() {
332332
if (!field.hasMangledTypeName())
333333
return {name, FieldType().withIndirect(field.isIndirectCase())};
334334

335-
std::vector<const ContextDescriptor *> descriptorPath;
336-
{
337-
const auto *parent = reinterpret_cast<
338-
const ContextDescriptor *>(baseDesc);
339-
while (parent) {
340-
if (parent->isGeneric())
341-
descriptorPath.push_back(parent);
342-
343-
parent = parent->Parent.get();
344-
}
345-
}
346-
347335
auto typeName = field.getMangledTypeName(0);
348336

349-
auto typeInfo = _getTypeByMangledName(
350-
typeName,
351-
[&](unsigned depth, unsigned index) -> const Metadata * {
352-
if (depth >= descriptorPath.size())
353-
return nullptr;
354-
355-
unsigned currentDepth = 0;
356-
unsigned flatIndex = index;
357-
const ContextDescriptor *currentContext = descriptorPath.back();
358-
359-
for (const auto *context : llvm::reverse(descriptorPath)) {
360-
if (currentDepth >= depth)
361-
break;
362-
363-
flatIndex += context->getNumGenericParams();
364-
currentContext = context;
365-
++currentDepth;
366-
}
367-
368-
if (index >= currentContext->getNumGenericParams())
369-
return nullptr;
370-
371-
return base->getGenericArgs()[flatIndex];
372-
});
337+
SubstGenericParametersFromMetadata substitutions(base);
338+
auto typeInfo = _getTypeByMangledName(typeName, substitutions);
373339

374340
// Complete the type metadata before returning it to the caller.
375341
if (typeInfo) {

test/stdlib/Mirror.swift

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1795,4 +1795,41 @@ mirrors.test("SymbolicReferenceInsideType") {
17951795
expectEqual(expected, output)
17961796
}
17971797

1798+
protocol P1 { }
1799+
protocol P2 { }
1800+
protocol P3 { }
1801+
1802+
struct ConformsToP1: P1 { }
1803+
struct ConformsToP2: P2 { }
1804+
struct ConformsToP3: P3 { }
1805+
1806+
struct OuterTwoParams<T: P1, U: P2> {}
1807+
1808+
struct ConformsToP1AndP2 : P1, P2 { }
1809+
1810+
extension OuterTwoParams where U == T {
1811+
struct InnerEqualParams<V: P3> {
1812+
var x: T
1813+
var y: U
1814+
var z: V
1815+
}
1816+
}
1817+
1818+
mirrors.test("GenericNestedWithSameTypeConstraints") {
1819+
let value = OuterTwoParams.InnerEqualParams(x: ConformsToP1AndP2(),
1820+
y: ConformsToP1AndP2(),
1821+
z: ConformsToP3())
1822+
var output = ""
1823+
dump(value, to: &output)
1824+
1825+
// FIXME: The generic arguments are in the wrong place
1826+
let expected =
1827+
"▿ (extension in Mirror):Mirror.OuterTwoParams.InnerEqualParams<Mirror.ConformsToP1AndP2, Mirror.ConformsToP3>\n" +
1828+
" - x: Mirror.ConformsToP1AndP2\n" +
1829+
" - y: Mirror.ConformsToP1AndP2\n" +
1830+
" - z: Mirror.ConformsToP3\n"
1831+
1832+
expectEqual(expected, output)
1833+
}
1834+
17981835
runAllTests()

0 commit comments

Comments
 (0)