Skip to content

Commit 18fbfd4

Browse files
committed
Serialization: Fix latent bug with extensions of nested generic types
ExtensionDecls for nested generic types have multiple generic parameter lists, one for each level of nested generic context. We only serialized the outermost list, though. This didn't cause any problems as far as I can see because most of the time we seem to use the GenericSignature instead, which has the correct generic parameters. However since we still have usages of getGenericParamsOfContext() on deserialized DeclContexts, better safe than sorry. I added a test; the test used to pass on master, but with the new assertion I added, it would fail without the other changes in this patch.
1 parent e95aceb commit 18fbfd4

File tree

4 files changed

+57
-4
lines changed

4 files changed

+57
-4
lines changed

lib/Serialization/Deserialization.cpp

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3428,9 +3428,12 @@ Decl *ModuleFile::getDecl(DeclID DID, Optional<DeclContext *> ForcedContext) {
34283428
extension->setEarlyAttrValidation();
34293429
declOrOffset = extension;
34303430

3431-
// Generic parameters.
3432-
GenericParamList *genericParams = maybeReadGenericParams(DC);
3433-
extension->setGenericParams(genericParams);
3431+
// Generic parameter lists are written from outermost to innermost.
3432+
// Keep reading until we run out of generic parameter lists.
3433+
GenericParamList *outerParams = nullptr;
3434+
while (auto *genericParams = maybeReadGenericParams(DC, outerParams))
3435+
outerParams = genericParams;
3436+
extension->setGenericParams(outerParams);
34343437

34353438
configureGenericEnvironment(extension, genericEnvID);
34363439

@@ -3458,6 +3461,19 @@ Decl *ModuleFile::getDecl(DeclID DID, Optional<DeclContext *> ForcedContext) {
34583461

34593462
nominal->addExtension(extension);
34603463

3464+
#ifndef NDEBUG
3465+
if (outerParams) {
3466+
unsigned paramCount = 0;
3467+
for (auto *paramList = outerParams;
3468+
paramList != nullptr;
3469+
paramList = paramList->getOuterParameters()) {
3470+
paramCount += paramList->size();
3471+
}
3472+
assert(paramCount ==
3473+
extension->getGenericSignature()->getGenericParams().size());
3474+
}
3475+
#endif
3476+
34613477
break;
34623478
}
34633479

lib/Serialization/Serialization.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2385,7 +2385,21 @@ void Serializer::writeDecl(const Decl *D) {
23852385
isa<ProtocolDecl>(baseNominal);
23862386
}
23872387

2388-
writeGenericParams(extension->getGenericParams());
2388+
// Extensions of nested generic types have multiple generic parameter
2389+
// lists. Collect them all, from the innermost to outermost.
2390+
SmallVector<GenericParamList *, 2> allGenericParams;
2391+
for (auto *genericParams = extension->getGenericParams();
2392+
genericParams != nullptr;
2393+
genericParams = genericParams->getOuterParameters()) {
2394+
allGenericParams.push_back(genericParams);
2395+
}
2396+
2397+
// Reverse the list, and write the parameter lists, from outermost
2398+
// to innermost.
2399+
std::reverse(allGenericParams.begin(), allGenericParams.end());
2400+
for (auto *genericParams : allGenericParams)
2401+
writeGenericParams(genericParams);
2402+
23892403
writeMembers(extension->getMembers(), isClassExtension);
23902404
writeConformances(conformances, DeclTypeAbbrCodes);
23912405
break;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
public struct Outer<T> {
2+
public struct Inner<U> {
3+
public init() {}
4+
}
5+
}
6+
7+
extension Outer.Inner {
8+
public func extensionMethod(t: T) -> U {
9+
while true {}
10+
}
11+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// RUN: rm -rf %t
2+
// RUN: mkdir -p %t
3+
// RUN: %target-swift-frontend -emit-module -o %t %S/Inputs/has_nested_generic_extension.swift
4+
// RUN: llvm-bcanalyzer %t/has_nested_generic_extension.swiftmodule | %FileCheck %s
5+
// RUN: %target-swift-frontend -emit-ir -I %t %s -o /dev/null
6+
7+
// CHECK-NOT: UnknownCode
8+
9+
import has_nested_generic_extension
10+
11+
var sillyGeneric = Outer<String>.Inner<Float>()
12+
let result: Float = sillyGeneric.extensionMethod(t: "square root of two")

0 commit comments

Comments
 (0)