Skip to content

Commit 3ea4877

Browse files
committed
[Runtime] Handle non-key arguments when substituting from metadata.
Reimplement SubstGenericParametersFromMetadata to cope with non-key generic parameters, which are counted when referring to generic parameters from metadata but do not have corresponding generic arguments.
1 parent 5a87435 commit 3ea4877

File tree

3 files changed

+167
-28
lines changed

3 files changed

+167
-28
lines changed

stdlib/public/runtime/MetadataLookup.cpp

Lines changed: 101 additions & 27 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,49 +1240,106 @@ 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+
12261281
const Metadata *
12271282
SubstGenericParametersFromMetadata::operator()(unsigned flatIndex) const {
1228-
// FIXME: Adjust for non-key arguments.
1229-
return base->getGenericArgs()[flatIndex];
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);
12301302
}
12311303

12321304
const Metadata *
12331305
SubstGenericParametersFromMetadata::operator()(
12341306
unsigned depth, unsigned index) const {
12351307
// On first access, compute the descriptor path.
1236-
if (descriptorPath.empty()) {
1237-
if (auto *baseDesc = base->getTypeContextDescriptor()) {
1238-
const auto *parent = reinterpret_cast<
1239-
const ContextDescriptor *>(baseDesc);
1240-
while (parent) {
1241-
if (parent->isGeneric())
1242-
descriptorPath.push_back(parent);
1243-
1244-
parent = parent->Parent.get();
1245-
}
1246-
}
1247-
}
1308+
setup();
12481309

1310+
// If the depth is too great, there is nothing to do.
12491311
if (depth >= descriptorPath.size())
12501312
return nullptr;
12511313

1252-
unsigned currentDepth = 0;
1253-
unsigned flatIndex = index;
1254-
const ContextDescriptor *currentContext = descriptorPath.back();
1255-
1256-
for (const auto *context : llvm::reverse(descriptorPath)) {
1257-
if (currentDepth >= depth)
1258-
break;
1259-
1260-
flatIndex += context->getNumGenericParams();
1261-
currentContext = context;
1262-
++currentDepth;
1263-
}
1314+
/// Retrieve the descriptor path element at this depth.
1315+
auto &pathElement = descriptorPath[depth];
1316+
auto currentContext = pathElement.context;
12641317

1318+
// Check whether the index is clearly out of bounds.
12651319
if (index >= currentContext->getNumGenericParams())
12661320
return nullptr;
12671321

1268-
// FIXME: Adjust for non-key arguments.
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+
12691343
return base->getGenericArgs()[flatIndex];
12701344
}
12711345

stdlib/public/runtime/Private.h

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,9 +242,37 @@ class TypeInfo {
242242
/// Use with \c _getTypeByMangledName to decode potentially-generic types.
243243
class SWIFT_RUNTIME_LIBRARY_VISIBILITY SubstGenericParametersFromMetadata {
244244
const Metadata *base;
245-
mutable std::vector<const ContextDescriptor *> descriptorPath;
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;
246273

247274
public:
275+
/// Produce substitutions entirely from the given metadata.
248276
explicit SubstGenericParametersFromMetadata(const Metadata *base)
249277
: base(base) { }
250278

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)