Skip to content

Commit f2e2f83

Browse files
committed
[AST] Make dumping a recursive conformance not a horrible mistake.
A recursive conformance would previously recur infinitely, but now prints just the header plus "(details printed above)". Fixes (most of) rdar://problem/40074968
1 parent 1bccf58 commit f2e2f83

File tree

2 files changed

+167
-35
lines changed

2 files changed

+167
-35
lines changed

lib/AST/ASTDumper.cpp

Lines changed: 76 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2792,55 +2792,70 @@ void TypeRepr::dump() const {
27922792
llvm::errs() << '\n';
27932793
}
27942794

2795-
void ProtocolConformanceRef::dump() const {
2796-
dump(llvm::errs());
2797-
}
2798-
2799-
void ProtocolConformanceRef::dump(llvm::raw_ostream &out,
2800-
unsigned indent) const {
2801-
if (isConcrete()) {
2802-
getConcrete()->dump(out, indent);
2795+
// Recursive helpers to avoid infinite recursion for recursive protocol
2796+
// conformances.
2797+
static void dumpProtocolConformanceRec(
2798+
const ProtocolConformance *conformance, llvm::raw_ostream &out,
2799+
unsigned indent,
2800+
llvm::SmallPtrSetImpl<const ProtocolConformance *> &visited);
2801+
2802+
static void dumpProtocolConformanceRefRec(
2803+
const ProtocolConformanceRef conformance, llvm::raw_ostream &out,
2804+
unsigned indent,
2805+
llvm::SmallPtrSetImpl<const ProtocolConformance *> &visited) {
2806+
if (conformance.isConcrete()) {
2807+
dumpProtocolConformanceRec(conformance.getConcrete(), out, indent, visited);
28032808
} else {
28042809
out.indent(indent) << "(abstract_conformance protocol="
2805-
<< getAbstract()->getName();
2810+
<< conformance.getAbstract()->getName();
28062811
PrintWithColorRAII(out, ParenthesisColor) << ')';
28072812
out << '\n';
28082813
}
28092814
}
28102815

2811-
void ProtocolConformance::dump() const {
2812-
auto &out = llvm::errs();
2813-
dump(out);
2814-
out << '\n';
2815-
}
2816+
static void dumpProtocolConformanceRec(
2817+
const ProtocolConformance *conformance, llvm::raw_ostream &out,
2818+
unsigned indent,
2819+
llvm::SmallPtrSetImpl<const ProtocolConformance *> &visited) {
2820+
// A recursive conformance shouldn't have its contents printed, or there's
2821+
// infinite recursion. (This also avoids printing things that occur multiple
2822+
// times in a conformance hierarchy.)
2823+
auto shouldPrintDetails = visited.insert(conformance).second;
28162824

2817-
void ProtocolConformance::dump(llvm::raw_ostream &out, unsigned indent) const {
28182825
auto printCommon = [&](StringRef kind) {
28192826
out.indent(indent);
28202827
PrintWithColorRAII(out, ParenthesisColor) << '(';
2821-
out << kind << "_conformance type=" << getType()
2822-
<< " protocol=" << getProtocol()->getName();
2828+
out << kind << "_conformance type=" << conformance->getType()
2829+
<< " protocol=" << conformance->getProtocol()->getName();
2830+
2831+
if (!shouldPrintDetails)
2832+
out << " (details printed above)";
28232833
};
28242834

2825-
switch (getKind()) {
2835+
switch (conformance->getKind()) {
28262836
case ProtocolConformanceKind::Normal: {
2827-
auto normal = cast<NormalProtocolConformance>(this);
2837+
auto normal = cast<NormalProtocolConformance>(conformance);
28282838

28292839
printCommon("normal");
2840+
if (!shouldPrintDetails)
2841+
break;
2842+
28302843
// Maybe print information about the conforming context?
28312844
if (normal->isLazilyLoaded()) {
28322845
out << " lazy";
28332846
} else {
2834-
forEachTypeWitness(nullptr, [&](const AssociatedTypeDecl *req,
2835-
Type ty, const TypeDecl *) -> bool {
2836-
out << '\n';
2837-
out.indent(indent + 2);
2838-
PrintWithColorRAII(out, ParenthesisColor) << '(';
2839-
out << "assoc_type req=" << req->getName() << " type=";
2840-
PrintWithColorRAII(out, TypeColor) << ty;
2841-
PrintWithColorRAII(out, ParenthesisColor) << ')';
2842-
return false;
2843-
});
2847+
normal->forEachTypeWitness(
2848+
nullptr,
2849+
[&](const AssociatedTypeDecl *req, Type ty,
2850+
const TypeDecl *) -> bool {
2851+
out << '\n';
2852+
out.indent(indent + 2);
2853+
PrintWithColorRAII(out, ParenthesisColor) << '(';
2854+
out << "assoc_type req=" << req->getName() << " type=";
2855+
PrintWithColorRAII(out, TypeColor) << ty;
2856+
PrintWithColorRAII(out, ParenthesisColor) << ')';
2857+
return false;
2858+
});
28442859
normal->forEachValueWitness(nullptr, [&](const ValueDecl *req,
28452860
Witness witness) {
28462861
out << '\n';
@@ -2857,9 +2872,9 @@ void ProtocolConformance::dump(llvm::raw_ostream &out, unsigned indent) const {
28572872
PrintWithColorRAII(out, ParenthesisColor) << ')';
28582873
});
28592874

2860-
for (auto conformance : normal->getSignatureConformances()) {
2875+
for (auto sigConf : normal->getSignatureConformances()) {
28612876
out << '\n';
2862-
conformance.dump(out, indent + 2);
2877+
dumpProtocolConformanceRefRec(sigConf, out, indent + 2, visited);
28632878
}
28642879
}
28652880

@@ -2872,31 +2887,57 @@ void ProtocolConformance::dump(llvm::raw_ostream &out, unsigned indent) const {
28722887
}
28732888

28742889
case ProtocolConformanceKind::Inherited: {
2875-
auto conf = cast<InheritedProtocolConformance>(this);
2890+
auto conf = cast<InheritedProtocolConformance>(conformance);
28762891
printCommon("inherited");
2892+
if (!shouldPrintDetails)
2893+
break;
2894+
28772895
out << '\n';
2878-
conf->getInheritedConformance()->dump(out, indent + 2);
2896+
dumpProtocolConformanceRec(conf->getInheritedConformance(), out, indent + 2,
2897+
visited);
28792898
break;
28802899
}
28812900

28822901
case ProtocolConformanceKind::Specialized: {
2883-
auto conf = cast<SpecializedProtocolConformance>(this);
2902+
auto conf = cast<SpecializedProtocolConformance>(conformance);
28842903
printCommon("specialized");
2904+
if (!shouldPrintDetails)
2905+
break;
2906+
28852907
out << '\n';
28862908
conf->getSubstitutionMap().dump(out);
28872909
for (auto subReq : conf->getConditionalRequirements()) {
28882910
out.indent(indent + 2);
28892911
subReq.dump(out);
28902912
out << '\n';
28912913
}
2892-
conf->getGenericConformance()->dump(out, indent + 2);
2914+
dumpProtocolConformanceRec(conf->getGenericConformance(), out, indent + 2,
2915+
visited);
28932916
break;
28942917
}
28952918
}
28962919

28972920
PrintWithColorRAII(out, ParenthesisColor) << ')';
28982921
}
28992922

2923+
void ProtocolConformanceRef::dump() const { dump(llvm::errs()); }
2924+
2925+
void ProtocolConformanceRef::dump(llvm::raw_ostream &out,
2926+
unsigned indent) const {
2927+
llvm::SmallPtrSet<const ProtocolConformance *, 8> visited;
2928+
dumpProtocolConformanceRefRec(*this, out, indent, visited);
2929+
}
2930+
void ProtocolConformance::dump() const {
2931+
auto &out = llvm::errs();
2932+
dump(out);
2933+
out << '\n';
2934+
}
2935+
2936+
void ProtocolConformance::dump(llvm::raw_ostream &out, unsigned indent) const {
2937+
llvm::SmallPtrSet<const ProtocolConformance *, 8> visited;
2938+
dumpProtocolConformanceRec(this, out, indent, visited);
2939+
}
2940+
29002941
//===----------------------------------------------------------------------===//
29012942
// Dumping for Types.
29022943
//===----------------------------------------------------------------------===//

test/Driver/debug-generic-signatures.swift

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,17 @@ protocol P1 {
1010
func f() -> A
1111
}
1212

13+
// Recursion, and where clauses.
14+
// CHECK: Generic signature: <Self where Self : P2>
15+
// CHECK-NEXT: Canonical generic signature: <τ_0_0 where τ_0_0 : P2>
16+
// CHECK-LABEL: main.(file).P2@
17+
// CHECK: Requirement signature: <Self where Self.A : P2, Self.B : P2, Self.A.A == Self.B.A>
18+
// CHECK-NEXT: Canonical requirement signature: <τ_0_0 where τ_0_0.A : P2, τ_0_0.B : P2, τ_0_0.A.A == τ_0_0.B.A>
19+
protocol P2 {
20+
associatedtype A: P2
21+
associatedtype B: P2 where Self.A.A == Self.B.A
22+
}
23+
1324
// CHECK-LABEL: StructDecl name=Basic
1425
// CHECK: (normal_conformance type=Basic protocol=P1
1526
// CHECK-NEXT: (assoc_type req=A type=Basic.A)
@@ -18,3 +29,83 @@ struct Basic: P1 {
1829
typealias A = Int
1930
func f() -> Int { fatalError() }
2031
}
32+
33+
// Recursive conformances should have finite output.
34+
35+
// CHECK-LABEL: StructDecl name=Recur
36+
// CHECK-NEXT: (normal_conformance type=Recur protocol=P2
37+
// CHECK-NEXT: (assoc_type req=A type=Recur.A)
38+
// CHECK-NEXT: (assoc_type req=B type=Recur.B)
39+
// CHECK-NEXT: (normal_conformance type=Recur protocol=P2 (details printed above))
40+
// CHECK-NEXT: (normal_conformance type=Recur protocol=P2 (details printed above)))
41+
struct Recur: P2 {
42+
typealias A = Recur
43+
typealias B = Recur
44+
}
45+
46+
// The full information about a conformance doesn't need to be printed twice.
47+
48+
// CHECK-LABEL: StructDecl name=NonRecur
49+
// CHECK-NEXT: (normal_conformance type=NonRecur protocol=P2
50+
// CHECK-NEXT: (assoc_type req=A type=NonRecur.A)
51+
// CHECK-NEXT: (assoc_type req=B type=NonRecur.B)
52+
// CHECK-NEXT: (normal_conformance type=Recur protocol=P2
53+
// CHECK-NEXT: (assoc_type req=A type=Recur.A)
54+
// CHECK-NEXT: (assoc_type req=B type=Recur.B)
55+
// CHECK-NEXT: (normal_conformance type=Recur protocol=P2 (details printed above))
56+
// CHECK-NEXT: (normal_conformance type=Recur protocol=P2 (details printed above)))
57+
// CHECK-NEXT: (normal_conformance type=Recur protocol=P2 (details printed above)))
58+
struct NonRecur: P2 {
59+
typealias A = Recur
60+
typealias B = Recur
61+
}
62+
63+
// Conditional conformance.
64+
65+
struct Generic<T> {}
66+
// CHECK-LABEL: ExtensionDecl line={{.*}} base=Generic<T>
67+
// CHECK-NEXT: (normal_conformance type=Generic<T> protocol=P1
68+
// CHECK-NEXT: (assoc_type req=A type=Generic<T>.A)
69+
// CHECK-NEXT: (value req=f() witness=main.(file).Generic.f()@{{.*}})
70+
// CHECK-NEXT: conforms_to: T P1)
71+
extension Generic: P1 where T: P1 {
72+
typealias A = T
73+
func f() -> T { fatalError() }
74+
}
75+
76+
77+
// Satisfying associated types with requirements with generic params
78+
class Super<T> {}
79+
// CHECK-LABEL: ExtensionDecl line={{.*}} base=Super<T>
80+
// CHECK-NEXT: (normal_conformance type=Super<T> protocol=P2
81+
// CHECK-NEXT: (assoc_type req=A type=Super<T>.A)
82+
// CHECK-NEXT: (assoc_type req=B type=Super<T>.B)
83+
// CHECK-NEXT: (abstract_conformance protocol=P2)
84+
// CHECK: (abstract_conformance protocol=P2)
85+
// CHECK: conforms_to: T P2)
86+
extension Super: P2 where T: P2 {
87+
typealias A = T
88+
typealias B = T
89+
}
90+
91+
// Inherited/specialized conformances.
92+
// CHECK-LABEL: ClassDecl name=Sub
93+
// CHECK-NEXT: (inherited_conformance type=Sub protocol=P2
94+
// CHECK-NEXT: (specialized_conformance type=Super<Recur> protocol=P2
95+
// CHECK-NEXT: Generic signature: <T where T : P2>
96+
// CHECK-NEXT: Substitutions:
97+
// CHECK-NEXT: T -> Recur
98+
// CHECK: Conformance map:
99+
// CHECK-NEXT: T -> (normal_conformance type=Recur protocol=P2
100+
// CHECK-NEXT: (assoc_type req=A type=Recur.A)
101+
// CHECK-NEXT: (assoc_type req=B type=Recur.B)
102+
// CHECK-NEXT: (normal_conformance type=Recur protocol=P2 (details printed above))
103+
// CHECK-NEXT: (normal_conformance type=Recur protocol=P2 (details printed above)))
104+
// CHECK-NEXT: conforms_to: Recur P2
105+
// CHECK-NEXT: (normal_conformance type=Super<T> protocol=P2
106+
// CHECK-NEXT: (assoc_type req=A type=Super<T>.A)
107+
// CHECK-NEXT: (assoc_type req=B type=Super<T>.B)
108+
// CHECK-NEXT: (abstract_conformance protocol=P2)
109+
// CHECK: (abstract_conformance protocol=P2)
110+
// CHECK: conforms_to: T P2)))
111+
class Sub: Super<Recur> {}

0 commit comments

Comments
 (0)