Skip to content

Commit 63ca2fd

Browse files
authored
[clang] Reland: separate recursive instantiation check from CodeSynthesisContext (#164177)
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. This relands #162224 with no changes, turns out the buildbot failure was unrelated.
1 parent e4c97f0 commit 63ca2fd

File tree

5 files changed

+142
-62
lines changed

5 files changed

+142
-62
lines changed

clang/include/clang/Sema/Sema.h

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13007,6 +13007,37 @@ class Sema final : public SemaBase {
1300713007
/// default arguments of its methods have been parsed.
1300813008
UnparsedDefaultArgInstantiationsMap UnparsedDefaultArgInstantiations;
1300913009

13010+
using InstantiatingSpecializationsKey = llvm::PointerIntPair<Decl *, 2>;
13011+
13012+
struct RecursiveInstGuard {
13013+
enum class Kind {
13014+
Template,
13015+
DefaultArgument,
13016+
ExceptionSpec,
13017+
};
13018+
13019+
RecursiveInstGuard(Sema &S, Decl *D, Kind Kind)
13020+
: S(S), Key(D->getCanonicalDecl(), unsigned(Kind)) {
13021+
auto [_, Created] = S.InstantiatingSpecializations.insert(Key);
13022+
if (!Created)
13023+
Key = {};
13024+
}
13025+
13026+
~RecursiveInstGuard() {
13027+
if (Key.getOpaqueValue()) {
13028+
[[maybe_unused]] bool Erased =
13029+
S.InstantiatingSpecializations.erase(Key);
13030+
assert(Erased);
13031+
}
13032+
}
13033+
13034+
operator bool() const { return Key.getOpaqueValue() == nullptr; }
13035+
13036+
private:
13037+
Sema &S;
13038+
Sema::InstantiatingSpecializationsKey Key;
13039+
};
13040+
1301013041
/// A context in which code is being synthesized (where a source location
1301113042
/// alone is not sufficient to identify the context). This covers template
1301213043
/// instantiation and various forms of implicitly-generated functions.
@@ -13368,14 +13399,9 @@ class Sema final : public SemaBase {
1336813399
/// recursive template instantiations.
1336913400
bool isInvalid() const { return Invalid; }
1337013401

13371-
/// Determine whether we are already instantiating this
13372-
/// specialization in some surrounding active instantiation.
13373-
bool isAlreadyInstantiating() const { return AlreadyInstantiating; }
13374-
1337513402
private:
1337613403
Sema &SemaRef;
1337713404
bool Invalid;
13378-
bool AlreadyInstantiating;
1337913405

1338013406
InstantiatingTemplate(Sema &SemaRef,
1338113407
CodeSynthesisContext::SynthesisKind Kind,
@@ -13505,7 +13531,7 @@ class Sema final : public SemaBase {
1350513531
SmallVector<CodeSynthesisContext, 16> CodeSynthesisContexts;
1350613532

1350713533
/// Specializations whose definitions are currently being instantiated.
13508-
llvm::DenseSet<std::pair<Decl *, unsigned>> InstantiatingSpecializations;
13534+
llvm::DenseSet<InstantiatingSpecializationsKey> InstantiatingSpecializations;
1350913535

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

13809+
private:
13810+
bool InstantiateClassImpl(SourceLocation PointOfInstantiation,
13811+
CXXRecordDecl *Instantiation,
13812+
CXXRecordDecl *Pattern,
13813+
const MultiLevelTemplateArgumentList &TemplateArgs,
13814+
TemplateSpecializationKind TSK, bool Complain);
13815+
13816+
public:
1378313817
/// Instantiate the definition of an enum from a given pattern.
1378413818
///
1378513819
/// \param PointOfInstantiation The point of instantiation within the

clang/lib/Sema/SemaTemplateInstantiate.cpp

Lines changed: 49 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -639,15 +639,8 @@ Sema::InstantiatingTemplate::InstantiatingTemplate(
639639
}
640640

641641
Invalid = SemaRef.pushCodeSynthesisContext(Inst);
642-
if (!Invalid) {
643-
AlreadyInstantiating =
644-
!Inst.Entity
645-
? false
646-
: !SemaRef.InstantiatingSpecializations
647-
.insert({Inst.Entity->getCanonicalDecl(), Inst.Kind})
648-
.second;
642+
if (!Invalid)
649643
atTemplateBegin(SemaRef.TemplateInstCallbacks, SemaRef, Inst);
650-
}
651644
}
652645

653646
Sema::InstantiatingTemplate::InstantiatingTemplate(
@@ -902,13 +895,6 @@ void Sema::popCodeSynthesisContext() {
902895

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

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

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

33183312
InstantiatingTemplate Inst(*this, Loc, Param, TemplateArgs.getInnermost());
33193313
if (Inst.isInvalid())
33203314
return true;
3321-
if (Inst.isAlreadyInstantiating()) {
3322-
Diag(Param->getBeginLoc(), diag::err_recursive_default_argument) << FD;
3323-
Param->setInvalidDecl();
3324-
return true;
3325-
}
33263315

33273316
ExprResult Result;
33283317
// C++ [dcl.fct.default]p5:
@@ -3554,12 +3543,26 @@ namespace clang {
35543543
}
35553544
}
35563545

3557-
bool
3558-
Sema::InstantiateClass(SourceLocation PointOfInstantiation,
3559-
CXXRecordDecl *Instantiation, CXXRecordDecl *Pattern,
3560-
const MultiLevelTemplateArgumentList &TemplateArgs,
3561-
TemplateSpecializationKind TSK,
3562-
bool Complain) {
3546+
bool Sema::InstantiateClass(SourceLocation PointOfInstantiation,
3547+
CXXRecordDecl *Instantiation,
3548+
CXXRecordDecl *Pattern,
3549+
const MultiLevelTemplateArgumentList &TemplateArgs,
3550+
TemplateSpecializationKind TSK, bool Complain) {
3551+
#ifndef NDEBUG
3552+
RecursiveInstGuard AlreadyInstantiating(*this, Instantiation,
3553+
RecursiveInstGuard::Kind::Template);
3554+
assert(!AlreadyInstantiating && "should have been caught by caller");
3555+
#endif
3556+
3557+
return InstantiateClassImpl(PointOfInstantiation, Instantiation, Pattern,
3558+
TemplateArgs, TSK, Complain);
3559+
}
3560+
3561+
bool Sema::InstantiateClassImpl(
3562+
SourceLocation PointOfInstantiation, CXXRecordDecl *Instantiation,
3563+
CXXRecordDecl *Pattern, const MultiLevelTemplateArgumentList &TemplateArgs,
3564+
TemplateSpecializationKind TSK, bool Complain) {
3565+
35633566
CXXRecordDecl *PatternDef
35643567
= cast_or_null<CXXRecordDecl>(Pattern->getDefinition());
35653568
if (DiagnoseUninstantiableTemplate(PointOfInstantiation, Instantiation,
@@ -3596,7 +3599,6 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation,
35963599
InstantiatingTemplate Inst(*this, PointOfInstantiation, Instantiation);
35973600
if (Inst.isInvalid())
35983601
return true;
3599-
assert(!Inst.isAlreadyInstantiating() && "should have been caught by caller");
36003602
PrettyDeclStackTraceEntry CrashInfo(Context, Instantiation, SourceLocation(),
36013603
"instantiating class definition");
36023604

@@ -3808,6 +3810,12 @@ bool Sema::InstantiateEnum(SourceLocation PointOfInstantiation,
38083810
EnumDecl *Instantiation, EnumDecl *Pattern,
38093811
const MultiLevelTemplateArgumentList &TemplateArgs,
38103812
TemplateSpecializationKind TSK) {
3813+
#ifndef NDEBUG
3814+
RecursiveInstGuard AlreadyInstantiating(*this, Instantiation,
3815+
RecursiveInstGuard::Kind::Template);
3816+
assert(!AlreadyInstantiating && "should have been caught by caller");
3817+
#endif
3818+
38113819
EnumDecl *PatternDef = Pattern->getDefinition();
38123820
if (DiagnoseUninstantiableTemplate(PointOfInstantiation, Instantiation,
38133821
Instantiation->getInstantiatedFromMemberEnum(),
@@ -3825,8 +3833,6 @@ bool Sema::InstantiateEnum(SourceLocation PointOfInstantiation,
38253833
InstantiatingTemplate Inst(*this, PointOfInstantiation, Instantiation);
38263834
if (Inst.isInvalid())
38273835
return true;
3828-
if (Inst.isAlreadyInstantiating())
3829-
return false;
38303836
PrettyDeclStackTraceEntry CrashInfo(Context, Instantiation, SourceLocation(),
38313837
"instantiating enum definition");
38323838

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

3874+
RecursiveInstGuard AlreadyInstantiating(*this, Instantiation,
3875+
RecursiveInstGuard::Kind::Template);
3876+
if (AlreadyInstantiating)
3877+
// Error out if we hit an instantiation cycle for this initializer.
3878+
return Diag(PointOfInstantiation,
3879+
diag::err_default_member_initializer_cycle)
3880+
<< Instantiation;
3881+
38683882
// Error out if we haven't parsed the initializer of the pattern yet because
38693883
// we are waiting for the closing brace of the outer class.
38703884
Expr *OldInit = Pattern->getInClassInitializer();
@@ -3883,12 +3897,6 @@ bool Sema::InstantiateInClassInitializer(
38833897
InstantiatingTemplate Inst(*this, PointOfInstantiation, Instantiation);
38843898
if (Inst.isInvalid())
38853899
return true;
3886-
if (Inst.isAlreadyInstantiating()) {
3887-
// Error out if we hit an instantiation cycle for this initializer.
3888-
Diag(PointOfInstantiation, diag::err_default_member_initializer_cycle)
3889-
<< Instantiation;
3890-
return true;
3891-
}
38923900
PrettyDeclStackTraceEntry CrashInfo(Context, Instantiation, SourceLocation(),
38933901
"instantiating default member init");
38943902

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

39783984
llvm::PointerUnion<ClassTemplateDecl *,
39793985
ClassTemplatePartialSpecializationDecl *>
@@ -4136,6 +4142,11 @@ bool Sema::InstantiateClassTemplateSpecialization(
41364142
if (ClassTemplateSpec->isInvalidDecl())
41374143
return true;
41384144

4145+
Sema::RecursiveInstGuard AlreadyInstantiating(
4146+
*this, ClassTemplateSpec, Sema::RecursiveInstGuard::Kind::Template);
4147+
if (AlreadyInstantiating)
4148+
return false;
4149+
41394150
bool HadAvaibilityWarning =
41404151
ShouldDiagnoseAvailabilityOfDecl(ClassTemplateSpec, nullptr, nullptr)
41414152
.first != AR_Available;
@@ -4148,7 +4159,7 @@ bool Sema::InstantiateClassTemplateSpecialization(
41484159
if (!Pattern.isUsable())
41494160
return Pattern.isInvalid();
41504161

4151-
bool Err = InstantiateClass(
4162+
bool Err = InstantiateClassImpl(
41524163
PointOfInstantiation, ClassTemplateSpec, Pattern.get(),
41534164
getTemplateInstantiationArgs(ClassTemplateSpec), TSK, Complain);
41544165

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp

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

5315+
RecursiveInstGuard AlreadyInstantiating(
5316+
*this, Decl, RecursiveInstGuard::Kind::ExceptionSpec);
5317+
if (AlreadyInstantiating) {
5318+
// This exception specification indirectly depends on itself. Reject.
5319+
// FIXME: Corresponding rule in the standard?
5320+
Diag(PointOfInstantiation, diag::err_exception_spec_cycle) << Decl;
5321+
UpdateExceptionSpec(Decl, EST_None);
5322+
return;
5323+
}
5324+
53155325
InstantiatingTemplate Inst(*this, PointOfInstantiation, Decl,
53165326
InstantiatingTemplate::ExceptionSpecification());
53175327
if (Inst.isInvalid()) {
@@ -5320,13 +5330,6 @@ void Sema::InstantiateExceptionSpec(SourceLocation PointOfInstantiation,
53205330
UpdateExceptionSpec(Decl, EST_None);
53215331
return;
53225332
}
5323-
if (Inst.isAlreadyInstantiating()) {
5324-
// This exception specification indirectly depends on itself. Reject.
5325-
// FIXME: Corresponding rule in the standard?
5326-
Diag(PointOfInstantiation, diag::err_exception_spec_cycle) << Decl;
5327-
UpdateExceptionSpec(Decl, EST_None);
5328-
return;
5329-
}
53305333

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

5549+
#ifndef NDEBUG
5550+
RecursiveInstGuard AlreadyInstantiating(*this, Function,
5551+
RecursiveInstGuard::Kind::Template);
5552+
assert(!AlreadyInstantiating && "should have been caught by caller");
5553+
#endif
5554+
55485555
// Find the function body that we'll be substituting.
55495556
const FunctionDecl *PatternDecl = Function->getTemplateInstantiationPattern();
55505557
assert(PatternDecl && "instantiating a non-template");
@@ -5684,7 +5691,7 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
56845691
}
56855692

56865693
InstantiatingTemplate Inst(*this, PointOfInstantiation, Function);
5687-
if (Inst.isInvalid() || Inst.isAlreadyInstantiating())
5694+
if (Inst.isInvalid())
56885695
return;
56895696
PrettyDeclStackTraceEntry CrashInfo(Context, Function, SourceLocation(),
56905697
"instantiating function definition");
@@ -6253,6 +6260,11 @@ void Sema::InstantiateVariableDefinition(SourceLocation PointOfInstantiation,
62536260
if (TSK == TSK_ExplicitSpecialization)
62546261
return;
62556262

6263+
RecursiveInstGuard AlreadyInstantiating(*this, Var,
6264+
RecursiveInstGuard::Kind::Template);
6265+
if (AlreadyInstantiating)
6266+
return;
6267+
62566268
// Find the pattern and the arguments to substitute into it.
62576269
VarDecl *PatternDecl = Var->getTemplateInstantiationPattern();
62586270
assert(PatternDecl && "no pattern for templated variable");
@@ -6276,7 +6288,7 @@ void Sema::InstantiateVariableDefinition(SourceLocation PointOfInstantiation,
62766288
// FIXME: Factor out the duplicated instantiation context setup/tear down
62776289
// code here.
62786290
InstantiatingTemplate Inst(*this, PointOfInstantiation, Var);
6279-
if (Inst.isInvalid() || Inst.isAlreadyInstantiating())
6291+
if (Inst.isInvalid())
62806292
return;
62816293
PrettyDeclStackTraceEntry CrashInfo(Context, Var, SourceLocation(),
62826294
"instantiating variable initializer");
@@ -6380,7 +6392,7 @@ void Sema::InstantiateVariableDefinition(SourceLocation PointOfInstantiation,
63806392
}
63816393

63826394
InstantiatingTemplate Inst(*this, PointOfInstantiation, Var);
6383-
if (Inst.isInvalid() || Inst.isAlreadyInstantiating())
6395+
if (Inst.isInvalid())
63846396
return;
63856397
PrettyDeclStackTraceEntry CrashInfo(Context, Var, SourceLocation(),
63866398
"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

0 commit comments

Comments
 (0)