-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[RemoteMirror] Fix lookup of generic type parameters #65332
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
89f98e9
02160e6
16c0729
ed28d8e
5350cd5
488fb82
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -567,65 +567,66 @@ class TypeRefBuilder { | |
return nullptr; | ||
} | ||
|
||
const BoundGenericTypeRef *createBoundGenericTypeReconstructingParent( | ||
const NodePointer node, const TypeRefDecl &decl, size_t shapeIndex, | ||
const llvm::ArrayRef<const TypeRef *> &args, size_t argsIndex) { | ||
if (!node || !node->hasChildren()) | ||
return nullptr; | ||
|
||
auto maybeGenericParamsPerLevel = decl.genericParamsPerLevel; | ||
if (!maybeGenericParamsPerLevel) | ||
return nullptr; | ||
|
||
auto genericParamsPerLevel = *maybeGenericParamsPerLevel; | ||
|
||
auto kind = node->getKind(); | ||
// Kinds who have a "BoundGeneric..." variant. | ||
if (kind != Node::Kind::Class && kind != Node::Kind::Structure && | ||
kind != Node::Kind::Enum) | ||
return nullptr; | ||
auto mangling = Demangle::mangleNode(node); | ||
if (!mangling.isSuccess()) | ||
return nullptr; | ||
|
||
if (shapeIndex >= genericParamsPerLevel.size()) | ||
return nullptr; | ||
|
||
auto numGenericArgs = genericParamsPerLevel[shapeIndex]; | ||
|
||
auto startOffsetFromEnd = argsIndex + numGenericArgs; | ||
auto endOffsetFromEnd = argsIndex; | ||
if (startOffsetFromEnd > args.size() || endOffsetFromEnd > args.size()) | ||
return nullptr; | ||
|
||
std::vector<const TypeRef *> genericParams( | ||
args.end() - startOffsetFromEnd, args.end() - endOffsetFromEnd); | ||
|
||
const BoundGenericTypeRef *parent = nullptr; | ||
if (node->hasChildren()) { | ||
// Skip over nodes that are not of type class, enum or struct | ||
auto parentNode = node->getFirstChild(); | ||
while (parentNode->getKind() != Node::Kind::Class && | ||
parentNode->getKind() != Node::Kind::Structure && | ||
parentNode->getKind() != Node::Kind::Enum) { | ||
if (parentNode->hasChildren()) { | ||
parentNode = parentNode->getFirstChild(); | ||
} else { | ||
parentNode = nullptr; | ||
break; | ||
} | ||
// Construct a bound generic type ref along with the parent type info | ||
// The parent list contains every parent type with at least 1 generic | ||
// type parameter. | ||
const BoundGenericTypeRef *reconstructParentsOfBoundGenericType( | ||
const NodePointer startNode, | ||
const std::vector<size_t> &genericParamsPerLevel, | ||
const llvm::ArrayRef<const TypeRef *> &args) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After fussing around with different structures here, I finally reduced this to only handle reconstructing the parent list. That means it can consistently ignore nodes with no direct generic params. I also reworked it to be iterative rather than recursive, though that's not particularly relevant -- I just found it a little easier to follow that way. |
||
{ | ||
// Collect the first N parents that potentially have generic args | ||
// (Ignore the last genericParamPerLevel, which | ||
// applies to the startNode itself.) | ||
std::vector<NodePointer> nodes; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fairly unimportant, but if you use a |
||
NodePointer node = startNode; | ||
while (nodes.size() < genericParamsPerLevel.size() - 1) { | ||
if (!node || !node->hasChildren()) { | ||
return nullptr; | ||
} | ||
if (parentNode) { | ||
if (shapeIndex > 0) | ||
parent = createBoundGenericTypeReconstructingParent( | ||
parentNode, decl, --shapeIndex, args, argsIndex + numGenericArgs); | ||
else | ||
return nullptr; | ||
node = node->getFirstChild(); | ||
switch (node->getKind()) { | ||
case Node::Kind::Class: | ||
case Node::Kind::Structure: | ||
case Node::Kind::Enum: | ||
nodes.push_back(node); | ||
break; | ||
default: | ||
break; | ||
} | ||
} | ||
assert(nodes.size() == genericParamsPerLevel.size() - 1); | ||
|
||
// We're now going to build the type tree from the | ||
// outermost parent in, which matches the order of | ||
// the generic parameter list and genericParamsPerLevel. | ||
std::reverse(nodes.begin(), nodes.end()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Above, we walk the parent list from the innermost to outermost. That is, for I then need to build the new list in the opposite order, which is why I reverse the list here. |
||
|
||
// Walk the list of parent types together with | ||
// the generic argument list... | ||
const BoundGenericTypeRef *typeref = nullptr; | ||
auto argBegin = args.begin(); | ||
for (size_t i = 0; i < nodes.size(); i++) { | ||
// Get the mangling for this node | ||
auto mangling = Demangle::mangleNode(nodes[i]); | ||
if (!mangling.isSuccess()) { | ||
return nullptr; | ||
} | ||
|
||
return BoundGenericTypeRef::create(*this, mangling.result(), genericParams, | ||
parent); | ||
// Use the next N params for this node | ||
auto numGenericArgs = genericParamsPerLevel[i]; | ||
// Skip nodes that don't have any actual type params. | ||
if (numGenericArgs == 0) { | ||
continue; | ||
} | ||
auto argEnd = argBegin + numGenericArgs; | ||
std::vector<const TypeRef *> params(argBegin, argEnd); | ||
argBegin = argEnd; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Walking the list in order makes it a lot easier to track which arguments go with with parent node. |
||
|
||
// Extend the typeref list towards the innermost type | ||
typeref = BoundGenericTypeRef::create(*this, mangling.result(), params, typeref); | ||
} | ||
return typeref; | ||
} | ||
|
||
const BoundGenericTypeRef * | ||
|
@@ -634,17 +635,58 @@ class TypeRefBuilder { | |
if (!builtTypeDecl) | ||
return nullptr; | ||
|
||
if (!builtTypeDecl->genericParamsPerLevel) | ||
// If there aren't generic params on the parent types, we just emit | ||
// a single BG typeref with all the generic args | ||
auto maybeGenericParamsPerLevel = builtTypeDecl->genericParamsPerLevel; | ||
if (!maybeGenericParamsPerLevel) { | ||
return BoundGenericTypeRef::create(*this, builtTypeDecl->mangledName, args, nullptr); | ||
} | ||
|
||
|
||
// Otherwise, work from a full demangle tree to produce a | ||
// typeref that includes information about parent generic args | ||
auto node = Dem.demangleType(builtTypeDecl->mangledName); | ||
if (!node || !node->hasChildren() || node->getKind() != Node::Kind::Type) | ||
if (!node || !node->hasChildren() || node->getKind() != Node::Kind::Type) { | ||
return nullptr; | ||
} | ||
auto startNode = node->getFirstChild(); | ||
auto mangling = Demangle::mangleNode(startNode); | ||
if (!mangling.isSuccess()) { | ||
return nullptr; | ||
} | ||
|
||
// Sanity: Verify that the generic params per level add | ||
// up exactly to the number of args we were provided, and | ||
// that we don't have a rediculous number of either one | ||
auto genericParamsPerLevel = *maybeGenericParamsPerLevel; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There were a lot of checks to ensure that the shape list (params-per-level) and the actual args list made sense together. I found it easier to pull that together into a single unified check: Ensure that the total number of params mentioned in params-per-level adds up exactly to the number of args we have. I also imposed an arbitrary limit of 1000 nested types and 1000 generic args in order to be comfortably certain that we'll never have problems from integer wrapping. |
||
if (genericParamsPerLevel.size() > 1000 || args.size() > 1000) { | ||
return nullptr; | ||
} | ||
size_t totalParams = 0; | ||
for (size_t i = 0; i < genericParamsPerLevel.size(); i++) { | ||
if (genericParamsPerLevel[i] > args.size()) { | ||
return nullptr; | ||
} | ||
totalParams += genericParamsPerLevel[i]; | ||
} | ||
if (totalParams != args.size()) { | ||
return nullptr; | ||
} | ||
|
||
auto type = node->getFirstChild(); | ||
return createBoundGenericTypeReconstructingParent( | ||
type, *builtTypeDecl, builtTypeDecl->genericParamsPerLevel->size() - 1, args, 0); | ||
// Reconstruct all parents that have non-zero generic params | ||
auto parents = reconstructParentsOfBoundGenericType(startNode, genericParamsPerLevel, args); | ||
|
||
// Collect the final set of generic params for the | ||
// innermost type. Note: This will sometimes be empty: | ||
// consider `Foo<Int, String>.Bar.Baz<Double>.Quux` | ||
// which has 2 parents in the parent list | ||
// (`Foo<Int,String>`, `Baz<Double>`), and the | ||
// startNode is `Quux` with no params. | ||
auto numGenericArgs = genericParamsPerLevel[genericParamsPerLevel.size() - 1]; | ||
auto argBegin = args.end() - numGenericArgs; | ||
std::vector<const TypeRef *> params(argBegin, args.end()); | ||
|
||
// Build and return the top typeref | ||
return BoundGenericTypeRef::create(*this, mangling.result(), params, parents); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The innermost node always gets included here, since that's the node we're actually constructing the typeref for. This happens even if the innermost node has no explicit generic type params. |
||
} | ||
|
||
const BoundGenericTypeRef * | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
// RUN: %empty-directory(%t) | ||
// RUN: %target-build-swift -lswiftSwiftReflectionTest %s -o %t/reflect_nested | ||
// RUN: %target-codesign %t/reflect_nested | ||
// RUN: %target-run %target-swift-reflection-test %t/reflect_nested 2>&1 | %FileCheck %s --check-prefix=CHECK-%target-ptrsize | ||
// RUN: %target-run %target-swift-reflection-test %t/reflect_nested 2>&1 | %FileCheck %s --check-prefix=CHECK | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since the CHECK-64 and CHECK-32 lines here are all identical, I just eliminated the target-ptrsize distinction. |
||
// REQUIRES: reflection_test_support | ||
// REQUIRES: executable_test | ||
// UNSUPPORTED: use_os_stdlib | ||
|
@@ -26,24 +26,14 @@ var obj = OuterGeneric.Inner.Innermost(x: 17, y: "hello") | |
|
||
reflect(object: obj) | ||
|
||
// CHECK-64: Reflecting an object. | ||
// CHECK-64: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} | ||
// CHECK-64: Type reference: | ||
// CHECK-64: (bound_generic_class reflect_nested.OuterGeneric.Inner.Innermost | ||
// CHECK-64-NEXT: (struct Swift.String) | ||
// CHECK-64-NEXT: (bound_generic_class reflect_nested.OuterGeneric.Inner | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The one actual change in this test: reflect_nested.OuterGeneric.Inner is not a bound_generic_class, so does not need to be in the list of generic parent types. |
||
// CHECK-64-NEXT: (bound_generic_class reflect_nested.OuterGeneric | ||
// CHECK-64-NEXT: (struct Swift.Int)))) | ||
// CHECK: Reflecting an object. | ||
// CHECK: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} | ||
// CHECK: Type reference: | ||
// CHECK: (bound_generic_class reflect_nested.OuterGeneric.Inner.Innermost | ||
// CHECK-NEXT: (struct Swift.String) | ||
// CHECK-NEXT: (bound_generic_class reflect_nested.OuterGeneric | ||
// CHECK-NEXT: (struct Swift.Int))) | ||
|
||
// CHECK-32: Reflecting an object. | ||
// CHECK-32: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} | ||
// CHECK-32: Type reference: | ||
// CHECK-32: (bound_generic_class reflect_nested.OuterGeneric.Inner.Innermost | ||
// CHECK-32-NEXT: (struct Swift.String) | ||
// CHECK-32-NEXT: (bound_generic_class reflect_nested.OuterGeneric.Inner | ||
// CHECK-32-NEXT: (bound_generic_class reflect_nested.OuterGeneric | ||
// CHECK-32-NEXT: (struct Swift.Int)))) | ||
|
||
class OuterNonGeneric { | ||
class InnerGeneric<T, U> { | ||
var x: T | ||
|
@@ -59,24 +49,13 @@ class OuterNonGeneric { | |
var obj2 = OuterNonGeneric.InnerGeneric(x: 17, y: "hello") | ||
reflect(object: obj2) | ||
|
||
// CHECK-64: Reflecting an object. | ||
// CHECK-64: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} | ||
// CHECK-64: Type reference: | ||
// CHECK-64: (bound_generic_class reflect_nested.OuterNonGeneric.InnerGeneric | ||
// CHECK-64-NEXT: (struct Swift.Int) | ||
// CHECK-64-NEXT: (struct Swift.String) | ||
// CHECK-64-NEXT: (bound_generic_class reflect_nested.OuterNonGeneric)) | ||
|
||
// CHECK-32: Reflecting an object. | ||
// CHECK-32: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} | ||
// CHECK-32: Type reference: | ||
// CHECK-32: (bound_generic_class reflect_nested.OuterNonGeneric.InnerGeneric | ||
// CHECK-32-NEXT: (struct Swift.Int) | ||
// CHECK-32-NEXT: (struct Swift.String) | ||
// CHECK-32-NEXT: (bound_generic_class reflect_nested.OuterNonGeneric)) | ||
// CHECK: Reflecting an object. | ||
// CHECK: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}} | ||
// CHECK: Type reference: | ||
// CHECK: (bound_generic_class reflect_nested.OuterNonGeneric.InnerGeneric | ||
// CHECK-NEXT: (struct Swift.Int) | ||
// CHECK-NEXT: (struct Swift.String)) | ||
|
||
doneReflecting() | ||
|
||
// CHECK-64: Done. | ||
|
||
// CHECK-32: Done. | ||
// CHECK: Done. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was the key bug introduced by PR #62854: Adding a
0
params marker here caused non-generic types to get counted as generic types, throwing off the generic type parameter matching.