Skip to content

Commit 6202dd6

Browse files
Merge pull request #36553 from zoecarver/cxx/fix-deep-template-spec
[cxx-interop] Improve performance of deep template instantiations.
2 parents a4db7c8 + 3cd6c80 commit 6202dd6

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)