forked from OSchip/llvm-project
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:
parent
1fb895e890
commit
e5945871cf
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue