Skip to content

Commit 4b8068d

Browse files
committed
[runtime] Handle same-type constraints when resolving generic params
Generic parameters for a context are normally classified as "key", meaning they have actual metadata provided at runtime, or non-key, meaning they're derivable from somewhere else. However, a nested context or constrained extension can take what would be a "key" parameter in a parent context and make it non-key in a child context. This messes with the mapping between the (depth, index) representation of generic parameters and the flat list of generic arguments. Fix this by (1) consistently substituting out extension contexts with the contexts of the extended types, and (2) using the most nested context to decide which parameters are key, instead of the context a parameter was originally introduced in. Note that (1) may have problems if/when extensions start introducing their /own/ generic parameters. For now I tried to be consistent with what was there. rdar://problem/52364601
1 parent 284b674 commit 4b8068d

File tree

4 files changed

+210
-27
lines changed

4 files changed

+210
-27
lines changed

stdlib/public/runtime/MetadataLookup.cpp

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -246,11 +246,17 @@ _findContextDescriptor(Demangle::NodePointer node,
246246
Demangle::Demangler &Dem);
247247

248248
/// Find the context descriptor for the type extended by the given extension.
249+
///
250+
/// If \p maybeExtension isn't actually an extension context, returns nullptr.
249251
static const ContextDescriptor *
250-
_findExtendedTypeContextDescriptor(const ExtensionContextDescriptor *extension,
252+
_findExtendedTypeContextDescriptor(const ContextDescriptor *maybeExtension,
251253
Demangler &demangler,
252254
Demangle::NodePointer *demangledNode
253255
= nullptr) {
256+
auto extension = dyn_cast<ExtensionContextDescriptor>(maybeExtension);
257+
if (!extension)
258+
return nullptr;
259+
254260
Demangle::NodePointer localNode;
255261
Demangle::NodePointer &node = demangledNode ? *demangledNode : localNode;
256262

@@ -844,28 +850,26 @@ bool swift::_gatherGenericParameterCounts(
844850
DemanglerForRuntimeTypeResolution<> demangler;
845851
demangler.providePreallocatedMemory(BorrowFrom);
846852

847-
if (auto extension = dyn_cast<ExtensionContextDescriptor>(descriptor)) {
848-
// If we have an nominal type extension descriptor, extract the extended type
853+
if (auto extension = _findExtendedTypeContextDescriptor(descriptor,
854+
demangler)) {
855+
// If we have a nominal type extension descriptor, extract the extended type
849856
// and use that. If the extension is not nominal, then we can use the
850857
// extension's own signature.
851-
if (auto extendedType =
852-
_findExtendedTypeContextDescriptor(extension, demangler)) {
853-
descriptor = extendedType;
854-
}
858+
descriptor = extension;
855859
}
856860

857861
// Once we hit a non-generic descriptor, we're done.
858862
if (!descriptor->isGeneric()) return false;
859863

860864
// Recurse to record the parent context's generic parameters.
861-
if (auto parent = descriptor->Parent.get())
862-
(void)_gatherGenericParameterCounts(parent, genericParamCounts, demangler);
865+
auto parent = descriptor->Parent.get();
866+
(void)_gatherGenericParameterCounts(parent, genericParamCounts, demangler);
863867

864868
// Record a new level of generic parameters if the count exceeds the
865869
// previous count.
866-
auto myCount =
867-
descriptor->getGenericContext()->getGenericContextHeader().NumParams;
868-
if (genericParamCounts.empty() || myCount > genericParamCounts.back()) {
870+
unsigned parentCount = parent->getNumGenericParams();
871+
unsigned myCount = descriptor->getNumGenericParams();
872+
if (myCount > parentCount) {
869873
genericParamCounts.push_back(myCount);
870874
return true;
871875
}
@@ -1658,32 +1662,50 @@ static void installGetClassHook() {
16581662
#endif
16591663

16601664
unsigned SubstGenericParametersFromMetadata::
1661-
buildDescriptorPath(const ContextDescriptor *context) const {
1665+
buildDescriptorPath(const ContextDescriptor *context,
1666+
Demangler &borrowFrom) const {
1667+
assert(sourceIsMetadata);
1668+
16621669
// Terminating condition: we don't have a context.
16631670
if (!context)
16641671
return 0;
16651672

1673+
DemanglerForRuntimeTypeResolution<> demangler;
1674+
demangler.providePreallocatedMemory(borrowFrom);
1675+
1676+
if (auto extension = _findExtendedTypeContextDescriptor(context, demangler)) {
1677+
// If we have a nominal type extension descriptor, extract the extended type
1678+
// and use that. If the extension is not nominal, then we can use the
1679+
// extension's own signature.
1680+
context = extension;
1681+
}
1682+
16661683
// Add the parent's contribution to the descriptor path.
1667-
unsigned numKeyGenericParamsInParent =
1668-
buildDescriptorPath(context->Parent.get());
1684+
const ContextDescriptor *parent = context->Parent.get();
1685+
unsigned numKeyGenericParamsInParent = buildDescriptorPath(parent, demangler);
16691686

16701687
// If this context is non-generic, we're done.
16711688
if (!context->isGeneric())
16721689
return numKeyGenericParamsInParent;
16731690

16741691
// Count the number of key generic params at this level.
1692+
auto allGenericParams = baseContext->getGenericContext()->getGenericParams();
1693+
unsigned parentCount = parent->getNumGenericParams();
1694+
unsigned localCount = context->getNumGenericParams();
1695+
auto localGenericParams = allGenericParams.slice(parentCount,
1696+
localCount - parentCount);
1697+
16751698
unsigned numKeyGenericParamsHere = 0;
16761699
bool hasNonKeyGenericParams = false;
1677-
auto localGenericParams = getLocalGenericParams(context);
16781700
for (const auto &genericParam : localGenericParams) {
16791701
if (genericParam.hasKeyArgument())
16801702
++numKeyGenericParamsHere;
16811703
else
16821704
hasNonKeyGenericParams = true;
16831705
}
16841706

1685-
// Form the path element if there are any generic parameters to be found.
1686-
if (numKeyGenericParamsHere != 0)
1707+
// Form the path element if there are any new generic parameters.
1708+
if (localCount > parentCount)
16871709
descriptorPath.push_back(PathElement{localGenericParams,
16881710
context->getNumGenericParams(),
16891711
numKeyGenericParamsInParent,
@@ -1738,7 +1760,8 @@ void SubstGenericParametersFromMetadata::setup() const {
17381760
return;
17391761

17401762
if (sourceIsMetadata && baseContext) {
1741-
numKeyGenericParameters = buildDescriptorPath(baseContext);
1763+
DemanglerForRuntimeTypeResolution<StackAllocatedDemangler<2048>> demangler;
1764+
numKeyGenericParameters = buildDescriptorPath(baseContext, demangler);
17421765
return;
17431766
}
17441767

stdlib/public/runtime/Private.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,8 @@ class TypeInfo {
309309
///
310310
/// \returns a pair containing the number of key generic parameters in
311311
/// the path up to this point.
312-
unsigned buildDescriptorPath(const ContextDescriptor *context) const;
312+
unsigned buildDescriptorPath(const ContextDescriptor *context,
313+
Demangler &demangler) const;
313314

314315
/// Builds a path from the generic environment.
315316
unsigned buildEnvironmentPath(

test/IRGen/nested_generics.swift

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ protocol HasAssoc {
105105
associatedtype Assoc
106106
}
107107

108+
class SeparateGenericSuperclass<T> {}
109+
108110
enum Container<T : TagProtocol> {
109111
class _Superclass {}
110112
// CHECK-CONSTANTS-LABEL: @"$s15nested_generics9ContainerO9_SubclassCMn" =
@@ -127,27 +129,30 @@ enum Container<T : TagProtocol> {
127129
// CHECK-CONSTANTS-SAME: @"symbolic _____yx_G 15nested_generics9ContainerO11_SuperclassC"
128130
class _Subclass2<U: Collection>: _Superclass where T == U.Element {}
129131

130-
131132
// CHECK-CONSTANTS-LABEL: @"$s15nested_generics9ContainerO10_Subclass3CMn" =
132-
// FIXME: That "qd__" still causes problems: it's (depth: 1, index: 0), but
133-
// the runtime doesn't count the parameters at depth 0 correctly.
134133
// CHECK-CONSTANTS-SAME: @"symbolic _____y______qd__G 15nested_generics9ContainerO18_GenericSuperclassC AA5OuterO"
135134
class _GenericSuperclass<U> {}
136135
class _Subclass3<U>: _GenericSuperclass<U> where T == Outer {}
137136

137+
class MoreNesting {
138+
// CHECK-CONSTANTS-LABEL: @"$s15nested_generics9ContainerO11MoreNestingC9_SubclassCMn" =
139+
// CHECK-CONSTANTS-SAME: @"symbolic _____y______G 15nested_generics9ContainerO11_SuperclassC AA5OuterO"
140+
class _Subclass<U>: _Superclass where T == Outer {}
141+
}
142+
143+
// CHECK-CONSTANTS-LABEL: @"$s15nested_generics9ContainerO24_SeparateGenericSubclassCMn" =
144+
// CHECK-CONSTANTS-SAME: @"symbolic _____yxSgG 15nested_generics25SeparateGenericSuperclassC"
145+
class _SeparateGenericSubclass: SeparateGenericSuperclass<T?> {}
146+
138147
// CHECK-CONSTANTS-LABEL: @"$s15nested_generics9ContainerO6FieldsVMF" =
139148
// CHECK-CONSTANTS-SAME: @"symbolic _____ 15nested_generics5OuterO"
140-
// FIXME: This still causes problems: it's (depth: 1, index: 0), but
141-
// the runtime doesn't count the parameters at depth 0 correctly.
142149
// CHECK-CONSTANTS-SAME: @"symbolic qd__"
143150
struct Fields<U> where T == Outer {
144151
var x: T
145152
var y: U
146153
}
147154

148155
// CHECK-CONSTANTS-LABEL: @"$s15nested_generics9ContainerO5CasesOMF" =
149-
// FIXME: This still causes problems: it's (depth: 1, index: 0), but
150-
// the runtime doesn't count the parameters at depth 0 correctly.
151156
// CHECK-CONSTANTS-SAME: @"symbolic qd__"
152157
enum Cases<U> where T == Outer {
153158
case a(T)

test/stdlib/Mirror.swift

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1851,4 +1851,158 @@ mirrors.test("CustomAlignment") {
18511851
expectEqual(expected, output)
18521852
}
18531853

1854+
protocol STSTagProtocol {}
1855+
struct STSOuter : STSTagProtocol {}
1856+
1857+
enum STSContainer<T : STSTagProtocol> {
1858+
class Superclass {}
1859+
class Subclass<U>: Superclass where T == STSOuter {
1860+
class ExtraNested: Superclass {}
1861+
}
1862+
1863+
class GenericSuperclass<U> {}
1864+
class Subclass2<U>: GenericSuperclass<U> where T == STSOuter {}
1865+
1866+
class Subclass3<U: Collection>: Superclass where T == U.Element {}
1867+
1868+
class MoreNesting<X> {
1869+
class Subclass<U>: Superclass where T == STSOuter {}
1870+
}
1871+
1872+
struct Fields<U> where T == STSOuter {
1873+
var x: T?
1874+
var y: U?
1875+
}
1876+
1877+
enum Cases<U> where T == STSOuter {
1878+
case a(T)
1879+
case b(U)
1880+
}
1881+
}
1882+
1883+
// A new type with an easily-recognizable, easily-strippable suffix character.
1884+
enum STSContainer<T : STSTagProtocol> {
1885+
class Superclass {}
1886+
class GenericSuperclass<U> {}
1887+
}
1888+
extension STSContainerwhere T == STSOuter {
1889+
class Subclass<U>: Superclass {
1890+
class ExtraNested: Superclass {}
1891+
}
1892+
1893+
class Subclass2<U>: GenericSuperclass<U> {}
1894+
1895+
class MoreNesting<X> {
1896+
class Subclass<U>: Superclass {}
1897+
}
1898+
1899+
struct Fields<U> {
1900+
var x: T?
1901+
var y: U?
1902+
}
1903+
1904+
enum Cases<U> {
1905+
case a(T)
1906+
case b(U)
1907+
}
1908+
}
1909+
1910+
var sameTypeSuite = TestSuite("Mirrors.SameTypeConstraints")
1911+
1912+
func testSTSDump<RegularValue, ExtensionValue>(
1913+
_ regular: RegularValue,
1914+
_ ext: ExtensionValue,
1915+
_ expected: String
1916+
) {
1917+
var output = ""
1918+
dump(regular, to: &output)
1919+
expectEqual(expected, output)
1920+
1921+
func clean(_ dumpOutput: String) -> String {
1922+
// This isn't efficient but it doesn't have to be.
1923+
var result = dumpOutput
1924+
result.removeAll { $0 == "" }
1925+
if let openParenIndex = result.firstIndex(of: "(") {
1926+
if result[openParenIndex...].hasPrefix("(extension in Mirror):") {
1927+
let colonIndex = result[openParenIndex...].firstIndex(of: ":")!
1928+
result.removeSubrange(openParenIndex...colonIndex)
1929+
}
1930+
}
1931+
return result
1932+
}
1933+
1934+
var extensionOutput = ""
1935+
dump(ext, to: &extensionOutput)
1936+
expectEqual(expected, clean(extensionOutput))
1937+
}
1938+
1939+
func testSTSDump<RegularValue>(_ regular: RegularValue, _ expected: String) {
1940+
var output = ""
1941+
dump(regular, to: &output)
1942+
expectEqual(expected, output)
1943+
}
1944+
1945+
1946+
sameTypeSuite.test("Subclass") {
1947+
testSTSDump(STSContainer.Subclass<Int>(),
1948+
STSContainer.Subclass<Int>(),
1949+
"""
1950+
- Mirror.STSContainer<Mirror.STSOuter>.Subclass<Swift.Int> #0
1951+
- super: Mirror.STSContainer<Mirror.STSOuter>.Superclass\n
1952+
""")
1953+
1954+
testSTSDump(STSContainer.Subclass2<Int>(),
1955+
STSContainer.Subclass2<Int>(),
1956+
"""
1957+
- Mirror.STSContainer<Mirror.STSOuter>.Subclass2<Swift.Int> #0
1958+
- super: Mirror.STSContainer<Mirror.STSOuter>.GenericSuperclass<Swift.Int>\n
1959+
""")
1960+
1961+
testSTSDump(STSContainer.Subclass3<[STSOuter]>(),
1962+
"""
1963+
- Mirror.STSContainer<Mirror.STSOuter>.Subclass3<Swift.Array<Mirror.STSOuter>> #0
1964+
- super: Mirror.STSContainer<Mirror.STSOuter>.Superclass\n
1965+
""")
1966+
1967+
testSTSDump(STSContainer.Subclass<Int>.ExtraNested(),
1968+
STSContainer.Subclass<Int>.ExtraNested(),
1969+
"""
1970+
- Mirror.STSContainer<Mirror.STSOuter>.Subclass<Swift.Int>.ExtraNested #0
1971+
- super: Mirror.STSContainer<Mirror.STSOuter>.Superclass\n
1972+
""")
1973+
1974+
testSTSDump(STSContainer.MoreNesting<Bool>.Subclass<Int>(),
1975+
STSContainer.MoreNesting<Bool>.Subclass<Int>(),
1976+
"""
1977+
- Mirror.STSContainer<Mirror.STSOuter>.MoreNesting<Swift.Bool>.Subclass<Swift.Int> #0
1978+
- super: Mirror.STSContainer<Mirror.STSOuter>.Superclass\n
1979+
""")
1980+
}
1981+
1982+
sameTypeSuite.test("Fields") {
1983+
testSTSDump(STSContainer.Fields<Int>(),
1984+
STSContainer.Fields<Int>(),
1985+
"""
1986+
▿ Mirror.STSContainer<Mirror.STSOuter>.Fields<Swift.Int>
1987+
- x: nil
1988+
- y: nil\n
1989+
""")
1990+
}
1991+
1992+
sameTypeSuite.test("Cases") {
1993+
testSTSDump(STSContainer.Cases<Int>.a(.init()),
1994+
STSContainer.Cases<Int>.a(.init()),
1995+
"""
1996+
- Mirror.STSContainer<Mirror.STSOuter>.Cases<Swift.Int>.a\n
1997+
""")
1998+
1999+
testSTSDump(STSContainer.Cases<Int>.b(.init()),
2000+
STSContainer.Cases<Int>.b(.init()),
2001+
"""
2002+
▿ Mirror.STSContainer<Mirror.STSOuter>.Cases<Swift.Int>.b
2003+
- b: 0\n
2004+
""")
2005+
}
2006+
2007+
18542008
runAllTests()

0 commit comments

Comments
 (0)