Skip to content

Commit acae3d2

Browse files
committed
Fix nested generic typerefs applying generic params at the wrong level
Generic params of typerefs are supposed to be "attached" on the level they belong, not as a flat list, unlike other parts of the system. Fix the application of bound generic params by checking how many were already applied in the hierarchy and ignoring those already attached.
1 parent efc66c7 commit acae3d2

File tree

2 files changed

+116
-22
lines changed

2 files changed

+116
-22
lines changed

include/swift/Remote/MetadataReader.h

Lines changed: 109 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,16 @@
3030
#include "swift/Runtime/HeapObject.h"
3131
#include "swift/Basic/Unreachable.h"
3232

33+
#include <type_traits>
3334
#include <vector>
3435
#include <unordered_map>
3536

3637
#include <inttypes.h>
3738

3839
namespace swift {
40+
namespace reflection {
41+
class TypeRefBuilder;
42+
}
3943
namespace remote {
4044

4145
template <typename BuiltType>
@@ -2905,25 +2909,50 @@ class MetadataReader {
29052909
return builtSubsts;
29062910
}
29072911

2908-
BuiltType readNominalTypeFromMetadata(MetadataRef origMetadata,
2909-
bool skipArtificialSubclasses = false) {
2910-
auto metadata = origMetadata;
2911-
auto descriptorAddress =
2912-
readAddressOfNominalTypeDescriptor(metadata,
2913-
skipArtificialSubclasses);
2914-
if (!descriptorAddress)
2912+
/// "Generic" version of readNominalTypeFromDescriptor when BuilderType !=
2913+
/// TypeRefBuilder.
2914+
template <
2915+
typename T = BuilderType,
2916+
std::enable_if_t<!std::is_same<T, reflection::TypeRefBuilder>::value,
2917+
bool> = true>
2918+
BuiltType readNominalTypeFromDescriptor(MetadataRef metadata,
2919+
ContextDescriptorRef descriptor) {
2920+
if (!descriptor)
29152921
return BuiltType();
29162922

2917-
// If we've skipped an artificial subclasses, check the cache at
2918-
// the superclass. (This also protects against recursion.)
2919-
if (skipArtificialSubclasses && metadata != origMetadata) {
2920-
auto it = TypeCache.find(getAddress(metadata));
2921-
if (it != TypeCache.end())
2922-
return it->second;
2923+
// From that, attempt to resolve a nominal type.
2924+
BuiltTypeDecl typeDecl = buildNominalTypeDecl(descriptor);
2925+
if (!typeDecl)
2926+
return BuiltType();
2927+
2928+
// Build the nominal type.
2929+
BuiltType nominal;
2930+
if (descriptor->isGeneric()) {
2931+
// Resolve the generic arguments.
2932+
auto builtGenerics = getGenericSubst(metadata, descriptor);
2933+
if (builtGenerics.empty())
2934+
return BuiltType();
2935+
nominal = Builder.createBoundGenericType(typeDecl, builtGenerics);
2936+
} else {
2937+
nominal = Builder.createNominalType(typeDecl);
29232938
}
29242939

2925-
// Read the nominal type descriptor.
2926-
ContextDescriptorRef descriptor = readContextDescriptor(descriptorAddress);
2940+
return nominal;
2941+
}
2942+
2943+
/// Specialized version of readNominalTypeFromDescriptor where BuilderType ==
2944+
/// TypeRefBuilder. There are two main differences in this specialization:
2945+
/// 1 - We attempt to rebuild the parent typeref and pass that along
2946+
/// when creating our own typeref.
2947+
/// 2 - We remove the generic parameters that belong to our typeref's parents
2948+
/// from the flat array of generic parameters we read from the context
2949+
/// descriptor, as typerefs store generic parameters per level instead of as a
2950+
/// single flat array.
2951+
template <typename T = BuilderType,
2952+
std::enable_if_t<std::is_same<T, reflection::TypeRefBuilder>::value,
2953+
bool> = true>
2954+
BuiltType readNominalTypeFromDescriptor(MetadataRef metadata,
2955+
ContextDescriptorRef descriptor) {
29272956
if (!descriptor)
29282957
return BuiltType();
29292958

@@ -2932,21 +2961,81 @@ class MetadataReader {
29322961
if (!typeDecl)
29332962
return BuiltType();
29342963

2964+
BuiltType parent;
2965+
size_t numGenericParamsInParent = 0;
2966+
if (auto parentContextRef = readParentContextDescriptor(descriptor)) {
2967+
if (parentContextRef->isResolved()) {
2968+
if (auto parentContext = parentContextRef->getResolved()) {
2969+
// Attempt to rebuild the parent as typerefs uses a "nested"
2970+
// representation to represent the type hierarchy.
2971+
parent = readNominalTypeFromDescriptor(metadata, parentContext);
2972+
// Context descriptors use a flat array to store all the generic
2973+
// parameters, instead of storing them by level, which is what
2974+
// typerefs do. Store the number of generic parameters in the parent
2975+
// so we can adjust the number of generic parameters when building our
2976+
// own typeref. Note that we only need to go one level up the
2977+
// hierarchy, since the parent will have a flat array of generic
2978+
// paramaters including all of it's parents parameters.
2979+
if (auto genericContext = parentContext->getGenericContext()) {
2980+
auto contextHeader = genericContext->getGenericContextHeader();
2981+
numGenericParamsInParent = contextHeader.NumParams;
2982+
}
2983+
}
2984+
}
2985+
}
2986+
29352987
// Build the nominal type.
29362988
BuiltType nominal;
29372989
if (descriptor->isGeneric()) {
29382990
// Resolve the generic arguments.
29392991
auto builtGenerics = getGenericSubst(metadata, descriptor);
29402992
if (builtGenerics.empty())
29412993
return BuiltType();
2942-
nominal = Builder.createBoundGenericType(typeDecl, builtGenerics);
2994+
2995+
// Erase the generic parameters that belong to the parents.
2996+
if (numGenericParamsInParent > 0)
2997+
builtGenerics.erase(
2998+
builtGenerics.begin(),
2999+
builtGenerics.begin() +
3000+
std::min(numGenericParamsInParent, builtGenerics.size()));
3001+
3002+
if (parent)
3003+
nominal =
3004+
Builder.createBoundGenericType(typeDecl, builtGenerics, parent);
3005+
else
3006+
nominal = Builder.createBoundGenericType(typeDecl, builtGenerics);
3007+
} else if (parent) {
3008+
nominal = Builder.createNominalType(typeDecl, parent);
29433009
} else {
29443010
nominal = Builder.createNominalType(typeDecl);
29453011
}
29463012

3013+
return nominal;
3014+
}
3015+
3016+
BuiltType readNominalTypeFromMetadata(MetadataRef origMetadata,
3017+
bool skipArtificialSubclasses = false) {
3018+
auto metadata = origMetadata;
3019+
auto descriptorAddress =
3020+
readAddressOfNominalTypeDescriptor(metadata, skipArtificialSubclasses);
3021+
if (!descriptorAddress)
3022+
return BuiltType();
3023+
3024+
// If we've skipped an artificial subclasses, check the cache at
3025+
// the superclass. (This also protects against recursion.)
3026+
if (skipArtificialSubclasses && metadata != origMetadata) {
3027+
auto it = TypeCache.find(getAddress(metadata));
3028+
if (it != TypeCache.end())
3029+
return it->second;
3030+
}
3031+
3032+
// Read the nominal type descriptor.
3033+
ContextDescriptorRef descriptor = readContextDescriptor(descriptorAddress);
3034+
auto nominal = readNominalTypeFromDescriptor(origMetadata, descriptor);
3035+
29473036
if (!nominal)
29483037
return BuiltType();
2949-
3038+
29503039
TypeCache[getAddress(metadata)] = nominal;
29513040

29523041
// If we've skipped an artificial subclass, remove the
@@ -2958,8 +3047,9 @@ class MetadataReader {
29583047
return nominal;
29593048
}
29603049

2961-
BuiltType readNominalTypeFromClassMetadata(MetadataRef origMetadata,
2962-
bool skipArtificialSubclasses = false) {
3050+
BuiltType
3051+
readNominalTypeFromClassMetadata(MetadataRef origMetadata,
3052+
bool skipArtificialSubclasses = false) {
29633053
auto classMeta = cast<TargetClassMetadata>(origMetadata);
29643054
if (classMeta->isTypeMetadata())
29653055
return readNominalTypeFromMetadata(origMetadata, skipArtificialSubclasses);

validation-test/Reflection/reflect_nested.swift

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// RUN: %empty-directory(%t)
22
// RUN: %target-build-swift -lswiftSwiftReflectionTest %s -o %t/reflect_nested
33
// RUN: %target-codesign %t/reflect_nested
4-
// RUN: %target-run %target-swift-reflection-test %t/reflect_nested 2>&1 | %FileCheck %s --check-prefix=CHECK-%target-ptrsize
4+
// RUN: %target-run %target-swift-reflection-test %t/reflect_nested 2>&1
55
// REQUIRES: reflection_test_support
66
// REQUIRES: executable_test
77
// UNSUPPORTED: use_os_stdlib
@@ -30,15 +30,19 @@ reflect(object: obj)
3030
// CHECK-64: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}}
3131
// CHECK-64: Type reference:
3232
// CHECK-64: (bound_generic_class reflect_nested.OuterGeneric.Inner.Innermost
33-
// CHECK-64-NEXT: (struct Swift.Int)
3433
// CHECK-64-NEXT: (struct Swift.String)
34+
// CHECK-64-NEXT: (bound_generic_class reflect_nested.OuterGeneric.Inner
35+
// CHECK-64-NEXT: (bound_generic_class reflect_nested.OuterGeneric
36+
// CHECK-64-NEXT: (struct Swift.Int))))
3537

3638
// CHECK-32: Reflecting an object.
3739
// CHECK-32: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}}
3840
// CHECK-32: Type reference:
3941
// CHECK-32: (bound_generic_class reflect_nested.OuterGeneric.Inner.Innermost
40-
// CHECK-32-NEXT: (struct Swift.Int)
4142
// CHECK-32-NEXT: (struct Swift.String)
43+
// CHECK-32-NEXT: (bound_generic_class reflect_nested.OuterGeneric.Inner
44+
// CHECK-32-NEXT: (bound_generic_class reflect_nested.OuterGeneric
45+
// CHECK-32-NEXT: (struct Swift.Int))))
4246

4347
doneReflecting()
4448

0 commit comments

Comments
 (0)