From: Richard Smith Date: Fri, 6 Jan 2017 22:52:53 +0000 (+0000) Subject: Revisit PR10177: don't instantiate a variable if it's only referenced in a X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=26313ca6595d5365a58ec08eab7d51492a4c0033;p=clang 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 --- diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index ca984a360a..83b795ab8d 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -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> InstantiatingSpecializations; + /// Non-dependent types used in templates that have already been instantiated + /// by some template instantiation. + llvm::DenseSet InstantiatedNonDependentTypes; + /// \brief Extra modules inspected when performing a lookup during a template /// instantiation. Computed lazily. SmallVector ActiveTemplateInstantiationLookupModules; diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 1509b22a9e..c287874266 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -14122,48 +14122,13 @@ static void DoMarkVarDeclReferenced(Sema &SemaRef, SourceLocation Loc, "Invalid Expr argument to DoMarkVarDeclReferenced"); Var->setReferenced(); - 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; + if (SemaRef.isUnevaluatedContext()) + return; - // Instantiate, but do not mark as odr-used, variable templates. - MarkODRUsed = false; - } + TemplateSpecializationKind TSK = Var->getTemplateSpecializationKind(); + bool MarkODRUsed = IsPotentiallyEvaluatedContext(SemaRef); + bool NeedDefinition = + MarkODRUsed || Var->isUsableInConstantExpressions(SemaRef.Context); VarTemplateSpecializationDecl *VarSpec = dyn_cast(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(Var)) + if (NeedDefinition && TSK != TSK_Undeclared && + !isa(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(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(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 { diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 66a10ef799..795e6025d9 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -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 diff --git a/test/SemaCXX/PR10177.cpp b/test/SemaCXX/PR10177.cpp index 9286e29351..59630be508 100644 --- a/test/SemaCXX/PR10177.cpp +++ b/test/SemaCXX/PR10177.cpp @@ -24,6 +24,13 @@ void f() { (void)class_ref::a>(); // expected-note {{here}} }; +template +void not_instantiated() { + // These cases (arguably) do not require instantiation of U::a. + (void)alias_ref::a>(); + (void)func_ref::a>(); + (void)class_ref::a>(); +}; template void fi() { @@ -33,7 +40,7 @@ void fi() { }; int main() { - f(); // NOTE: Non-dependent name uses are type-checked at template definition time. + f(); // expected-note 3{{here}} fi<10>(); // expected-note 3{{here}} } diff --git a/test/SemaCXX/undefined-internal.cpp b/test/SemaCXX/undefined-internal.cpp index 59e6fdf9af..32151b71ea 100644 --- a/test/SemaCXX/undefined-internal.cpp +++ b/test/SemaCXX/undefined-internal.cpp @@ -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 t() { x(); } + template void t(int*) { x(10); } + template void t(int*, int*) {} + void g(int n) { + t(&n); // expected-note {{used here}} + t(&n, &n); // expected-note {{used here}} } - template void t(int*) { x(); } - template void t(long*) { x(10); } // expected-note {{used here}} - void g() { long a; t(&a); } } namespace test7 {