forked from OSchip/llvm-project
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.
This commit is contained in:
parent
9b1e95329a
commit
4544c2d95a
|
@ -3041,8 +3041,13 @@ Sema::DeduceTemplateArguments(ClassTemplatePartialSpecializationDecl *Partial,
|
|||
if (Trap.hasErrorOccurred())
|
||||
return Sema::TDK_SubstitutionFailure;
|
||||
|
||||
return ::FinishTemplateArgumentDeduction(
|
||||
*this, Partial, /*IsPartialOrdering=*/false, TemplateArgs, Deduced, Info);
|
||||
TemplateDeductionResult Result;
|
||||
runWithSufficientStackSpace(Info.getLocation(), [&] {
|
||||
Result = ::FinishTemplateArgumentDeduction(*this, Partial,
|
||||
/*IsPartialOrdering=*/false,
|
||||
TemplateArgs, Deduced, Info);
|
||||
});
|
||||
return Result;
|
||||
}
|
||||
|
||||
/// Perform template argument deduction to determine whether
|
||||
|
@ -3082,8 +3087,13 @@ Sema::DeduceTemplateArguments(VarTemplatePartialSpecializationDecl *Partial,
|
|||
if (Trap.hasErrorOccurred())
|
||||
return Sema::TDK_SubstitutionFailure;
|
||||
|
||||
return ::FinishTemplateArgumentDeduction(
|
||||
*this, Partial, /*IsPartialOrdering=*/false, TemplateArgs, Deduced, Info);
|
||||
TemplateDeductionResult Result;
|
||||
runWithSufficientStackSpace(Info.getLocation(), [&] {
|
||||
Result = ::FinishTemplateArgumentDeduction(*this, Partial,
|
||||
/*IsPartialOrdering=*/false,
|
||||
TemplateArgs, Deduced, Info);
|
||||
});
|
||||
return Result;
|
||||
}
|
||||
|
||||
/// Determine whether the given type T is a simple-template-id type.
|
||||
|
@ -4032,13 +4042,12 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
|
|||
SmallVector<QualType, 8> ParamTypes;
|
||||
unsigned NumExplicitlySpecified = 0;
|
||||
if (ExplicitTemplateArgs) {
|
||||
TemplateDeductionResult Result =
|
||||
SubstituteExplicitTemplateArguments(FunctionTemplate,
|
||||
*ExplicitTemplateArgs,
|
||||
Deduced,
|
||||
ParamTypes,
|
||||
nullptr,
|
||||
Info);
|
||||
TemplateDeductionResult Result;
|
||||
runWithSufficientStackSpace(Info.getLocation(), [&] {
|
||||
Result = SubstituteExplicitTemplateArguments(
|
||||
FunctionTemplate, *ExplicitTemplateArgs, Deduced, ParamTypes, nullptr,
|
||||
Info);
|
||||
});
|
||||
if (Result)
|
||||
return Result;
|
||||
|
||||
|
@ -4140,12 +4149,16 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
|
|||
// that is needed when the accessibility of template arguments is checked.
|
||||
DeclContext *CallingCtx = CurContext;
|
||||
|
||||
return FinishTemplateArgumentDeduction(
|
||||
FunctionTemplate, Deduced, NumExplicitlySpecified, Specialization, Info,
|
||||
&OriginalCallArgs, PartialOverloading, [&, CallingCtx]() {
|
||||
ContextRAII SavedContext(*this, CallingCtx);
|
||||
return CheckNonDependent(ParamTypesForArgChecking);
|
||||
});
|
||||
TemplateDeductionResult Result;
|
||||
runWithSufficientStackSpace(Info.getLocation(), [&] {
|
||||
Result = FinishTemplateArgumentDeduction(
|
||||
FunctionTemplate, Deduced, NumExplicitlySpecified, Specialization, Info,
|
||||
&OriginalCallArgs, PartialOverloading, [&, CallingCtx]() {
|
||||
ContextRAII SavedContext(*this, CallingCtx);
|
||||
return CheckNonDependent(ParamTypesForArgChecking);
|
||||
});
|
||||
});
|
||||
return Result;
|
||||
}
|
||||
|
||||
QualType Sema::adjustCCAndNoReturn(QualType ArgFunctionType,
|
||||
|
@ -4231,11 +4244,13 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
|
|||
unsigned NumExplicitlySpecified = 0;
|
||||
SmallVector<QualType, 4> ParamTypes;
|
||||
if (ExplicitTemplateArgs) {
|
||||
if (TemplateDeductionResult Result
|
||||
= SubstituteExplicitTemplateArguments(FunctionTemplate,
|
||||
*ExplicitTemplateArgs,
|
||||
Deduced, ParamTypes,
|
||||
&FunctionType, Info))
|
||||
TemplateDeductionResult Result;
|
||||
runWithSufficientStackSpace(Info.getLocation(), [&] {
|
||||
Result = SubstituteExplicitTemplateArguments(
|
||||
FunctionTemplate, *ExplicitTemplateArgs, Deduced, ParamTypes,
|
||||
&FunctionType, Info);
|
||||
});
|
||||
if (Result)
|
||||
return Result;
|
||||
|
||||
NumExplicitlySpecified = Deduced.size();
|
||||
|
@ -4277,10 +4292,13 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
|
|||
return Result;
|
||||
}
|
||||
|
||||
if (TemplateDeductionResult Result
|
||||
= FinishTemplateArgumentDeduction(FunctionTemplate, Deduced,
|
||||
NumExplicitlySpecified,
|
||||
Specialization, Info))
|
||||
TemplateDeductionResult Result;
|
||||
runWithSufficientStackSpace(Info.getLocation(), [&] {
|
||||
Result = FinishTemplateArgumentDeduction(FunctionTemplate, Deduced,
|
||||
NumExplicitlySpecified,
|
||||
Specialization, Info);
|
||||
});
|
||||
if (Result)
|
||||
return Result;
|
||||
|
||||
// If the function has a deduced return type, deduce it now, so we can check
|
||||
|
@ -4437,9 +4455,11 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *ConversionTemplate,
|
|||
LocalInstantiationScope InstScope(*this);
|
||||
// Finish template argument deduction.
|
||||
FunctionDecl *ConversionSpecialized = nullptr;
|
||||
TemplateDeductionResult Result
|
||||
= FinishTemplateArgumentDeduction(ConversionTemplate, Deduced, 0,
|
||||
ConversionSpecialized, Info);
|
||||
TemplateDeductionResult Result;
|
||||
runWithSufficientStackSpace(Info.getLocation(), [&] {
|
||||
Result = FinishTemplateArgumentDeduction(ConversionTemplate, Deduced, 0,
|
||||
ConversionSpecialized, Info);
|
||||
});
|
||||
Specialization = cast_or_null<CXXConversionDecl>(ConversionSpecialized);
|
||||
return Result;
|
||||
}
|
||||
|
@ -5379,14 +5399,15 @@ static bool isAtLeastAsSpecializedAs(Sema &S, QualType T1, QualType T2,
|
|||
Sema::InstantiatingTemplate Inst(S, Info.getLocation(), P2, DeducedArgs,
|
||||
Info);
|
||||
auto *TST1 = T1->castAs<TemplateSpecializationType>();
|
||||
if (FinishTemplateArgumentDeduction(
|
||||
S, P2, /*IsPartialOrdering=*/true,
|
||||
TemplateArgumentList(TemplateArgumentList::OnStack,
|
||||
TST1->template_arguments()),
|
||||
Deduced, Info))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
bool AtLeastAsSpecialized;
|
||||
S.runWithSufficientStackSpace(Info.getLocation(), [&] {
|
||||
AtLeastAsSpecialized = !FinishTemplateArgumentDeduction(
|
||||
S, P2, /*IsPartialOrdering=*/true,
|
||||
TemplateArgumentList(TemplateArgumentList::OnStack,
|
||||
TST1->template_arguments()),
|
||||
Deduced, Info);
|
||||
});
|
||||
return AtLeastAsSpecialized;
|
||||
}
|
||||
|
||||
/// Returns the more specialized class template partial specialization
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
// RUN: %clang_cc1 -verify %s
|
||||
// RUN: %clang_cc1 -verify %s -DTEST=1
|
||||
// RUN: %clang_cc1 -verify %s -DTEST=2
|
||||
// RUN: %clang_cc1 -verify %s -DTEST=3
|
||||
// REQUIRES: thread_support
|
||||
|
||||
// FIXME: Detection of, or recovery from, stack exhaustion does not work on
|
||||
|
@ -9,6 +11,8 @@
|
|||
// expected-warning@* 0-1{{stack nearly exhausted}}
|
||||
// expected-note@* 0+{{}}
|
||||
|
||||
#if TEST == 1
|
||||
|
||||
template<int N> struct X : X<N-1> {};
|
||||
template<> struct X<0> {};
|
||||
X<1000> x;
|
||||
|
@ -21,3 +25,38 @@ int f(X<0>);
|
|||
template<int N> auto f(X<N>) -> f(X<N-1>());
|
||||
|
||||
int k = f(X<1000>());
|
||||
|
||||
#elif TEST == 2
|
||||
|
||||
namespace template_argument_recursion {
|
||||
struct ostream;
|
||||
template<typename T> T &&declval();
|
||||
|
||||
namespace mlir {
|
||||
template<typename T, typename = decltype(declval<ostream&>() << declval<T&>())>
|
||||
ostream &operator<<(ostream& os, const T& obj); // expected-error {{exceeded maximum depth}}
|
||||
struct Value;
|
||||
}
|
||||
|
||||
void printFunctionalType(ostream &os, mlir::Value &v) { os << v; }
|
||||
}
|
||||
|
||||
#elif TEST == 3
|
||||
|
||||
namespace template_parameter_type_recursion {
|
||||
struct ostream;
|
||||
template<typename T> T &&declval();
|
||||
template<bool B, typename T> struct enable_if { using type = T; };
|
||||
|
||||
namespace mlir {
|
||||
template<typename T, typename enable_if<declval<ostream&>() << declval<T&>(), void*>::type = nullptr>
|
||||
ostream &operator<<(ostream& os, const T& obj); // expected-error {{exceeded maximum depth}}
|
||||
struct Value;
|
||||
}
|
||||
|
||||
void printFunctionalType(ostream &os, mlir::Value &v) { os << v; }
|
||||
}
|
||||
|
||||
#else
|
||||
#error unknown test
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue