Skip to content

Commit 722282e

Browse files
committed
ASTPrinter: fix nested inverse printing
Nested types with inverse requirements on generic parameters would sometimes print incorrectly. We only print the inverses on outer generic parameters for extensions. fixes rdar://123281976
1 parent d6e038f commit 722282e

File tree

4 files changed

+158
-2
lines changed

4 files changed

+158
-2
lines changed

lib/AST/ASTPrinter.cpp

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -963,6 +963,7 @@ class PrintAST : public ASTVisitor<PrintAST> {
963963
SwapSelfAndDependentMemberType = 8,
964964
PrintInherited = 16,
965965
PrintInverseRequirements = 32,
966+
IncludeOuterInverses = 64,
966967
};
967968

968969
void printInheritedFromRequirementSignature(ProtocolDecl *proto,
@@ -1668,6 +1669,17 @@ void PrintAST::printGenericSignature(
16681669
genericSig.getRequirements().end());
16691670
}
16701671

1672+
// Unless `IncludeOuterInverses` is enabled, limit inverses to the
1673+
// innermost generic parameters.
1674+
if (!(flags & IncludeOuterInverses) && !inverses.empty()) {
1675+
auto innerParams = genericSig.getInnermostGenericParams();
1676+
SmallPtrSet<TypeBase *, 4> innerParamSet(innerParams.begin(),
1677+
innerParams.end());
1678+
llvm::erase_if(inverses, [&](InverseRequirement inverse) -> bool {
1679+
return !innerParamSet.contains(inverse.subject.getPointer());
1680+
});
1681+
}
1682+
16711683
if (flags & InnermostOnly) {
16721684
auto genericParams = genericSig.getInnermostGenericParams();
16731685

@@ -2696,9 +2708,17 @@ void PrintAST::printDeclGenericRequirements(GenericContext *decl) {
26962708
if (parentSig && parentSig->isEqual(genericSig))
26972709
return;
26982710

2711+
unsigned flags = PrintRequirements | PrintInverseRequirements;
2712+
2713+
// In many cases, inverses should not be printed for outer generic parameters.
2714+
// Exceptions to that include extensions, as it's valid to write an inverse
2715+
// on the generic parameters they get from the extended nominal.
2716+
if (isa<ExtensionDecl>(decl))
2717+
flags |= IncludeOuterInverses;
2718+
26992719
Printer.printStructurePre(PrintStructureKind::DeclGenericParameterClause);
27002720
printGenericSignature(genericSig,
2701-
PrintRequirements | PrintInverseRequirements,
2721+
flags,
27022722
[parentSig](const Requirement &req) {
27032723
if (parentSig)
27042724
return !parentSig->isRequirementSatisfied(req);
@@ -2959,7 +2979,9 @@ void PrintAST::printExtension(ExtensionDecl *decl) {
29592979
assert(baseGenericSig &&
29602980
"an extension can't be generic if the base type isn't");
29612981
printGenericSignature(genericSig,
2962-
PrintRequirements | PrintInverseRequirements,
2982+
PrintRequirements
2983+
| PrintInverseRequirements
2984+
| IncludeOuterInverses,
29632985
[baseGenericSig](const Requirement &req) -> bool {
29642986
// Only include constraints that are not satisfied by the base type.
29652987
return !baseGenericSig->isRequirementSatisfied(req);
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// RUN: %target-swift-frontend \
2+
// RUN: -enable-experimental-feature NoncopyableGenerics \
3+
// RUN: -enable-experimental-feature NonescapableTypes \
4+
// RUN: -verify -typecheck %s -debug-generic-signatures \
5+
// RUN: -debug-inverse-requirements 2>&1 | %FileCheck %s --implicit-check-not "error:"
6+
7+
// CHECK-LABEL: .Outer@
8+
// CHECK: Generic signature: <A where A : Escapable>
9+
10+
// CHECK-LABEL: .Outer.innerFn@
11+
// CHECK: Generic signature: <A, B where A : Escapable, B : Escapable>
12+
13+
// CHECK-LABEL: .Outer.InnerStruct@
14+
// CHECK: Generic signature: <A, C where A : Escapable, C : Escapable>
15+
16+
// CHECK-LABEL: .Outer.InnerStruct.g@
17+
// CHECK: Generic signature: <A, C, D where A : Escapable, C : Escapable, D : Escapable>
18+
19+
// CHECK-LABEL: .Outer.InnerStruct.init()@
20+
// CHECK: Generic signature: <A, C where A : Escapable, C : Escapable>
21+
22+
// CHECK: (normal_conformance type="Outer<A>.InnerStruct<C>" protocol="Escapable")
23+
24+
// CHECK-LABEL: .Outer.InnerVariation1@
25+
// CHECK: Generic signature: <A, D where A : Escapable, D : Escapable>
26+
27+
// CHECK-LABEL: .Outer.InnerVariation2@
28+
// CHECK: Generic signature: <A, D where A : Escapable, D : Copyable>
29+
30+
// CHECK-LABEL: ExtensionDecl {{.*}} base=Outer.InnerStruct
31+
// CHECK: Generic signature: <A, C where A : Copyable, A : Escapable, C : Copyable, C : Escapable>
32+
33+
// CHECK-LABEL: .InnerStruct extension.hello@
34+
// CHECK: Generic signature: <A, C, T where A : Copyable, A : Escapable, C : Copyable, C : Escapable, T : Copyable>
35+
36+
// CHECK-LABEL: .Freestanding@
37+
// CHECK: Generic signature: <T>
38+
39+
// CHECK-LABEL: ExtensionDecl {{.*}} base=Outer
40+
// CHECK: Generic signature: <A where A : Copyable, A : Escapable>
41+
42+
// CHECK-LABEL: ExtensionDecl {{.*}} base=Outer.InnerVariation1
43+
// CHECK: Generic signature: <A, D where A : Escapable, D : Copyable, D : Escapable>
44+
45+
// CHECK-LABEL: ExtensionDecl {{.*}} base=Outer.InnerVariation2
46+
// CHECK: Generic signature: <A, D where A : Escapable, D : Copyable>
47+
48+
// CHECK-LABEL: ExtensionDecl line=0 base=Outer<A>.InnerStruct<C>
49+
// CHECK-NEXT: (normal_conformance type="Outer<A>.InnerStruct<C>" protocol="Copyable"
50+
// CHECK-NEXT: (requirement "A" conforms_to "Copyable")
51+
// CHECK-NEXT: (requirement "C" conforms_to "Copyable"))
52+
53+
// CHECK-LABEL: ExtensionDecl line=0 base=Outer<A>.InnerVariation1<D>
54+
// CHECK-NEXT: (normal_conformance type="Outer<A>.InnerVariation1<D>" protocol="Copyable"
55+
// CHECK-NEXT: (requirement "A" conforms_to "Copyable")
56+
// CHECK-NEXT: (requirement "D" conforms_to "Copyable"))
57+
58+
// CHECK-LABEL: ExtensionDecl line=0 base=Outer<A>.InnerVariation2<D>
59+
// CHECK-NEXT: (normal_conformance type="Outer<A>.InnerVariation2<D>" protocol="Escapable"
60+
// CHECK-NEXT: (requirement "D" conforms_to "Escapable"))
61+
62+
// CHECK-LABEL: ExtensionDecl line=0 base=Outer<A>
63+
// CHECK-NEXT: (normal_conformance type="Outer<A>" protocol="Copyable"
64+
// CHECK-NEXT: (requirement "A" conforms_to "Copyable"))
65+
66+
// CHECK-LABEL: ExtensionDecl line=0 base=Freestanding<T>
67+
// CHECK-NEXT: (normal_conformance type="Freestanding<T>" protocol="Copyable"
68+
// CHECK-NEXT: (requirement "T" conforms_to "Copyable"))
69+
70+
// CHECK-LABEL: ExtensionDecl line=0 base=Freestanding<T>
71+
// CHECK-NEXT: (normal_conformance type="Freestanding<T>" protocol="Escapable"
72+
// CHECK-NEXT: (requirement "T" conforms_to "Escapable"))
73+
74+
public struct Outer<A: ~Copyable> {
75+
public func innerFn<B: ~Copyable>(_ b: borrowing B) {}
76+
public struct InnerStruct<C: ~Copyable> {
77+
public func g<D>(_ d: borrowing D) where D: ~Copyable {}
78+
}
79+
public struct InnerVariation1<D: ~Copyable>: ~Escapable {}
80+
public struct InnerVariation2<D: ~Escapable>: ~Copyable {}
81+
}
82+
83+
extension Outer.InnerStruct {
84+
public func hello<T: ~Escapable>(_ t: T) {}
85+
}
86+
87+
public struct Freestanding<T: ~Copyable> where T: ~Escapable {}
88+
89+
extension Outer {}
90+
extension Outer.InnerVariation1 where A: ~Copyable {}
91+
extension Outer.InnerVariation2 where D: ~Escapable, A: ~Copyable {}

test/ModuleInterface/Inputs/NoncopyableGenerics_Misc.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,19 @@ public func noInversesEND() {}
8080
public func checkAnyInv1<Result>(_ t: borrowing Result) where Result: Any & ~Copyable {}
8181
public func checkAnyInv2<Result: Any>(_ t: borrowing Result) where Result: ~Copyable & ~Escapable {}
8282
public func checkAnyObject<Result>(_ t: Result) where Result: AnyObject {}
83+
84+
// coverage for rdar://123281976
85+
public struct Outer<A: ~Copyable> {
86+
public func innerFn<B: ~Copyable>(_ b: borrowing B) {}
87+
public struct InnerStruct<C: ~Copyable> {
88+
public func g<D>(_ d: borrowing D) where D: ~Copyable {}
89+
}
90+
public struct InnerVariation1<D: ~Copyable>: ~Escapable {}
91+
public struct InnerVariation2<D: ~Escapable>: ~Copyable {}
92+
}
93+
94+
extension Outer.InnerStruct {
95+
public func hello<T: ~Escapable>(_ t: T) {}
96+
}
97+
98+
public struct Freestanding<T: ~Copyable> where T: ~Escapable {}

test/ModuleInterface/noncopyable_generics.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,12 +104,39 @@ import NoncopyableGenerics_Misc
104104
// CHECK-MISC: public func checkAnyInv2<Result>(_ t: borrowing Result) where Result : ~Copyable, Result : ~Escapable
105105
// CHECK-MISC: public func checkAnyObject<Result>(_ t: Result) where Result : AnyObject
106106

107+
// CHECK-MISC: #if compiler(>=5.3) && $NoncopyableGenerics
108+
// CHECK-MISC-NEXT: public struct Outer<A> where A : ~Copyable {
109+
// CHECK-MISC-NEXT: public func innerFn<B>(_ b: borrowing B) where B : ~Copyable
110+
// CHECK-MISC: public struct InnerStruct<C> where C : ~Copyable {
111+
// CHECK-MISC-NEXT: public func g<D>(_ d: borrowing D) where D : ~Copyable
112+
// CHECK-MISC: public struct InnerVariation1<D> : ~Escapable where D : ~Copyable
113+
// CHECK-MISC: public struct InnerVariation2<D> : ~Copyable where D : ~Escapable
114+
115+
// CHECK-MISC: #if compiler(>=5.3) && $NoncopyableGenerics
116+
// CHECK-MISC-NEXT: extension {{.*}}.Outer.InnerStruct {
117+
// CHECK-MISC-NEXT: public func hello<T>(_ t: T) where T : ~Escapable
118+
119+
// CHECK-MISC: #if compiler(>=5.3) && $NoncopyableGenerics
120+
// CHECK-MISC-NEXT: public struct Freestanding<T> where T : ~Copyable, T : ~Escapable {
121+
107122
///////////////////////////////////////////////////////////////////////
108123
// Synthesized conditional conformances are next
109124

110125
// CHECK-MISC: #if compiler(>=5.3) && $NoncopyableGenerics
111126
// CHECK-MISC-NEXT: extension {{.*}}.Hello : Swift.Copyable where T : ~Escapable {
112127

128+
// FIXME: (rdar://123293620) inner struct extensions are not feature-guarded correctly
129+
// CHECK-MISC: extension {{.*}}.Outer.InnerStruct : Swift.Copyable {
130+
// CHECK-MISC: extension {{.*}}.Outer.InnerVariation1 : Swift.Copyable {
131+
// CHECK-MISC: extension {{.*}}.Outer.InnerVariation2 : Swift.Escapable where A : ~Copyable {
132+
// CHECK-MISC: extension {{.*}}.Outer : Swift.Copyable {
133+
134+
// CHECK-MISC: #if compiler(>=5.3) && $NoncopyableGenerics
135+
// CHECK-MISC-NEXT: extension {{.*}}.Freestanding : Swift.Copyable where T : ~Escapable {
136+
137+
// CHECK-MISC: #if compiler(>=5.3) && $NoncopyableGenerics
138+
// CHECK-MISC-NEXT: extension {{.*}}.Freestanding : Swift.Escapable where T : ~Copyable {
139+
113140
////////////////////////////////////////////////////////////////////////
114141
// At the end, ensure there are no synthesized Copyable extensions
115142

0 commit comments

Comments
 (0)