Skip to content

Commit 3cd6c80

Browse files
committed
[cxx-interop] Improve performance of deep template instantiations.
If there are more than 10,000 instantiations of the same class template then we need to bail because it will take to long to mangle, instantiate, etc.
1 parent 7e32606 commit 3cd6c80

File tree

3 files changed

+58
-0
lines changed

3 files changed

+58
-0
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3725,6 +3725,14 @@ namespace {
37253725

37263726
Decl *VisitClassTemplateSpecializationDecl(
37273727
const clang::ClassTemplateSpecializationDecl *decl) {
3728+
// Before we go any further, check if we've already got tens of thousands
3729+
// of specializations. If so, it means we're likely instantiating a very
3730+
// deep/complex template, or we've run into an infinite loop. In either
3731+
// case, its not worth the compile time, so bail.
3732+
// TODO: this could be configurable at some point.
3733+
if (llvm::size(decl->getSpecializedTemplate()->specializations()) > 10000)
3734+
return nullptr;
3735+
37283736
// `Sema::isCompleteType` will try to instantiate the class template as a
37293737
// side-effect and we rely on this here. `decl->getDefinition()` can
37303738
// return nullptr before the call to sema and return its definition

test/Interop/Cxx/templates/Inputs/large-class-templates.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,47 @@ struct HasTypeWithSelfAsParam {
44
};
55

66
using WillBeInfinite = HasTypeWithSelfAsParam<int>;
7+
8+
namespace RegressionTest {
9+
10+
// This is a regression test to check that we don't instantiate exponentially
11+
// large templates. The below template will instantiate hundreds of thousands
12+
// (or more) of specializations before hitting the 8-template-param-deep limit.
13+
template<class E, int>
14+
struct SliceExpr {
15+
using type = typename E::type;
16+
E expr;
17+
18+
typename E::type* test() { return nullptr; }
19+
};
20+
21+
template<class T>
22+
struct Array {
23+
using type = T;
24+
};
25+
26+
template<class E>
27+
struct ValExpr {
28+
using type = typename E::type;
29+
E expr;
30+
31+
ValExpr<SliceExpr<E, 1>> test1() { return {SliceExpr<E, 1>{expr}}; }
32+
ValExpr<SliceExpr<E, 2>> test2() { return {SliceExpr<E, 2>{expr}}; }
33+
ValExpr<SliceExpr<E, 3>> test3() { return {SliceExpr<E, 3>{expr}}; }
34+
ValExpr<SliceExpr<E, 4>> test4() { return {SliceExpr<E, 4>{expr}}; }
35+
ValExpr<SliceExpr<E, 5>> test5() { return {SliceExpr<E, 5>{expr}}; }
36+
ValExpr<SliceExpr<E, 6>> test6() { return {SliceExpr<E, 6>{expr}}; }
37+
ValExpr<SliceExpr<E, 7>> test7() { return {SliceExpr<E, 7>{expr}}; }
38+
ValExpr<SliceExpr<E, 8>> test8() { return {SliceExpr<E, 8>{expr}}; }
39+
ValExpr<SliceExpr<E, 9>> test9() { return {SliceExpr<E, 8>{expr}}; }
40+
ValExpr<SliceExpr<E, 11>> test11() { return {SliceExpr<E, 11>{expr}}; }
41+
ValExpr<SliceExpr<E, 12>> test12() { return {SliceExpr<E, 12>{expr}}; }
42+
};
43+
44+
// This class template is exponentially slow to *fully* instantiate (and the
45+
// exponent is the number of testX methods). Make sure that we can still
46+
// partially import it without importing all of its child template
47+
// instantiations.
48+
void user(ValExpr<Array<int>> *) { }
49+
50+
} // end namespace 'RegressionTest'

test/Interop/Cxx/templates/large-class-templates-module-interface.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,9 @@
99
// CHECK: }
1010

1111
// CHECK: typealias WillBeInfinite = __CxxTemplateInst22HasTypeWithSelfAsParamIiE
12+
13+
// Make sure we fail to import super slow templates.
14+
// CHECK-NOT: __CxxTemplateInstN14RegressionTest7ValExprINS_9SliceExprINS_5ArrayIiEELi1EEEEE
15+
// TODO: we should not be importing functions that use this type in their
16+
// signature (such as the function below).
17+
// CHECK: mutating func test1() -> RegressionTest.__CxxTemplateInstN14RegressionTest7ValExprINS_9SliceExprINS_5ArrayIiEELi1EEEEE

0 commit comments

Comments
 (0)