Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 40 additions & 6 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -13007,6 +13007,37 @@ class Sema final : public SemaBase {
/// default arguments of its methods have been parsed.
UnparsedDefaultArgInstantiationsMap UnparsedDefaultArgInstantiations;

using InstantiatingSpecializationsKey = llvm::PointerIntPair<Decl *, 2>;

struct RecursiveInstGuard {
enum class Kind {
Template,
DefaultArgument,
ExceptionSpec,
};

RecursiveInstGuard(Sema &S, Decl *D, Kind Kind)
: S(S), Key(D->getCanonicalDecl(), unsigned(Kind)) {
auto [_, Created] = S.InstantiatingSpecializations.insert(Key);
if (!Created)
Key = {};
}

~RecursiveInstGuard() {
if (Key.getOpaqueValue()) {
[[maybe_unused]] bool Erased =
S.InstantiatingSpecializations.erase(Key);
assert(Erased);
}
}

operator bool() const { return Key.getOpaqueValue() == nullptr; }

private:
Sema &S;
Sema::InstantiatingSpecializationsKey Key;
};

/// A context in which code is being synthesized (where a source location
/// alone is not sufficient to identify the context). This covers template
/// instantiation and various forms of implicitly-generated functions.
Expand Down Expand Up @@ -13368,14 +13399,9 @@ class Sema final : public SemaBase {
/// recursive template instantiations.
bool isInvalid() const { return Invalid; }

/// Determine whether we are already instantiating this
/// specialization in some surrounding active instantiation.
bool isAlreadyInstantiating() const { return AlreadyInstantiating; }

private:
Sema &SemaRef;
bool Invalid;
bool AlreadyInstantiating;

InstantiatingTemplate(Sema &SemaRef,
CodeSynthesisContext::SynthesisKind Kind,
Expand Down Expand Up @@ -13505,7 +13531,7 @@ class Sema final : public SemaBase {
SmallVector<CodeSynthesisContext, 16> CodeSynthesisContexts;

/// Specializations whose definitions are currently being instantiated.
llvm::DenseSet<std::pair<Decl *, unsigned>> InstantiatingSpecializations;
llvm::DenseSet<InstantiatingSpecializationsKey> InstantiatingSpecializations;

/// Non-dependent types used in templates that have already been instantiated
/// by some template instantiation.
Expand Down Expand Up @@ -13780,6 +13806,14 @@ class Sema final : public SemaBase {
const MultiLevelTemplateArgumentList &TemplateArgs,
TemplateSpecializationKind TSK, bool Complain = true);

private:
bool InstantiateClassImpl(SourceLocation PointOfInstantiation,
CXXRecordDecl *Instantiation,
CXXRecordDecl *Pattern,
const MultiLevelTemplateArgumentList &TemplateArgs,
TemplateSpecializationKind TSK, bool Complain);

public:
/// Instantiate the definition of an enum from a given pattern.
///
/// \param PointOfInstantiation The point of instantiation within the
Expand Down
87 changes: 49 additions & 38 deletions clang/lib/Sema/SemaTemplateInstantiate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -639,15 +639,8 @@ Sema::InstantiatingTemplate::InstantiatingTemplate(
}

Invalid = SemaRef.pushCodeSynthesisContext(Inst);
if (!Invalid) {
AlreadyInstantiating =
!Inst.Entity
? false
: !SemaRef.InstantiatingSpecializations
.insert({Inst.Entity->getCanonicalDecl(), Inst.Kind})
.second;
if (!Invalid)
atTemplateBegin(SemaRef.TemplateInstCallbacks, SemaRef, Inst);
}
}

Sema::InstantiatingTemplate::InstantiatingTemplate(
Expand Down Expand Up @@ -902,13 +895,6 @@ void Sema::popCodeSynthesisContext() {

void Sema::InstantiatingTemplate::Clear() {
if (!Invalid) {
if (!AlreadyInstantiating) {
auto &Active = SemaRef.CodeSynthesisContexts.back();
if (Active.Entity)
SemaRef.InstantiatingSpecializations.erase(
{Active.Entity->getCanonicalDecl(), Active.Kind});
}

atTemplateEnd(SemaRef.TemplateInstCallbacks, SemaRef,
SemaRef.CodeSynthesisContexts.back());

Expand Down Expand Up @@ -3312,17 +3298,20 @@ bool Sema::SubstDefaultArgument(
FunctionDecl *FD = cast<FunctionDecl>(Param->getDeclContext());
Expr *PatternExpr = Param->getUninstantiatedDefaultArg();

RecursiveInstGuard AlreadyInstantiating(
*this, Param, RecursiveInstGuard::Kind::DefaultArgument);
if (AlreadyInstantiating) {
Param->setInvalidDecl();
return Diag(Param->getBeginLoc(), diag::err_recursive_default_argument)
<< FD << PatternExpr->getSourceRange();
}

EnterExpressionEvaluationContext EvalContext(
*this, ExpressionEvaluationContext::PotentiallyEvaluated, Param);

InstantiatingTemplate Inst(*this, Loc, Param, TemplateArgs.getInnermost());
if (Inst.isInvalid())
return true;
if (Inst.isAlreadyInstantiating()) {
Diag(Param->getBeginLoc(), diag::err_recursive_default_argument) << FD;
Param->setInvalidDecl();
return true;
}

ExprResult Result;
// C++ [dcl.fct.default]p5:
Expand Down Expand Up @@ -3554,12 +3543,26 @@ namespace clang {
}
}

bool
Sema::InstantiateClass(SourceLocation PointOfInstantiation,
CXXRecordDecl *Instantiation, CXXRecordDecl *Pattern,
const MultiLevelTemplateArgumentList &TemplateArgs,
TemplateSpecializationKind TSK,
bool Complain) {
bool Sema::InstantiateClass(SourceLocation PointOfInstantiation,
CXXRecordDecl *Instantiation,
CXXRecordDecl *Pattern,
const MultiLevelTemplateArgumentList &TemplateArgs,
TemplateSpecializationKind TSK, bool Complain) {
#ifndef NDEBUG
RecursiveInstGuard AlreadyInstantiating(*this, Instantiation,
RecursiveInstGuard::Kind::Template);
assert(!AlreadyInstantiating && "should have been caught by caller");
#endif

return InstantiateClassImpl(PointOfInstantiation, Instantiation, Pattern,
TemplateArgs, TSK, Complain);
}

bool Sema::InstantiateClassImpl(
SourceLocation PointOfInstantiation, CXXRecordDecl *Instantiation,
CXXRecordDecl *Pattern, const MultiLevelTemplateArgumentList &TemplateArgs,
TemplateSpecializationKind TSK, bool Complain) {

CXXRecordDecl *PatternDef
= cast_or_null<CXXRecordDecl>(Pattern->getDefinition());
if (DiagnoseUninstantiableTemplate(PointOfInstantiation, Instantiation,
Expand Down Expand Up @@ -3596,7 +3599,6 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation,
InstantiatingTemplate Inst(*this, PointOfInstantiation, Instantiation);
if (Inst.isInvalid())
return true;
assert(!Inst.isAlreadyInstantiating() && "should have been caught by caller");
PrettyDeclStackTraceEntry CrashInfo(Context, Instantiation, SourceLocation(),
"instantiating class definition");

Expand Down Expand Up @@ -3808,6 +3810,12 @@ bool Sema::InstantiateEnum(SourceLocation PointOfInstantiation,
EnumDecl *Instantiation, EnumDecl *Pattern,
const MultiLevelTemplateArgumentList &TemplateArgs,
TemplateSpecializationKind TSK) {
#ifndef NDEBUG
RecursiveInstGuard AlreadyInstantiating(*this, Instantiation,
RecursiveInstGuard::Kind::Template);
assert(!AlreadyInstantiating && "should have been caught by caller");
#endif

EnumDecl *PatternDef = Pattern->getDefinition();
if (DiagnoseUninstantiableTemplate(PointOfInstantiation, Instantiation,
Instantiation->getInstantiatedFromMemberEnum(),
Expand All @@ -3825,8 +3833,6 @@ bool Sema::InstantiateEnum(SourceLocation PointOfInstantiation,
InstantiatingTemplate Inst(*this, PointOfInstantiation, Instantiation);
if (Inst.isInvalid())
return true;
if (Inst.isAlreadyInstantiating())
return false;
PrettyDeclStackTraceEntry CrashInfo(Context, Instantiation, SourceLocation(),
"instantiating enum definition");

Expand Down Expand Up @@ -3865,6 +3871,14 @@ bool Sema::InstantiateInClassInitializer(
Pattern->getInClassInitStyle() &&
"pattern and instantiation disagree about init style");

RecursiveInstGuard AlreadyInstantiating(*this, Instantiation,
RecursiveInstGuard::Kind::Template);
if (AlreadyInstantiating)
// Error out if we hit an instantiation cycle for this initializer.
return Diag(PointOfInstantiation,
diag::err_default_member_initializer_cycle)
<< Instantiation;

// Error out if we haven't parsed the initializer of the pattern yet because
// we are waiting for the closing brace of the outer class.
Expr *OldInit = Pattern->getInClassInitializer();
Expand All @@ -3883,12 +3897,6 @@ bool Sema::InstantiateInClassInitializer(
InstantiatingTemplate Inst(*this, PointOfInstantiation, Instantiation);
if (Inst.isInvalid())
return true;
if (Inst.isAlreadyInstantiating()) {
// Error out if we hit an instantiation cycle for this initializer.
Diag(PointOfInstantiation, diag::err_default_member_initializer_cycle)
<< Instantiation;
return true;
}
PrettyDeclStackTraceEntry CrashInfo(Context, Instantiation, SourceLocation(),
"instantiating default member init");

Expand Down Expand Up @@ -3972,8 +3980,6 @@ static ActionResult<CXXRecordDecl *> getPatternForClassTemplateSpecialization(
Sema::InstantiatingTemplate Inst(S, PointOfInstantiation, ClassTemplateSpec);
if (Inst.isInvalid())
return {/*Invalid=*/true};
if (Inst.isAlreadyInstantiating())
return {/*Invalid=*/false};

llvm::PointerUnion<ClassTemplateDecl *,
ClassTemplatePartialSpecializationDecl *>
Expand Down Expand Up @@ -4136,6 +4142,11 @@ bool Sema::InstantiateClassTemplateSpecialization(
if (ClassTemplateSpec->isInvalidDecl())
return true;

Sema::RecursiveInstGuard AlreadyInstantiating(
*this, ClassTemplateSpec, Sema::RecursiveInstGuard::Kind::Template);
if (AlreadyInstantiating)
return false;

bool HadAvaibilityWarning =
ShouldDiagnoseAvailabilityOfDecl(ClassTemplateSpec, nullptr, nullptr)
.first != AR_Available;
Expand All @@ -4148,7 +4159,7 @@ bool Sema::InstantiateClassTemplateSpecialization(
if (!Pattern.isUsable())
return Pattern.isInvalid();

bool Err = InstantiateClass(
bool Err = InstantiateClassImpl(
PointOfInstantiation, ClassTemplateSpec, Pattern.get(),
getTemplateInstantiationArgs(ClassTemplateSpec), TSK, Complain);

Expand Down
36 changes: 24 additions & 12 deletions clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5312,6 +5312,16 @@ void Sema::InstantiateExceptionSpec(SourceLocation PointOfInstantiation,
if (Proto->getExceptionSpecType() != EST_Uninstantiated)
return;

RecursiveInstGuard AlreadyInstantiating(
*this, Decl, RecursiveInstGuard::Kind::ExceptionSpec);
if (AlreadyInstantiating) {
// This exception specification indirectly depends on itself. Reject.
// FIXME: Corresponding rule in the standard?
Diag(PointOfInstantiation, diag::err_exception_spec_cycle) << Decl;
UpdateExceptionSpec(Decl, EST_None);
return;
}

InstantiatingTemplate Inst(*this, PointOfInstantiation, Decl,
InstantiatingTemplate::ExceptionSpecification());
if (Inst.isInvalid()) {
Expand All @@ -5320,13 +5330,6 @@ void Sema::InstantiateExceptionSpec(SourceLocation PointOfInstantiation,
UpdateExceptionSpec(Decl, EST_None);
return;
}
if (Inst.isAlreadyInstantiating()) {
// This exception specification indirectly depends on itself. Reject.
// FIXME: Corresponding rule in the standard?
Diag(PointOfInstantiation, diag::err_exception_spec_cycle) << Decl;
UpdateExceptionSpec(Decl, EST_None);
return;
}

// Enter the scope of this instantiation. We don't use
// PushDeclContext because we don't have a scope.
Expand Down Expand Up @@ -5386,8 +5389,6 @@ TemplateDeclInstantiator::InitFunctionInstantiation(FunctionDecl *New,
if (ActiveInst.Kind == ActiveInstType::ExplicitTemplateArgumentSubstitution ||
ActiveInst.Kind == ActiveInstType::DeducedTemplateArgumentSubstitution) {
if (isa<FunctionTemplateDecl>(ActiveInst.Entity)) {
SemaRef.InstantiatingSpecializations.erase(
{ActiveInst.Entity->getCanonicalDecl(), ActiveInst.Kind});
atTemplateEnd(SemaRef.TemplateInstCallbacks, SemaRef, ActiveInst);
ActiveInst.Kind = ActiveInstType::TemplateInstantiation;
ActiveInst.Entity = New;
Expand Down Expand Up @@ -5545,6 +5546,12 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
Function = const_cast<FunctionDecl*>(ExistingDefn);
}

#ifndef NDEBUG
RecursiveInstGuard AlreadyInstantiating(*this, Function,
RecursiveInstGuard::Kind::Template);
assert(!AlreadyInstantiating && "should have been caught by caller");
#endif

// Find the function body that we'll be substituting.
const FunctionDecl *PatternDecl = Function->getTemplateInstantiationPattern();
assert(PatternDecl && "instantiating a non-template");
Expand Down Expand Up @@ -5684,7 +5691,7 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
}

InstantiatingTemplate Inst(*this, PointOfInstantiation, Function);
if (Inst.isInvalid() || Inst.isAlreadyInstantiating())
if (Inst.isInvalid())
return;
PrettyDeclStackTraceEntry CrashInfo(Context, Function, SourceLocation(),
"instantiating function definition");
Expand Down Expand Up @@ -6253,6 +6260,11 @@ void Sema::InstantiateVariableDefinition(SourceLocation PointOfInstantiation,
if (TSK == TSK_ExplicitSpecialization)
return;

RecursiveInstGuard AlreadyInstantiating(*this, Var,
RecursiveInstGuard::Kind::Template);
if (AlreadyInstantiating)
return;

// Find the pattern and the arguments to substitute into it.
VarDecl *PatternDecl = Var->getTemplateInstantiationPattern();
assert(PatternDecl && "no pattern for templated variable");
Expand All @@ -6276,7 +6288,7 @@ void Sema::InstantiateVariableDefinition(SourceLocation PointOfInstantiation,
// FIXME: Factor out the duplicated instantiation context setup/tear down
// code here.
InstantiatingTemplate Inst(*this, PointOfInstantiation, Var);
if (Inst.isInvalid() || Inst.isAlreadyInstantiating())
if (Inst.isInvalid())
return;
PrettyDeclStackTraceEntry CrashInfo(Context, Var, SourceLocation(),
"instantiating variable initializer");
Expand Down Expand Up @@ -6380,7 +6392,7 @@ void Sema::InstantiateVariableDefinition(SourceLocation PointOfInstantiation,
}

InstantiatingTemplate Inst(*this, PointOfInstantiation, Var);
if (Inst.isInvalid() || Inst.isAlreadyInstantiating())
if (Inst.isInvalid())
return;
PrettyDeclStackTraceEntry CrashInfo(Context, Var, SourceLocation(),
"instantiating variable definition");
Expand Down
7 changes: 4 additions & 3 deletions clang/test/SemaCXX/libstdcxx_pair_swap_hack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,14 @@ namespace sad {
template<typename T> void swap(T &, T &);

template<typename A, typename B> struct CLASS {
void swap(CLASS &other) noexcept(noexcept(swap(*this, other))); // expected-error {{too many arguments}} expected-note {{declared here}}
// expected-error@-1{{uses itself}} expected-note@-1{{in instantiation of}}
void swap(CLASS &other) // expected-note {{declared here}}
noexcept(noexcept(swap(*this, other))); // expected-error {{too many arguments}}
// expected-error@-1{{uses itself}}
};

CLASS<int, int> pi;

static_assert(!noexcept(pi.swap(pi)), ""); // expected-note 2{{in instantiation of exception specification for 'swap'}}
static_assert(!noexcept(pi.swap(pi)), ""); // expected-note {{in instantiation of exception specification for 'swap'}}
}

#endif
Loading
Loading