Skip to content

Commit ce3f107

Browse files
authored
Merge pull request #37308 from hamishknight/extra-extensions-5.5
[5.5] [IDE] Fix superclass constraint handling for extension merging
2 parents 39e353b + 9d3a17c commit ce3f107

6 files changed

+921
-4
lines changed

lib/IDE/IDETypeChecking.cpp

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,8 @@ struct SynthesizedExtensionAnalyzer::Implementation {
306306
continue;
307307

308308
if (!BaseType->isExistentialType()) {
309+
// Apply any substitutions we need to map the requirements from a
310+
// a protocol extension to an extension on the conforming type.
309311
First = First.subst(subMap);
310312
Second = Second.subst(subMap);
311313

@@ -317,11 +319,12 @@ struct SynthesizedExtensionAnalyzer::Implementation {
317319
}
318320
}
319321

322+
assert(!First->hasArchetype() && !Second->hasArchetype());
320323
switch (Kind) {
321324
case RequirementKind::Conformance: {
322325
auto *M = DC->getParentModule();
323326
auto *Proto = Second->castTo<ProtocolType>()->getDecl();
324-
if (!First->isTypeParameter() && !First->is<ArchetypeType>() &&
327+
if (!First->isTypeParameter() &&
325328
M->conformsToProtocol(First, Proto).isInvalid())
326329
return true;
327330
if (M->conformsToProtocol(First, Proto).isInvalid())
@@ -330,11 +333,27 @@ struct SynthesizedExtensionAnalyzer::Implementation {
330333
}
331334

332335
case RequirementKind::Superclass:
333-
if (!Second->isBindableToSuperclassOf(First)) {
334-
return true;
335-
} else if (!Second->isExactSuperclassOf(Second)) {
336+
// If the subject type of the requirement is still a type parameter,
337+
// we need to check if the contextual type could possibly be bound to
338+
// the superclass. If not, this extension isn't applicable.
339+
if (First->isTypeParameter()) {
340+
if (!Target->mapTypeIntoContext(First)->isBindableTo(
341+
Target->mapTypeIntoContext(Second))) {
342+
return true;
343+
}
336344
MergeInfo.addRequirement(GenericSig, First, Second, Kind);
345+
break;
337346
}
347+
348+
// If we've substituted in a concrete type for the subject, we can
349+
// check for an exact superclass match, and disregard the extension if
350+
// it missed.
351+
// FIXME: What if it ends being something like `C<U> : C<Int>`?
352+
// Arguably we should allow that to be mirrored with a U == Int
353+
// constraint.
354+
if (!Second->isExactSuperclassOf(First))
355+
return true;
356+
338357
break;
339358

340359
case RequirementKind::SameType:

test/IDE/print_synthesized_extensions.swift

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
// RUN: %FileCheck %s -check-prefix=CHECK12 < %t.syn.txt
1616
// RUN: %FileCheck %s -check-prefix=CHECK13 < %t.syn.txt
1717
// RUN: %FileCheck %s -check-prefix=CHECK14 < %t.syn.txt
18+
// RUN: %FileCheck %s -check-prefix=CHECK15 < %t.syn.txt
19+
// RUN: %FileCheck %s -check-prefix=CHECK16 < %t.syn.txt
1820

1921
public protocol P1 {
2022
associatedtype T1
@@ -339,3 +341,60 @@ extension S13 : P5 {
339341
// CHECK14-NEXT: <decl:Func>/// This should not crash
340342
// CHECK14-NEXT: public func <loc>foo5()</loc></decl>
341343
// CHECK14-NEXT: }</synthesized>
344+
345+
// rdar://76868074: Make sure we print the extensions for C.
346+
public class C<T> {}
347+
public class D {}
348+
public class E {}
349+
350+
extension C where T : D {
351+
public func foo() {}
352+
}
353+
354+
public protocol P8 {
355+
associatedtype T
356+
}
357+
358+
extension P8 where T : D {
359+
public func bar() {}
360+
}
361+
362+
extension P8 where T : E {
363+
public func baz() {}
364+
}
365+
366+
extension C : P8 {}
367+
368+
// CHECK15: <decl:Class>public class <loc>C<<decl:GenericTypeParam>T</decl>></loc> {
369+
// CHECK15-NEXT: }</decl>
370+
371+
// CHECK15: <decl:Extension>extension <loc><ref:Class>C</ref></loc> : <ref:module>print_synthesized_extensions</ref>.<ref:Protocol>P8</ref> {
372+
// CHECK15-NEXT: }</decl>
373+
374+
// CHECK15: <decl:Extension>extension <loc><ref:Class>C</ref></loc> where <ref:GenericTypeParam>T</ref> : <ref:module>print_synthesized_extensions</ref>.<ref:Class>D</ref> {
375+
// CHECK15-NEXT: <decl:Func>public func <loc>foo()</loc></decl></decl>
376+
// CHECK15-NEXT: <decl:Func>public func <loc>bar()</loc></decl>
377+
// CHECK15-NEXT: }</synthesized>
378+
379+
// CHECK15: <synthesized>extension <ref:Class>C</ref> where <ref:GenericTypeParam>T</ref> : <ref:Class>E</ref> {
380+
// CHECK15-NEXT: <decl:Func>public func <loc>baz()</loc></decl>
381+
// CHECK15-NEXT: }</synthesized>
382+
383+
// CHECK15: <decl:Extension>extension <loc><ref:Protocol>P8</ref></loc> where <ref:GenericTypeParam>Self</ref>.<ref:AssociatedType>T</ref> : <ref:module>print_synthesized_extensions</ref>.<ref:Class>D</ref> {
384+
// CHECK15-NEXT: <decl:Func>public func <loc>bar()</loc></decl>
385+
// CHECK15-NEXT: }</decl>
386+
387+
// CHECK15: <decl:Extension>extension <loc><ref:Protocol>P8</ref></loc> where <ref:GenericTypeParam>Self</ref>.<ref:AssociatedType>T</ref> : <ref:module>print_synthesized_extensions</ref>.<ref:Class>E</ref> {
388+
// CHECK15-NEXT: <decl:Func>public func <loc>baz()</loc></decl>
389+
// CHECK15-NEXT: }</decl>
390+
391+
// For this class, make sure we mirror the extension P8 where T : D, but not
392+
// the extension P8 where T : E.
393+
public class F<T : D> {}
394+
extension F : P8 {}
395+
396+
// CHECK16: <synthesized>extension <ref:Class>F</ref> where <ref:GenericTypeParam>T</ref> : <ref:Class>D</ref> {
397+
// CHECK16-NEXT: <decl:Func>public func <loc>bar()</loc></decl>
398+
// CHECK16-NEXT: }</synthesized>
399+
400+
// CHECK16-NOT: <synthesized>extension <ref:Class>F</ref> where <ref:GenericTypeParam>T</ref> : <ref:Class>E</ref> {
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -emit-module-path %t/print_synthesized_extensions_generics.swiftmodule -emit-module-doc -emit-module-doc-path %t/print_synthesized_extensions_generics.swiftdoc %s
3+
// RUN: %target-swift-ide-test -print-module -synthesize-extension -print-interface -no-empty-line-between-members -module-to-print=print_synthesized_extensions_generics -I %t -source-filename=%s | %FileCheck %s
4+
5+
public protocol P1 {
6+
associatedtype T
7+
associatedtype U
8+
}
9+
10+
public class A {}
11+
public class B<T> : A {}
12+
13+
extension P1 where T : B<U> {
14+
public func qux() {}
15+
}
16+
extension P1 where T : B<Int> {
17+
public func flob() {}
18+
}
19+
20+
public class C<T : A, U> {}
21+
extension C : P1 {}
22+
// CHECK: extension C where T : B<U> {
23+
// CHECK-NEXT: func qux()
24+
// CHECK-NEXT: }
25+
26+
// CHECK: extension C where T : B<Int> {
27+
// CHECK-NEXT: func flob()
28+
// CHECK-NEXT: }
29+
30+
public class D<U> {}
31+
extension D : P1 {
32+
public typealias T = B<U>
33+
}
34+
// CHECK: class D<U> {
35+
// CHECK-NEXT: func qux()
36+
// CHECK-NEXT: }
37+
38+
// FIXME: Arguably we should support this
39+
// CHECK-NOT: extension D where U == Int
40+
41+
public class E {}
42+
extension E: P1 {
43+
public typealias T = B<U>
44+
public typealias U = Int
45+
}
46+
// CHECK: class E {
47+
// CHECK-NEXT: func qux()
48+
// CHECK-NEXT: func flob()
49+
// CHECK-NEXT: }
50+
51+
public class F {}
52+
extension F : P1 {
53+
public typealias T = B<U>
54+
public typealias U = String
55+
}
56+
// CHECK: class F {
57+
// CHECK-NEXT: func qux()
58+
// CHECK-NEXT: }
59+
60+
// CHECK-NOT: extension F where T : B<Int>
61+
62+
public protocol P2 {
63+
associatedtype T : P1
64+
}
65+
66+
extension P2 where T.T : A {
67+
public func blah() {}
68+
}
69+
70+
public class G<T : P1> {}
71+
extension G : P2 {}
72+
73+
// CHECK: extension G where T.T : A {
74+
// CHECK-NEXT: func blah()
75+
// CHECK-NEXT: }
76+
77+
// CHECK: extension P1 where Self.T : print_synthesized_extensions_generics.B<Self.U> {
78+
// CHECK-NEXT: func qux()
79+
// CHECK-NEXT: }
80+
81+
// CHECK: extension P1 where Self.T : print_synthesized_extensions_generics.B<Int> {
82+
// CHECK-NEXT: func flob()
83+
// CHECK-NEXT: }
84+
85+
// CHECK: extension P2 where Self.T.T : print_synthesized_extensions_generics.A {
86+
// CHECK-NEXT: func blah()
87+
// CHECK-NEXT: }
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
public class C<T> {}
3+
public class D {}
4+
public class E {}
5+
6+
extension C where T : D {
7+
public func foo() {}
8+
}
9+
10+
public protocol P8 {
11+
associatedtype T
12+
}
13+
14+
extension P8 where T : D {
15+
public func bar() {}
16+
}
17+
18+
extension P8 where T : E {
19+
public func baz() {}
20+
}
21+
22+
extension C : P8 {}
23+
24+
public class F<T : D> {}
25+
extension F : P8 {}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// RUN: %empty-directory(%t.mod)
2+
// RUN: %swift -emit-module -o %t.mod/module_with_class_extension.swiftmodule %S/Inputs/module_with_class_extension.swift -parse-as-library
3+
// RUN: %sourcekitd-test -req=doc-info -module module_with_class_extension -- -Xfrontend -disable-implicit-concurrency-module-import -I %t.mod > %t.response
4+
// RUN: %diff -u %s.response %t.response
5+
6+
// rdar://76868074: Make sure we print the extensions for C.

0 commit comments

Comments
 (0)