Skip to content
This repository was archived by the owner on Nov 1, 2021. It is now read-only.

Commit 26313ca

Browse files
committed
Revisit PR10177: don't instantiate a variable if it's only referenced in a
dependent context and can't be used in a constant expression. Per C++ [temp.inst]p2, "the instantiation of a static data member does not occur unless the static data member is used in a way that requires the definition to exist". This doesn't /quite/ match that, as we still instantiate static data members that are usable in constant expressions even if the use doesn't require a definition. A followup patch will fix that for both variables and functions. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@291295 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent d362589 commit 26313ca

File tree

5 files changed

+75
-67
lines changed

5 files changed

+75
-67
lines changed

include/clang/Sema/Sema.h

+8
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "clang/AST/NSAPI.h"
2828
#include "clang/AST/PrettyPrinter.h"
2929
#include "clang/AST/TypeLoc.h"
30+
#include "clang/AST/TypeOrdering.h"
3031
#include "clang/Basic/ExpressionTraits.h"
3132
#include "clang/Basic/LangOptions.h"
3233
#include "clang/Basic/Module.h"
@@ -3801,6 +3802,9 @@ class Sema {
38013802
/// variable will have in the given scope.
38023803
QualType getCapturedDeclRefType(VarDecl *Var, SourceLocation Loc);
38033804

3805+
/// Mark all of the declarations referenced within a particular AST node as
3806+
/// referenced. Used when template instantiation instantiates a non-dependent
3807+
/// type -- entities referenced by the type are now referenced.
38043808
void MarkDeclarationsReferencedInType(SourceLocation Loc, QualType T);
38053809
void MarkDeclarationsReferencedInExpr(Expr *E,
38063810
bool SkipLocalVariables = false);
@@ -6877,6 +6881,10 @@ class Sema {
68776881
/// Specializations whose definitions are currently being instantiated.
68786882
llvm::DenseSet<std::pair<Decl *, unsigned>> InstantiatingSpecializations;
68796883

6884+
/// Non-dependent types used in templates that have already been instantiated
6885+
/// by some template instantiation.
6886+
llvm::DenseSet<QualType> InstantiatedNonDependentTypes;
6887+
68806888
/// \brief Extra modules inspected when performing a lookup during a template
68816889
/// instantiation. Computed lazily.
68826890
SmallVector<Module*, 16> ActiveTemplateInstantiationLookupModules;

lib/Sema/SemaExpr.cpp

+46-63
Original file line numberDiff line numberDiff line change
@@ -14122,48 +14122,13 @@ static void DoMarkVarDeclReferenced(Sema &SemaRef, SourceLocation Loc,
1412214122
"Invalid Expr argument to DoMarkVarDeclReferenced");
1412314123
Var->setReferenced();
1412414124

14125-
TemplateSpecializationKind TSK = Var->getTemplateSpecializationKind();
14126-
bool MarkODRUsed = true;
14127-
14128-
// If the context is not potentially evaluated, this is not an odr-use and
14129-
// does not trigger instantiation.
14130-
if (!IsPotentiallyEvaluatedContext(SemaRef)) {
14131-
if (SemaRef.isUnevaluatedContext())
14132-
return;
14133-
14134-
// If we don't yet know whether this context is going to end up being an
14135-
// evaluated context, and we're referencing a variable from an enclosing
14136-
// scope, add a potential capture.
14137-
//
14138-
// FIXME: Is this necessary? These contexts are only used for default
14139-
// arguments, where local variables can't be used.
14140-
const bool RefersToEnclosingScope =
14141-
(SemaRef.CurContext != Var->getDeclContext() &&
14142-
Var->getDeclContext()->isFunctionOrMethod() && Var->hasLocalStorage());
14143-
if (RefersToEnclosingScope) {
14144-
if (LambdaScopeInfo *const LSI =
14145-
SemaRef.getCurLambda(/*IgnoreCapturedRegions=*/true)) {
14146-
// If a variable could potentially be odr-used, defer marking it so
14147-
// until we finish analyzing the full expression for any
14148-
// lvalue-to-rvalue
14149-
// or discarded value conversions that would obviate odr-use.
14150-
// Add it to the list of potential captures that will be analyzed
14151-
// later (ActOnFinishFullExpr) for eventual capture and odr-use marking
14152-
// unless the variable is a reference that was initialized by a constant
14153-
// expression (this will never need to be captured or odr-used).
14154-
assert(E && "Capture variable should be used in an expression.");
14155-
if (!Var->getType()->isReferenceType() ||
14156-
!IsVariableNonDependentAndAConstantExpression(Var, SemaRef.Context))
14157-
LSI->addPotentialCapture(E->IgnoreParens());
14158-
}
14159-
}
14160-
14161-
if (!isTemplateInstantiation(TSK))
14162-
return;
14125+
if (SemaRef.isUnevaluatedContext())
14126+
return;
1416314127

14164-
// Instantiate, but do not mark as odr-used, variable templates.
14165-
MarkODRUsed = false;
14166-
}
14128+
TemplateSpecializationKind TSK = Var->getTemplateSpecializationKind();
14129+
bool MarkODRUsed = IsPotentiallyEvaluatedContext(SemaRef);
14130+
bool NeedDefinition =
14131+
MarkODRUsed || Var->isUsableInConstantExpressions(SemaRef.Context);
1416714132

1416814133
VarTemplateSpecializationDecl *VarSpec =
1416914134
dyn_cast<VarTemplateSpecializationDecl>(Var);
@@ -14173,14 +14138,15 @@ static void DoMarkVarDeclReferenced(Sema &SemaRef, SourceLocation Loc,
1417314138
// If this might be a member specialization of a static data member, check
1417414139
// the specialization is visible. We already did the checks for variable
1417514140
// template specializations when we created them.
14176-
if (TSK != TSK_Undeclared && !isa<VarTemplateSpecializationDecl>(Var))
14141+
if (NeedDefinition && TSK != TSK_Undeclared &&
14142+
!isa<VarTemplateSpecializationDecl>(Var))
1417714143
SemaRef.checkSpecializationVisibility(Loc, Var);
1417814144

1417914145
// Perform implicit instantiation of static data members, static data member
1418014146
// templates of class templates, and variable template specializations. Delay
1418114147
// instantiations of variable templates, except for those that could be used
1418214148
// in a constant expression.
14183-
if (isTemplateInstantiation(TSK)) {
14149+
if (NeedDefinition && isTemplateInstantiation(TSK)) {
1418414150
bool TryInstantiating = TSK == TSK_ImplicitInstantiation;
1418514151

1418614152
if (TryInstantiating && !isa<VarTemplateSpecializationDecl>(Var)) {
@@ -14219,9 +14185,6 @@ static void DoMarkVarDeclReferenced(Sema &SemaRef, SourceLocation Loc,
1421914185
}
1422014186
}
1422114187

14222-
if (!MarkODRUsed)
14223-
return;
14224-
1422514188
// Per C++11 [basic.def.odr], a variable is odr-used "unless it satisfies
1422614189
// the requirements for appearing in a constant expression (5.19) and, if
1422714190
// it is an object, the lvalue-to-rvalue conversion (4.1)
@@ -14230,14 +14193,39 @@ static void DoMarkVarDeclReferenced(Sema &SemaRef, SourceLocation Loc,
1423014193
// Note that we use the C++11 definition everywhere because nothing in
1423114194
// C++03 depends on whether we get the C++03 version correct. The second
1423214195
// part does not apply to references, since they are not objects.
14233-
if (E && IsVariableAConstantExpression(Var, SemaRef.Context)) {
14196+
if (MarkODRUsed && E && IsVariableAConstantExpression(Var, SemaRef.Context)) {
1423414197
// A reference initialized by a constant expression can never be
1423514198
// odr-used, so simply ignore it.
1423614199
if (!Var->getType()->isReferenceType())
1423714200
SemaRef.MaybeODRUseExprs.insert(E);
14238-
} else
14201+
} else if (MarkODRUsed) {
1423914202
MarkVarDeclODRUsed(Var, Loc, SemaRef,
1424014203
/*MaxFunctionScopeIndex ptr*/ nullptr);
14204+
} else {
14205+
// If we don't yet know whether this context is going to end up being an
14206+
// evaluated context, and we're referencing a variable from an enclosing
14207+
// scope, add a potential capture.
14208+
const bool RefersToEnclosingScope =
14209+
(SemaRef.CurContext != Var->getDeclContext() &&
14210+
Var->getDeclContext()->isFunctionOrMethod() && Var->hasLocalStorage());
14211+
if (RefersToEnclosingScope) {
14212+
if (LambdaScopeInfo *const LSI =
14213+
SemaRef.getCurLambda(/*IgnoreCapturedRegions=*/true)) {
14214+
// If a variable could potentially be odr-used, defer marking it so
14215+
// until we finish analyzing the full expression for any
14216+
// lvalue-to-rvalue
14217+
// or discarded value conversions that would obviate odr-use.
14218+
// Add it to the list of potential captures that will be analyzed
14219+
// later (ActOnFinishFullExpr) for eventual capture and odr-use marking
14220+
// unless the variable is a reference that was initialized by a constant
14221+
// expression (this will never need to be captured or odr-used).
14222+
assert(E && "Capture variable should be used in an expression.");
14223+
if (!Var->getType()->isReferenceType() ||
14224+
!IsVariableNonDependentAndAConstantExpression(Var, SemaRef.Context))
14225+
LSI->addPotentialCapture(E->IgnoreParens());
14226+
}
14227+
}
14228+
}
1424114229
}
1424214230

1424314231
/// \brief Mark a variable referenced, and check whether it is odr-used
@@ -14346,33 +14334,28 @@ namespace {
1434614334
MarkReferencedDecls(Sema &S, SourceLocation Loc) : S(S), Loc(Loc) { }
1434714335

1434814336
bool TraverseTemplateArgument(const TemplateArgument &Arg);
14349-
bool TraverseRecordType(RecordType *T);
1435014337
};
1435114338
}
1435214339

1435314340
bool MarkReferencedDecls::TraverseTemplateArgument(
1435414341
const TemplateArgument &Arg) {
14355-
if (Arg.getKind() == TemplateArgument::Declaration) {
14356-
if (Decl *D = Arg.getAsDecl())
14357-
S.MarkAnyDeclReferenced(Loc, D, true);
14342+
{
14343+
// A non-type template argument is a constant-evaluated context.
14344+
EnterExpressionEvaluationContext Evaluated(S, Sema::ConstantEvaluated);
14345+
if (Arg.getKind() == TemplateArgument::Declaration) {
14346+
if (Decl *D = Arg.getAsDecl())
14347+
S.MarkAnyDeclReferenced(Loc, D, true);
14348+
} else if (Arg.getKind() == TemplateArgument::Expression) {
14349+
S.MarkDeclarationsReferencedInExpr(Arg.getAsExpr(), false);
14350+
}
1435814351
}
1435914352

1436014353
return Inherited::TraverseTemplateArgument(Arg);
1436114354
}
1436214355

14363-
bool MarkReferencedDecls::TraverseRecordType(RecordType *T) {
14364-
if (ClassTemplateSpecializationDecl *Spec
14365-
= dyn_cast<ClassTemplateSpecializationDecl>(T->getDecl())) {
14366-
const TemplateArgumentList &Args = Spec->getTemplateArgs();
14367-
return TraverseTemplateArguments(Args.data(), Args.size());
14368-
}
14369-
14370-
return true;
14371-
}
14372-
1437314356
void Sema::MarkDeclarationsReferencedInType(SourceLocation Loc, QualType T) {
1437414357
MarkReferencedDecls Marker(*this, Loc);
14375-
Marker.TraverseType(Context.getCanonicalType(T));
14358+
Marker.TraverseType(T);
1437614359
}
1437714360

1437814361
namespace {

lib/Sema/SemaTemplate.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -5158,6 +5158,11 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
51585158
return Arg;
51595159
}
51605160

5161+
// The initialization of the parameter from the argument is
5162+
// a constant-evaluated context.
5163+
EnterExpressionEvaluationContext ConstantEvaluated(*this,
5164+
Sema::ConstantEvaluated);
5165+
51615166
if (getLangOpts().CPlusPlus1z) {
51625167
// C++1z [temp.arg.nontype]p1:
51635168
// A template-argument for a non-type template parameter shall be

test/SemaCXX/PR10177.cpp

+8-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ void f() {
2424
(void)class_ref<int, int&, U<2>::a>(); // expected-note {{here}}
2525
};
2626

27+
template<typename T>
28+
void not_instantiated() {
29+
// These cases (arguably) do not require instantiation of U<i>::a.
30+
(void)alias_ref<int, int&, U<3>::a>();
31+
(void)func_ref<int, int&, U<4>::a>();
32+
(void)class_ref<int, int&, U<5>::a>();
33+
};
2734

2835
template<int N>
2936
void fi() {
@@ -33,7 +40,7 @@ void fi() {
3340
};
3441

3542
int main() {
36-
f<int>(); // NOTE: Non-dependent name uses are type-checked at template definition time.
43+
f<int>(); // expected-note 3{{here}}
3744
fi<10>(); // expected-note 3{{here}}
3845
}
3946

test/SemaCXX/undefined-internal.cpp

+8-3
Original file line numberDiff line numberDiff line change
@@ -186,10 +186,15 @@ namespace OverloadUse {
186186
namespace {
187187
void f();
188188
void f(int); // expected-warning {{function 'OverloadUse::(anonymous namespace)::f' has internal linkage but is not defined}}
189+
void f(int, int); // expected-warning {{function 'OverloadUse::(anonymous namespace)::f' has internal linkage but is not defined}}
190+
}
191+
template<void x()> void t() { x(); }
192+
template<void x(int)> void t(int*) { x(10); }
193+
template<void x(int, int)> void t(int*, int*) {}
194+
void g(int n) {
195+
t<f>(&n); // expected-note {{used here}}
196+
t<f>(&n, &n); // expected-note {{used here}}
189197
}
190-
template<void x()> void t(int*) { x(); }
191-
template<void x(int)> void t(long*) { x(10); } // expected-note {{used here}}
192-
void g() { long a; t<f>(&a); }
193198
}
194199

195200
namespace test7 {

0 commit comments

Comments
 (0)