Skip to content

Commit dcf5b6f

Browse files
committed
[clang] separate recursive instantiation check from CodeSynthesisContext
This makes pushing / popping CodeSynthesisContexts much cheaper, as it delegates to another class this functionality which is not actually needed in most cases. It also converts a bunch of these uses into just asserts. This improves compiler performance a little bit: Some diagnostics change a little bit, because we avoid printing a redundant context notes.
1 parent 2e6da80 commit dcf5b6f

File tree

5 files changed

+99
-55
lines changed

5 files changed

+99
-55
lines changed

clang/include/clang/Sema/Sema.h

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12995,6 +12995,36 @@ class Sema final : public SemaBase {
1299512995
/// default arguments of its methods have been parsed.
1299612996
UnparsedDefaultArgInstantiationsMap UnparsedDefaultArgInstantiations;
1299712997

12998+
using InstantiatingSpecializationsKey = llvm::PointerIntPair<Decl *, 2>;
12999+
13000+
struct RecursiveInstGuard {
13001+
enum class Kind {
13002+
Template,
13003+
DefaultArgument,
13004+
ExceptionSpec,
13005+
};
13006+
13007+
RecursiveInstGuard(Sema &S, Decl *D, Kind Kind)
13008+
: S(S), Key(D->getCanonicalDecl(), unsigned(Kind)) {
13009+
auto [_, Created] = S.InstantiatingSpecializations.insert(Key);
13010+
if (!Created)
13011+
Key = {};
13012+
}
13013+
13014+
~RecursiveInstGuard() {
13015+
if (Key.getOpaqueValue()) {
13016+
[[maybe_unused]] bool Found = S.InstantiatingSpecializations.erase(Key);
13017+
assert(Found);
13018+
}
13019+
}
13020+
13021+
operator bool() const { return Key.getOpaqueValue() == nullptr; }
13022+
13023+
private:
13024+
Sema &S;
13025+
Sema::InstantiatingSpecializationsKey Key;
13026+
};
13027+
1299813028
/// A context in which code is being synthesized (where a source location
1299913029
/// alone is not sufficient to identify the context). This covers template
1300013030
/// instantiation and various forms of implicitly-generated functions.
@@ -13356,14 +13386,9 @@ class Sema final : public SemaBase {
1335613386
/// recursive template instantiations.
1335713387
bool isInvalid() const { return Invalid; }
1335813388

13359-
/// Determine whether we are already instantiating this
13360-
/// specialization in some surrounding active instantiation.
13361-
bool isAlreadyInstantiating() const { return AlreadyInstantiating; }
13362-
1336313389
private:
1336413390
Sema &SemaRef;
1336513391
bool Invalid;
13366-
bool AlreadyInstantiating;
1336713392

1336813393
InstantiatingTemplate(Sema &SemaRef,
1336913394
CodeSynthesisContext::SynthesisKind Kind,
@@ -13486,7 +13511,7 @@ class Sema final : public SemaBase {
1348613511
SmallVector<CodeSynthesisContext, 16> CodeSynthesisContexts;
1348713512

1348813513
/// Specializations whose definitions are currently being instantiated.
13489-
llvm::DenseSet<std::pair<Decl *, unsigned>> InstantiatingSpecializations;
13514+
llvm::DenseSet<InstantiatingSpecializationsKey> InstantiatingSpecializations;
1349013515

1349113516
/// Non-dependent types used in templates that have already been instantiated
1349213517
/// by some template instantiation.

clang/lib/Sema/SemaTemplateInstantiate.cpp

Lines changed: 37 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -638,15 +638,8 @@ Sema::InstantiatingTemplate::InstantiatingTemplate(
638638
}
639639

640640
Invalid = SemaRef.pushCodeSynthesisContext(Inst);
641-
if (!Invalid) {
642-
AlreadyInstantiating =
643-
!Inst.Entity
644-
? false
645-
: !SemaRef.InstantiatingSpecializations
646-
.insert({Inst.Entity->getCanonicalDecl(), Inst.Kind})
647-
.second;
641+
if (!Invalid)
648642
atTemplateBegin(SemaRef.TemplateInstCallbacks, SemaRef, Inst);
649-
}
650643
}
651644

652645
Sema::InstantiatingTemplate::InstantiatingTemplate(
@@ -901,13 +894,6 @@ void Sema::popCodeSynthesisContext() {
901894

902895
void Sema::InstantiatingTemplate::Clear() {
903896
if (!Invalid) {
904-
if (!AlreadyInstantiating) {
905-
auto &Active = SemaRef.CodeSynthesisContexts.back();
906-
if (Active.Entity)
907-
SemaRef.InstantiatingSpecializations.erase(
908-
{Active.Entity->getCanonicalDecl(), Active.Kind});
909-
}
910-
911897
atTemplateEnd(SemaRef.TemplateInstCallbacks, SemaRef,
912898
SemaRef.CodeSynthesisContexts.back());
913899

@@ -3311,17 +3297,21 @@ bool Sema::SubstDefaultArgument(
33113297
FunctionDecl *FD = cast<FunctionDecl>(Param->getDeclContext());
33123298
Expr *PatternExpr = Param->getUninstantiatedDefaultArg();
33133299

3300+
RecursiveInstGuard AlreadyInstantiating(
3301+
*this, Param, RecursiveInstGuard::Kind::DefaultArgument);
3302+
if (AlreadyInstantiating) {
3303+
Diag(Param->getBeginLoc(), diag::err_recursive_default_argument)
3304+
<< FD << PatternExpr->getSourceRange();
3305+
Param->setInvalidDecl();
3306+
return true;
3307+
}
3308+
33143309
EnterExpressionEvaluationContext EvalContext(
33153310
*this, ExpressionEvaluationContext::PotentiallyEvaluated, Param);
33163311

33173312
InstantiatingTemplate Inst(*this, Loc, Param, TemplateArgs.getInnermost());
33183313
if (Inst.isInvalid())
33193314
return true;
3320-
if (Inst.isAlreadyInstantiating()) {
3321-
Diag(Param->getBeginLoc(), diag::err_recursive_default_argument) << FD;
3322-
Param->setInvalidDecl();
3323-
return true;
3324-
}
33253315

33263316
ExprResult Result;
33273317
// C++ [dcl.fct.default]p5:
@@ -3559,6 +3549,12 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation,
35593549
const MultiLevelTemplateArgumentList &TemplateArgs,
35603550
TemplateSpecializationKind TSK,
35613551
bool Complain) {
3552+
#ifndef NDEBUG
3553+
RecursiveInstGuard AlreadyInstantiating(*this, Instantiation,
3554+
RecursiveInstGuard::Kind::Template);
3555+
assert(!AlreadyInstantiating && "should have been caught by caller");
3556+
#endif
3557+
35623558
CXXRecordDecl *PatternDef
35633559
= cast_or_null<CXXRecordDecl>(Pattern->getDefinition());
35643560
if (DiagnoseUninstantiableTemplate(PointOfInstantiation, Instantiation,
@@ -3595,7 +3591,6 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation,
35953591
InstantiatingTemplate Inst(*this, PointOfInstantiation, Instantiation);
35963592
if (Inst.isInvalid())
35973593
return true;
3598-
assert(!Inst.isAlreadyInstantiating() && "should have been caught by caller");
35993594
PrettyDeclStackTraceEntry CrashInfo(Context, Instantiation, SourceLocation(),
36003595
"instantiating class definition");
36013596

@@ -3807,6 +3802,12 @@ bool Sema::InstantiateEnum(SourceLocation PointOfInstantiation,
38073802
EnumDecl *Instantiation, EnumDecl *Pattern,
38083803
const MultiLevelTemplateArgumentList &TemplateArgs,
38093804
TemplateSpecializationKind TSK) {
3805+
#ifndef NDEBUG
3806+
RecursiveInstGuard AlreadyInstantiating(*this, Instantiation,
3807+
RecursiveInstGuard::Kind::Template);
3808+
assert(!AlreadyInstantiating && "should have been caught by caller");
3809+
#endif
3810+
38103811
EnumDecl *PatternDef = Pattern->getDefinition();
38113812
if (DiagnoseUninstantiableTemplate(PointOfInstantiation, Instantiation,
38123813
Instantiation->getInstantiatedFromMemberEnum(),
@@ -3824,8 +3825,6 @@ bool Sema::InstantiateEnum(SourceLocation PointOfInstantiation,
38243825
InstantiatingTemplate Inst(*this, PointOfInstantiation, Instantiation);
38253826
if (Inst.isInvalid())
38263827
return true;
3827-
if (Inst.isAlreadyInstantiating())
3828-
return false;
38293828
PrettyDeclStackTraceEntry CrashInfo(Context, Instantiation, SourceLocation(),
38303829
"instantiating enum definition");
38313830

@@ -3864,6 +3863,15 @@ bool Sema::InstantiateInClassInitializer(
38643863
Pattern->getInClassInitStyle() &&
38653864
"pattern and instantiation disagree about init style");
38663865

3866+
RecursiveInstGuard AlreadyInstantiating(*this, Instantiation,
3867+
RecursiveInstGuard::Kind::Template);
3868+
if (AlreadyInstantiating) {
3869+
// Error out if we hit an instantiation cycle for this initializer.
3870+
Diag(PointOfInstantiation, diag::err_default_member_initializer_cycle)
3871+
<< Instantiation;
3872+
return true;
3873+
}
3874+
38673875
// Error out if we haven't parsed the initializer of the pattern yet because
38683876
// we are waiting for the closing brace of the outer class.
38693877
Expr *OldInit = Pattern->getInClassInitializer();
@@ -3882,12 +3890,6 @@ bool Sema::InstantiateInClassInitializer(
38823890
InstantiatingTemplate Inst(*this, PointOfInstantiation, Instantiation);
38833891
if (Inst.isInvalid())
38843892
return true;
3885-
if (Inst.isAlreadyInstantiating()) {
3886-
// Error out if we hit an instantiation cycle for this initializer.
3887-
Diag(PointOfInstantiation, diag::err_default_member_initializer_cycle)
3888-
<< Instantiation;
3889-
return true;
3890-
}
38913893
PrettyDeclStackTraceEntry CrashInfo(Context, Instantiation, SourceLocation(),
38923894
"instantiating default member init");
38933895

@@ -3968,11 +3970,15 @@ static ActionResult<CXXRecordDecl *> getPatternForClassTemplateSpecialization(
39683970
Sema &S, SourceLocation PointOfInstantiation,
39693971
ClassTemplateSpecializationDecl *ClassTemplateSpec,
39703972
TemplateSpecializationKind TSK, bool PrimaryStrictPackMatch) {
3973+
#ifndef NDEBUG
3974+
Sema::RecursiveInstGuard AlreadyInstantiating(
3975+
S, ClassTemplateSpec, Sema::RecursiveInstGuard::Kind::Template);
3976+
assert(!AlreadyInstantiating && "should have been caught by caller");
3977+
#endif
3978+
39713979
Sema::InstantiatingTemplate Inst(S, PointOfInstantiation, ClassTemplateSpec);
39723980
if (Inst.isInvalid())
39733981
return {/*Invalid=*/true};
3974-
if (Inst.isAlreadyInstantiating())
3975-
return {/*Invalid=*/false};
39763982

39773983
llvm::PointerUnion<ClassTemplateDecl *,
39783984
ClassTemplatePartialSpecializationDecl *>

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5282,6 +5282,16 @@ void Sema::InstantiateExceptionSpec(SourceLocation PointOfInstantiation,
52825282
if (Proto->getExceptionSpecType() != EST_Uninstantiated)
52835283
return;
52845284

5285+
RecursiveInstGuard AlreadyInstantiating(
5286+
*this, Decl, RecursiveInstGuard::Kind::ExceptionSpec);
5287+
if (AlreadyInstantiating) {
5288+
// This exception specification indirectly depends on itself. Reject.
5289+
// FIXME: Corresponding rule in the standard?
5290+
Diag(PointOfInstantiation, diag::err_exception_spec_cycle) << Decl;
5291+
UpdateExceptionSpec(Decl, EST_None);
5292+
return;
5293+
}
5294+
52855295
InstantiatingTemplate Inst(*this, PointOfInstantiation, Decl,
52865296
InstantiatingTemplate::ExceptionSpecification());
52875297
if (Inst.isInvalid()) {
@@ -5290,13 +5300,6 @@ void Sema::InstantiateExceptionSpec(SourceLocation PointOfInstantiation,
52905300
UpdateExceptionSpec(Decl, EST_None);
52915301
return;
52925302
}
5293-
if (Inst.isAlreadyInstantiating()) {
5294-
// This exception specification indirectly depends on itself. Reject.
5295-
// FIXME: Corresponding rule in the standard?
5296-
Diag(PointOfInstantiation, diag::err_exception_spec_cycle) << Decl;
5297-
UpdateExceptionSpec(Decl, EST_None);
5298-
return;
5299-
}
53005303

53015304
// Enter the scope of this instantiation. We don't use
53025305
// PushDeclContext because we don't have a scope.
@@ -5356,8 +5359,6 @@ TemplateDeclInstantiator::InitFunctionInstantiation(FunctionDecl *New,
53565359
if (ActiveInst.Kind == ActiveInstType::ExplicitTemplateArgumentSubstitution ||
53575360
ActiveInst.Kind == ActiveInstType::DeducedTemplateArgumentSubstitution) {
53585361
if (isa<FunctionTemplateDecl>(ActiveInst.Entity)) {
5359-
SemaRef.InstantiatingSpecializations.erase(
5360-
{ActiveInst.Entity->getCanonicalDecl(), ActiveInst.Kind});
53615362
atTemplateEnd(SemaRef.TemplateInstCallbacks, SemaRef, ActiveInst);
53625363
ActiveInst.Kind = ActiveInstType::TemplateInstantiation;
53635364
ActiveInst.Entity = New;
@@ -5515,6 +5516,12 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
55155516
Function = const_cast<FunctionDecl*>(ExistingDefn);
55165517
}
55175518

5519+
#ifndef NDEBUG
5520+
RecursiveInstGuard AlreadyInstantiating(*this, Function,
5521+
RecursiveInstGuard::Kind::Template);
5522+
assert(!AlreadyInstantiating && "should have been caught by caller");
5523+
#endif
5524+
55185525
// Find the function body that we'll be substituting.
55195526
const FunctionDecl *PatternDecl = Function->getTemplateInstantiationPattern();
55205527
assert(PatternDecl && "instantiating a non-template");
@@ -5654,7 +5661,7 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
56545661
}
56555662

56565663
InstantiatingTemplate Inst(*this, PointOfInstantiation, Function);
5657-
if (Inst.isInvalid() || Inst.isAlreadyInstantiating())
5664+
if (Inst.isInvalid())
56585665
return;
56595666
PrettyDeclStackTraceEntry CrashInfo(Context, Function, SourceLocation(),
56605667
"instantiating function definition");
@@ -6223,6 +6230,11 @@ void Sema::InstantiateVariableDefinition(SourceLocation PointOfInstantiation,
62236230
if (TSK == TSK_ExplicitSpecialization)
62246231
return;
62256232

6233+
RecursiveInstGuard AlreadyInstantiating(*this, Var,
6234+
RecursiveInstGuard::Kind::Template);
6235+
if (AlreadyInstantiating)
6236+
return;
6237+
62266238
// Find the pattern and the arguments to substitute into it.
62276239
VarDecl *PatternDecl = Var->getTemplateInstantiationPattern();
62286240
assert(PatternDecl && "no pattern for templated variable");
@@ -6246,7 +6258,7 @@ void Sema::InstantiateVariableDefinition(SourceLocation PointOfInstantiation,
62466258
// FIXME: Factor out the duplicated instantiation context setup/tear down
62476259
// code here.
62486260
InstantiatingTemplate Inst(*this, PointOfInstantiation, Var);
6249-
if (Inst.isInvalid() || Inst.isAlreadyInstantiating())
6261+
if (Inst.isInvalid())
62506262
return;
62516263
PrettyDeclStackTraceEntry CrashInfo(Context, Var, SourceLocation(),
62526264
"instantiating variable initializer");
@@ -6350,7 +6362,7 @@ void Sema::InstantiateVariableDefinition(SourceLocation PointOfInstantiation,
63506362
}
63516363

63526364
InstantiatingTemplate Inst(*this, PointOfInstantiation, Var);
6353-
if (Inst.isInvalid() || Inst.isAlreadyInstantiating())
6365+
if (Inst.isInvalid())
63546366
return;
63556367
PrettyDeclStackTraceEntry CrashInfo(Context, Var, SourceLocation(),
63566368
"instantiating variable definition");

clang/test/SemaCXX/libstdcxx_pair_swap_hack.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,14 @@ namespace sad {
8282
template<typename T> void swap(T &, T &);
8383

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

8990
CLASS<int, int> pi;
9091

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

9495
#endif

clang/test/SemaTemplate/instantiate-self.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ namespace test7 {
8686

8787
namespace test8 {
8888
template<typename T> struct A {
89-
int n = A{}.n; // expected-error {{default member initializer for 'n' uses itself}} expected-note {{instantiation of default member init}}
89+
int n = A{}.n; // expected-error {{default member initializer for 'n' uses itself}}
9090
};
9191
A<int> ai = {}; // expected-note {{instantiation of default member init}}
9292
}
@@ -100,7 +100,7 @@ namespace test9 {
100100

101101
namespace test10 {
102102
template<typename T> struct A {
103-
void f() noexcept(noexcept(f())); // expected-error {{exception specification of 'f' uses itself}} expected-note {{instantiation of}}
103+
void f() noexcept(noexcept(f())); // expected-error {{exception specification of 'f' uses itself}}
104104
};
105105
bool b = noexcept(A<int>().f()); // expected-note {{instantiation of}}
106106
}
@@ -125,7 +125,7 @@ namespace test11 {
125125
}
126126

127127
namespace test12 {
128-
template<typename T> int f(T t, int = f(T())) {} // expected-error {{recursive evaluation of default argument}} expected-note {{instantiation of}}
128+
template<typename T> int f(T t, int = f(T())) {} // expected-error {{recursive evaluation of default argument}}
129129
struct X {};
130130
int q = f(X()); // expected-note {{instantiation of}}
131131
}

0 commit comments

Comments
 (0)