Skip to content

Commit 4109f87

Browse files
committed
[sil-generic-specializer] Avoid unlimited generic specialization of very deeply nested bound generic types
The generic specialized would get out of control in certain cases and would not stop generating specializations of generic functions until it runs out of memory after a while. rdar://21260480
1 parent 968f1b4 commit 4109f87

File tree

2 files changed

+70
-0
lines changed

2 files changed

+70
-0
lines changed

lib/SILOptimizer/Utils/Generics.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,29 @@
1919

2020
using namespace swift;
2121

22+
// Max depth of a bound generic which can be processed by the generic
23+
// specializer.
24+
// E.g. the depth of Array<Array<Array<T>>> is 3.
25+
// No specializations will be produced, if any of generic parameters contains
26+
// a bound generic type with the depth higher than this threshold
27+
static const unsigned BoundGenericDepthThreshold = 50;
28+
29+
static unsigned getBoundGenericDepth(Type t) {
30+
unsigned Depth = 0;
31+
if (auto BGT = t->getAs<BoundGenericType>()) {
32+
Depth++;
33+
auto GenericArgs = BGT->getGenericArgs();
34+
unsigned MaxGenericArgDepth = 0;
35+
for (auto GenericArg : GenericArgs) {
36+
auto ArgDepth = getBoundGenericDepth(GenericArg);
37+
if (ArgDepth > MaxGenericArgDepth)
38+
MaxGenericArgDepth = ArgDepth;
39+
}
40+
Depth += MaxGenericArgDepth;
41+
}
42+
return Depth;
43+
}
44+
2245
// =============================================================================
2346
// ReabstractionInfo
2447
// =============================================================================
@@ -50,6 +73,19 @@ ReabstractionInfo::ReabstractionInfo(SILFunction *OrigF,
5073
DEBUG(llvm::dbgs() << " Cannot specialize with dynamic self.\n");
5174
return;
5275
}
76+
77+
// Check if the substitution contains any generic types that are too deep.
78+
// If this is the case, bail to avoid the explosion in the number of
79+
// generated specializations.
80+
for (auto Sub : ParamSubs) {
81+
auto Replacement = Sub.getReplacement();
82+
if (Replacement.findIf([](Type ty) -> bool {
83+
return getBoundGenericDepth(ty) >= BoundGenericDepthThreshold;
84+
})) {
85+
return;
86+
}
87+
}
88+
5389
SILModule &M = OrigF->getModule();
5490
Module *SM = M.getSwiftModule();
5591

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// RUN: %target-swift-frontend -O -emit-sil %s | FileCheck %s
2+
3+
// Check that this Church Numerals inspired example does not hang
4+
// a compiler in the generic specializer.
5+
//
6+
// rdar://problem/21260480
7+
8+
protocol Nat {
9+
init()
10+
// static stored properties are not supported in generic structs.
11+
var val : Int32 {get}
12+
}
13+
14+
struct Zero : Nat { var val : Int32 = 0 }
15+
16+
struct PlusOne<X : Nat> : Nat {
17+
var val : Int32 = X().val + 1
18+
}
19+
20+
// Compiler used to keep performing the generic specialization of
21+
// computeNat for increasingly deeply nested bound generic types
22+
// like PlusOne<PlusOne<....<PlusOne<Zero>>>
23+
func computeNat<T: Nat>(_ v : Int32, _ t: T) -> Int32 {
24+
if v == 0 {
25+
return t.val
26+
}
27+
return computeNat(v - 1, PlusOne<T>())
28+
}
29+
30+
// CHECK-LABEL: sil @_TF24specialize_deep_generics14testComputeNatFT_Vs5Int32
31+
public func testComputeNat() -> Int32 {
32+
return computeNat(8, Zero())
33+
}
34+

0 commit comments

Comments
 (0)