Skip to content

Commit 4544c2d

Browse files
committed
Recover more gracefully from stack exhaustion during template argument
deduction. Template argument deduction can trigger substitution, both with explicitly-specified template arguments and with deduced template arguments in various ways. We previously had no check for stack exhaustion along some of those codepaths, making it fairly easy to crash clang with a template resulting in a substitution that referred back to that same template. We should now produce a proper diagnostic for such cases rather than crashing.
1 parent 9b1e953 commit 4544c2d

File tree

2 files changed

+98
-38
lines changed

2 files changed

+98
-38
lines changed

clang/lib/Sema/SemaTemplateDeduction.cpp

Lines changed: 58 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3041,8 +3041,13 @@ Sema::DeduceTemplateArguments(ClassTemplatePartialSpecializationDecl *Partial,
30413041
if (Trap.hasErrorOccurred())
30423042
return Sema::TDK_SubstitutionFailure;
30433043

3044-
return ::FinishTemplateArgumentDeduction(
3045-
*this, Partial, /*IsPartialOrdering=*/false, TemplateArgs, Deduced, Info);
3044+
TemplateDeductionResult Result;
3045+
runWithSufficientStackSpace(Info.getLocation(), [&] {
3046+
Result = ::FinishTemplateArgumentDeduction(*this, Partial,
3047+
/*IsPartialOrdering=*/false,
3048+
TemplateArgs, Deduced, Info);
3049+
});
3050+
return Result;
30463051
}
30473052

30483053
/// Perform template argument deduction to determine whether
@@ -3082,8 +3087,13 @@ Sema::DeduceTemplateArguments(VarTemplatePartialSpecializationDecl *Partial,
30823087
if (Trap.hasErrorOccurred())
30833088
return Sema::TDK_SubstitutionFailure;
30843089

3085-
return ::FinishTemplateArgumentDeduction(
3086-
*this, Partial, /*IsPartialOrdering=*/false, TemplateArgs, Deduced, Info);
3090+
TemplateDeductionResult Result;
3091+
runWithSufficientStackSpace(Info.getLocation(), [&] {
3092+
Result = ::FinishTemplateArgumentDeduction(*this, Partial,
3093+
/*IsPartialOrdering=*/false,
3094+
TemplateArgs, Deduced, Info);
3095+
});
3096+
return Result;
30873097
}
30883098

30893099
/// Determine whether the given type T is a simple-template-id type.
@@ -4032,13 +4042,12 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
40324042
SmallVector<QualType, 8> ParamTypes;
40334043
unsigned NumExplicitlySpecified = 0;
40344044
if (ExplicitTemplateArgs) {
4035-
TemplateDeductionResult Result =
4036-
SubstituteExplicitTemplateArguments(FunctionTemplate,
4037-
*ExplicitTemplateArgs,
4038-
Deduced,
4039-
ParamTypes,
4040-
nullptr,
4041-
Info);
4045+
TemplateDeductionResult Result;
4046+
runWithSufficientStackSpace(Info.getLocation(), [&] {
4047+
Result = SubstituteExplicitTemplateArguments(
4048+
FunctionTemplate, *ExplicitTemplateArgs, Deduced, ParamTypes, nullptr,
4049+
Info);
4050+
});
40424051
if (Result)
40434052
return Result;
40444053

@@ -4140,12 +4149,16 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
41404149
// that is needed when the accessibility of template arguments is checked.
41414150
DeclContext *CallingCtx = CurContext;
41424151

4143-
return FinishTemplateArgumentDeduction(
4144-
FunctionTemplate, Deduced, NumExplicitlySpecified, Specialization, Info,
4145-
&OriginalCallArgs, PartialOverloading, [&, CallingCtx]() {
4146-
ContextRAII SavedContext(*this, CallingCtx);
4147-
return CheckNonDependent(ParamTypesForArgChecking);
4148-
});
4152+
TemplateDeductionResult Result;
4153+
runWithSufficientStackSpace(Info.getLocation(), [&] {
4154+
Result = FinishTemplateArgumentDeduction(
4155+
FunctionTemplate, Deduced, NumExplicitlySpecified, Specialization, Info,
4156+
&OriginalCallArgs, PartialOverloading, [&, CallingCtx]() {
4157+
ContextRAII SavedContext(*this, CallingCtx);
4158+
return CheckNonDependent(ParamTypesForArgChecking);
4159+
});
4160+
});
4161+
return Result;
41494162
}
41504163

41514164
QualType Sema::adjustCCAndNoReturn(QualType ArgFunctionType,
@@ -4231,11 +4244,13 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
42314244
unsigned NumExplicitlySpecified = 0;
42324245
SmallVector<QualType, 4> ParamTypes;
42334246
if (ExplicitTemplateArgs) {
4234-
if (TemplateDeductionResult Result
4235-
= SubstituteExplicitTemplateArguments(FunctionTemplate,
4236-
*ExplicitTemplateArgs,
4237-
Deduced, ParamTypes,
4238-
&FunctionType, Info))
4247+
TemplateDeductionResult Result;
4248+
runWithSufficientStackSpace(Info.getLocation(), [&] {
4249+
Result = SubstituteExplicitTemplateArguments(
4250+
FunctionTemplate, *ExplicitTemplateArgs, Deduced, ParamTypes,
4251+
&FunctionType, Info);
4252+
});
4253+
if (Result)
42394254
return Result;
42404255

42414256
NumExplicitlySpecified = Deduced.size();
@@ -4277,10 +4292,13 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
42774292
return Result;
42784293
}
42794294

4280-
if (TemplateDeductionResult Result
4281-
= FinishTemplateArgumentDeduction(FunctionTemplate, Deduced,
4282-
NumExplicitlySpecified,
4283-
Specialization, Info))
4295+
TemplateDeductionResult Result;
4296+
runWithSufficientStackSpace(Info.getLocation(), [&] {
4297+
Result = FinishTemplateArgumentDeduction(FunctionTemplate, Deduced,
4298+
NumExplicitlySpecified,
4299+
Specialization, Info);
4300+
});
4301+
if (Result)
42844302
return Result;
42854303

42864304
// If the function has a deduced return type, deduce it now, so we can check
@@ -4437,9 +4455,11 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *ConversionTemplate,
44374455
LocalInstantiationScope InstScope(*this);
44384456
// Finish template argument deduction.
44394457
FunctionDecl *ConversionSpecialized = nullptr;
4440-
TemplateDeductionResult Result
4441-
= FinishTemplateArgumentDeduction(ConversionTemplate, Deduced, 0,
4442-
ConversionSpecialized, Info);
4458+
TemplateDeductionResult Result;
4459+
runWithSufficientStackSpace(Info.getLocation(), [&] {
4460+
Result = FinishTemplateArgumentDeduction(ConversionTemplate, Deduced, 0,
4461+
ConversionSpecialized, Info);
4462+
});
44434463
Specialization = cast_or_null<CXXConversionDecl>(ConversionSpecialized);
44444464
return Result;
44454465
}
@@ -5379,14 +5399,15 @@ static bool isAtLeastAsSpecializedAs(Sema &S, QualType T1, QualType T2,
53795399
Sema::InstantiatingTemplate Inst(S, Info.getLocation(), P2, DeducedArgs,
53805400
Info);
53815401
auto *TST1 = T1->castAs<TemplateSpecializationType>();
5382-
if (FinishTemplateArgumentDeduction(
5383-
S, P2, /*IsPartialOrdering=*/true,
5384-
TemplateArgumentList(TemplateArgumentList::OnStack,
5385-
TST1->template_arguments()),
5386-
Deduced, Info))
5387-
return false;
5388-
5389-
return true;
5402+
bool AtLeastAsSpecialized;
5403+
S.runWithSufficientStackSpace(Info.getLocation(), [&] {
5404+
AtLeastAsSpecialized = !FinishTemplateArgumentDeduction(
5405+
S, P2, /*IsPartialOrdering=*/true,
5406+
TemplateArgumentList(TemplateArgumentList::OnStack,
5407+
TST1->template_arguments()),
5408+
Deduced, Info);
5409+
});
5410+
return AtLeastAsSpecialized;
53905411
}
53915412

53925413
/// Returns the more specialized class template partial specialization

clang/test/SemaTemplate/stack-exhaustion.cpp

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
// RUN: %clang_cc1 -verify %s
1+
// RUN: %clang_cc1 -verify %s -DTEST=1
2+
// RUN: %clang_cc1 -verify %s -DTEST=2
3+
// RUN: %clang_cc1 -verify %s -DTEST=3
24
// REQUIRES: thread_support
35

46
// FIXME: Detection of, or recovery from, stack exhaustion does not work on
@@ -9,6 +11,8 @@
911
// expected-warning@* 0-1{{stack nearly exhausted}}
1012
// expected-note@* 0+{{}}
1113

14+
#if TEST == 1
15+
1216
template<int N> struct X : X<N-1> {};
1317
template<> struct X<0> {};
1418
X<1000> x;
@@ -21,3 +25,38 @@ int f(X<0>);
2125
template<int N> auto f(X<N>) -> f(X<N-1>());
2226

2327
int k = f(X<1000>());
28+
29+
#elif TEST == 2
30+
31+
namespace template_argument_recursion {
32+
struct ostream;
33+
template<typename T> T &&declval();
34+
35+
namespace mlir {
36+
template<typename T, typename = decltype(declval<ostream&>() << declval<T&>())>
37+
ostream &operator<<(ostream& os, const T& obj); // expected-error {{exceeded maximum depth}}
38+
struct Value;
39+
}
40+
41+
void printFunctionalType(ostream &os, mlir::Value &v) { os << v; }
42+
}
43+
44+
#elif TEST == 3
45+
46+
namespace template_parameter_type_recursion {
47+
struct ostream;
48+
template<typename T> T &&declval();
49+
template<bool B, typename T> struct enable_if { using type = T; };
50+
51+
namespace mlir {
52+
template<typename T, typename enable_if<declval<ostream&>() << declval<T&>(), void*>::type = nullptr>
53+
ostream &operator<<(ostream& os, const T& obj); // expected-error {{exceeded maximum depth}}
54+
struct Value;
55+
}
56+
57+
void printFunctionalType(ostream &os, mlir::Value &v) { os << v; }
58+
}
59+
60+
#else
61+
#error unknown test
62+
#endif

0 commit comments

Comments
 (0)