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.

llvm-svn: 291295
This commit is contained in:
Richard Smith 2017-01-06 22:52:53 +00:00
parent 1fb895e890
commit e5945871cf
5 changed files with 75 additions and 67 deletions

View File

@ -27,6 +27,7 @@
#include "clang/AST/NSAPI.h"
#include "clang/AST/PrettyPrinter.h"
#include "clang/AST/TypeLoc.h"
#include "clang/AST/TypeOrdering.h"
#include "clang/Basic/ExpressionTraits.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/Module.h"
@ -3801,6 +3802,9 @@ public:
/// variable will have in the given scope.
QualType getCapturedDeclRefType(VarDecl *Var, SourceLocation Loc);
/// Mark all of the declarations referenced within a particular AST node as
/// referenced. Used when template instantiation instantiates a non-dependent
/// type -- entities referenced by the type are now referenced.
void MarkDeclarationsReferencedInType(SourceLocation Loc, QualType T);
void MarkDeclarationsReferencedInExpr(Expr *E,
bool SkipLocalVariables = false);
@ -6877,6 +6881,10 @@ public:
/// Specializations whose definitions are currently being instantiated.
llvm::DenseSet<std::pair<Decl *, unsigned>> InstantiatingSpecializations;
/// Non-dependent types used in templates that have already been instantiated
/// by some template instantiation.
llvm::DenseSet<QualType> InstantiatedNonDependentTypes;
/// \brief Extra modules inspected when performing a lookup during a template
/// instantiation. Computed lazily.
SmallVector<Module*, 16> ActiveTemplateInstantiationLookupModules;

View File

@ -14122,48 +14122,13 @@ static void DoMarkVarDeclReferenced(Sema &SemaRef, SourceLocation Loc,
"Invalid Expr argument to DoMarkVarDeclReferenced");
Var->setReferenced();
if (SemaRef.isUnevaluatedContext())
return;
TemplateSpecializationKind TSK = Var->getTemplateSpecializationKind();
bool MarkODRUsed = true;
// If the context is not potentially evaluated, this is not an odr-use and
// does not trigger instantiation.
if (!IsPotentiallyEvaluatedContext(SemaRef)) {
if (SemaRef.isUnevaluatedContext())
return;
// If we don't yet know whether this context is going to end up being an
// evaluated context, and we're referencing a variable from an enclosing
// scope, add a potential capture.
//
// FIXME: Is this necessary? These contexts are only used for default
// arguments, where local variables can't be used.
const bool RefersToEnclosingScope =
(SemaRef.CurContext != Var->getDeclContext() &&
Var->getDeclContext()->isFunctionOrMethod() && Var->hasLocalStorage());
if (RefersToEnclosingScope) {
if (LambdaScopeInfo *const LSI =
SemaRef.getCurLambda(/*IgnoreCapturedRegions=*/true)) {
// If a variable could potentially be odr-used, defer marking it so
// until we finish analyzing the full expression for any
// lvalue-to-rvalue
// or discarded value conversions that would obviate odr-use.
// Add it to the list of potential captures that will be analyzed
// later (ActOnFinishFullExpr) for eventual capture and odr-use marking
// unless the variable is a reference that was initialized by a constant
// expression (this will never need to be captured or odr-used).
assert(E && "Capture variable should be used in an expression.");
if (!Var->getType()->isReferenceType() ||
!IsVariableNonDependentAndAConstantExpression(Var, SemaRef.Context))
LSI->addPotentialCapture(E->IgnoreParens());
}
}
if (!isTemplateInstantiation(TSK))
return;
// Instantiate, but do not mark as odr-used, variable templates.
MarkODRUsed = false;
}
bool MarkODRUsed = IsPotentiallyEvaluatedContext(SemaRef);
bool NeedDefinition =
MarkODRUsed || Var->isUsableInConstantExpressions(SemaRef.Context);
VarTemplateSpecializationDecl *VarSpec =
dyn_cast<VarTemplateSpecializationDecl>(Var);
@ -14173,14 +14138,15 @@ static void DoMarkVarDeclReferenced(Sema &SemaRef, SourceLocation Loc,
// If this might be a member specialization of a static data member, check
// the specialization is visible. We already did the checks for variable
// template specializations when we created them.
if (TSK != TSK_Undeclared && !isa<VarTemplateSpecializationDecl>(Var))
if (NeedDefinition && TSK != TSK_Undeclared &&
!isa<VarTemplateSpecializationDecl>(Var))
SemaRef.checkSpecializationVisibility(Loc, Var);
// Perform implicit instantiation of static data members, static data member
// templates of class templates, and variable template specializations. Delay
// instantiations of variable templates, except for those that could be used
// in a constant expression.
if (isTemplateInstantiation(TSK)) {
if (NeedDefinition && isTemplateInstantiation(TSK)) {
bool TryInstantiating = TSK == TSK_ImplicitInstantiation;
if (TryInstantiating && !isa<VarTemplateSpecializationDecl>(Var)) {
@ -14219,9 +14185,6 @@ static void DoMarkVarDeclReferenced(Sema &SemaRef, SourceLocation Loc,
}
}
if (!MarkODRUsed)
return;
// Per C++11 [basic.def.odr], a variable is odr-used "unless it satisfies
// the requirements for appearing in a constant expression (5.19) and, if
// it is an object, the lvalue-to-rvalue conversion (4.1)
@ -14230,14 +14193,39 @@ static void DoMarkVarDeclReferenced(Sema &SemaRef, SourceLocation Loc,
// Note that we use the C++11 definition everywhere because nothing in
// C++03 depends on whether we get the C++03 version correct. The second
// part does not apply to references, since they are not objects.
if (E && IsVariableAConstantExpression(Var, SemaRef.Context)) {
if (MarkODRUsed && E && IsVariableAConstantExpression(Var, SemaRef.Context)) {
// A reference initialized by a constant expression can never be
// odr-used, so simply ignore it.
if (!Var->getType()->isReferenceType())
SemaRef.MaybeODRUseExprs.insert(E);
} else
} else if (MarkODRUsed) {
MarkVarDeclODRUsed(Var, Loc, SemaRef,
/*MaxFunctionScopeIndex ptr*/ nullptr);
} else {
// If we don't yet know whether this context is going to end up being an
// evaluated context, and we're referencing a variable from an enclosing
// scope, add a potential capture.
const bool RefersToEnclosingScope =
(SemaRef.CurContext != Var->getDeclContext() &&
Var->getDeclContext()->isFunctionOrMethod() && Var->hasLocalStorage());
if (RefersToEnclosingScope) {
if (LambdaScopeInfo *const LSI =
SemaRef.getCurLambda(/*IgnoreCapturedRegions=*/true)) {
// If a variable could potentially be odr-used, defer marking it so
// until we finish analyzing the full expression for any
// lvalue-to-rvalue
// or discarded value conversions that would obviate odr-use.
// Add it to the list of potential captures that will be analyzed
// later (ActOnFinishFullExpr) for eventual capture and odr-use marking
// unless the variable is a reference that was initialized by a constant
// expression (this will never need to be captured or odr-used).
assert(E && "Capture variable should be used in an expression.");
if (!Var->getType()->isReferenceType() ||
!IsVariableNonDependentAndAConstantExpression(Var, SemaRef.Context))
LSI->addPotentialCapture(E->IgnoreParens());
}
}
}
}
/// \brief Mark a variable referenced, and check whether it is odr-used
@ -14346,33 +14334,28 @@ namespace {
MarkReferencedDecls(Sema &S, SourceLocation Loc) : S(S), Loc(Loc) { }
bool TraverseTemplateArgument(const TemplateArgument &Arg);
bool TraverseRecordType(RecordType *T);
};
}
bool MarkReferencedDecls::TraverseTemplateArgument(
const TemplateArgument &Arg) {
if (Arg.getKind() == TemplateArgument::Declaration) {
if (Decl *D = Arg.getAsDecl())
S.MarkAnyDeclReferenced(Loc, D, true);
{
// A non-type template argument is a constant-evaluated context.
EnterExpressionEvaluationContext Evaluated(S, Sema::ConstantEvaluated);
if (Arg.getKind() == TemplateArgument::Declaration) {
if (Decl *D = Arg.getAsDecl())
S.MarkAnyDeclReferenced(Loc, D, true);
} else if (Arg.getKind() == TemplateArgument::Expression) {
S.MarkDeclarationsReferencedInExpr(Arg.getAsExpr(), false);
}
}
return Inherited::TraverseTemplateArgument(Arg);
}
bool MarkReferencedDecls::TraverseRecordType(RecordType *T) {
if (ClassTemplateSpecializationDecl *Spec
= dyn_cast<ClassTemplateSpecializationDecl>(T->getDecl())) {
const TemplateArgumentList &Args = Spec->getTemplateArgs();
return TraverseTemplateArguments(Args.data(), Args.size());
}
return true;
}
void Sema::MarkDeclarationsReferencedInType(SourceLocation Loc, QualType T) {
MarkReferencedDecls Marker(*this, Loc);
Marker.TraverseType(Context.getCanonicalType(T));
Marker.TraverseType(T);
}
namespace {

View File

@ -5158,6 +5158,11 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
return Arg;
}
// The initialization of the parameter from the argument is
// a constant-evaluated context.
EnterExpressionEvaluationContext ConstantEvaluated(*this,
Sema::ConstantEvaluated);
if (getLangOpts().CPlusPlus1z) {
// C++1z [temp.arg.nontype]p1:
// A template-argument for a non-type template parameter shall be

View File

@ -24,6 +24,13 @@ void f() {
(void)class_ref<int, int&, U<2>::a>(); // expected-note {{here}}
};
template<typename T>
void not_instantiated() {
// These cases (arguably) do not require instantiation of U<i>::a.
(void)alias_ref<int, int&, U<3>::a>();
(void)func_ref<int, int&, U<4>::a>();
(void)class_ref<int, int&, U<5>::a>();
};
template<int N>
void fi() {
@ -33,7 +40,7 @@ void fi() {
};
int main() {
f<int>(); // NOTE: Non-dependent name uses are type-checked at template definition time.
f<int>(); // expected-note 3{{here}}
fi<10>(); // expected-note 3{{here}}
}

View File

@ -186,10 +186,15 @@ namespace OverloadUse {
namespace {
void f();
void f(int); // expected-warning {{function 'OverloadUse::(anonymous namespace)::f' has internal linkage but is not defined}}
void f(int, int); // expected-warning {{function 'OverloadUse::(anonymous namespace)::f' has internal linkage but is not defined}}
}
template<void x()> void t() { x(); }
template<void x(int)> void t(int*) { x(10); }
template<void x(int, int)> void t(int*, int*) {}
void g(int n) {
t<f>(&n); // expected-note {{used here}}
t<f>(&n, &n); // expected-note {{used here}}
}
template<void x()> void t(int*) { x(); }
template<void x(int)> void t(long*) { x(10); } // expected-note {{used here}}
void g() { long a; t<f>(&a); }
}
namespace test7 {