From: Eli Friedman Date: Thu, 2 Feb 2012 23:15:15 +0000 (+0000) Subject: Add some code to accurately perform odr-used marking for variables per the C++11... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=d2cce136878badcee6694b216479d7f1b72a1e68;p=clang Add some code to accurately perform odr-used marking for variables per the C++11 rules. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@149641 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 88a4d0f8e2..9c8802c34f 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -233,6 +233,8 @@ public: /// element type here is ExprWithCleanups::Object. SmallVector ExprCleanupObjects; + llvm::SmallPtrSet MaybeODRUseExprs; + /// \brief Stack containing information about each of the nested /// function, block, and method scopes that are currently active. /// @@ -556,6 +558,8 @@ public: /// this expression evaluation context. unsigned NumCleanupObjects; + llvm::SmallPtrSet SavedMaybeODRUseExprs; + ExpressionEvaluationContextRecord(ExpressionEvaluationContext Context, unsigned NumCleanupObjects, bool ParentNeedsCleanups) @@ -2278,6 +2282,10 @@ public: void MarkDeclRefReferenced(DeclRefExpr *E); void MarkMemberReferenced(MemberExpr *E); + void UpdateMarkingForLValueToRValue(Expr *E); + void CleanupVarDeclMarking(); + + void MarkDeclarationsReferencedInType(SourceLocation Loc, QualType T); void MarkDeclarationsReferencedInExpr(Expr *E); diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 6ace744fc3..8a0e461ec2 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -7299,6 +7299,8 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, assert(ExprCleanupObjects.empty() && "Leftover temporaries in function"); assert(!ExprNeedsCleanups && "Unaccounted cleanups in function"); + assert(MaybeODRUseExprs.empty() && + "Leftover expressions for odr-use checking"); } if (!IsInstantiation) diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 87dc760902..eab1f2e8e9 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -404,6 +404,8 @@ ExprResult Sema::DefaultLvalueConversion(Expr *E) { if (T.hasQualifiers()) T = T.getUnqualifiedType(); + UpdateMarkingForLValueToRValue(E); + ExprResult Res = Owned(ImplicitCastExpr::Create(Context, T, CK_LValueToRValue, E, 0, VK_RValue)); @@ -9433,6 +9435,8 @@ Sema::PushExpressionEvaluationContext(ExpressionEvaluationContext NewContext) { ExprCleanupObjects.size(), ExprNeedsCleanups)); ExprNeedsCleanups = false; + if (!MaybeODRUseExprs.empty()) + std::swap(MaybeODRUseExprs, ExprEvalContexts.back().SavedMaybeODRUseExprs); } void Sema::PopExpressionEvaluationContext() { @@ -9446,10 +9450,14 @@ void Sema::PopExpressionEvaluationContext() { ExprCleanupObjects.erase(ExprCleanupObjects.begin() + Rec.NumCleanupObjects, ExprCleanupObjects.end()); ExprNeedsCleanups = Rec.ParentNeedsCleanups; + CleanupVarDeclMarking(); + std::swap(MaybeODRUseExprs, Rec.SavedMaybeODRUseExprs); // Otherwise, merge the contexts together. } else { ExprNeedsCleanups |= Rec.ParentNeedsCleanups; + MaybeODRUseExprs.insert(Rec.SavedMaybeODRUseExprs.begin(), + Rec.SavedMaybeODRUseExprs.end()); } // Pop the current expression evaluation context off the stack. @@ -9461,6 +9469,7 @@ void Sema::DiscardCleanupsInEvaluationContext() { ExprCleanupObjects.begin() + ExprEvalContexts.back().NumCleanupObjects, ExprCleanupObjects.end()); ExprNeedsCleanups = false; + MaybeODRUseExprs.clear(); } ExprResult Sema::HandleExprEvaluationContextForTypeof(Expr *E) { @@ -9604,16 +9613,62 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func) { Func->setUsed(true); } -/// \brief Mark a variable referenced, and check whether it is odr-used -/// (C++ [basic.def.odr]p2, C99 6.9p3). Note that this should not be -/// used directly for normal expressions referring to VarDecl. -void Sema::MarkVariableReferenced(SourceLocation Loc, VarDecl *Var) { +static void MarkVarDeclODRUsed(Sema &SemaRef, VarDecl *Var, + SourceLocation Loc) { + // Keep track of used but undefined variables. + if (Var->hasDefinition() == VarDecl::DeclarationOnly && + Var->getLinkage() != ExternalLinkage) { + SourceLocation &old = SemaRef.UndefinedInternals[Var->getCanonicalDecl()]; + if (old.isInvalid()) old = Loc; + } + + Var->setUsed(true); +} + +void Sema::UpdateMarkingForLValueToRValue(Expr *E) { + // Per C++11 [basic.def.odr], a variable is odr-used "unless it is + // an object that satisfies the requirements for appearing in a + // constant expression (5.19) and the lvalue-to-rvalue conversion (4.1) + // is immediately applied." This function handles the lvalue-to-rvalue + // conversion part. + MaybeODRUseExprs.erase(E->IgnoreParens()); +} + +void Sema::CleanupVarDeclMarking() { + for (llvm::SmallPtrSetIterator i = MaybeODRUseExprs.begin(), + e = MaybeODRUseExprs.end(); + i != e; ++i) { + VarDecl *Var; + SourceLocation Loc; + if (BlockDeclRefExpr *BDRE = dyn_cast(*i)) { + Var = BDRE->getDecl(); + Loc = BDRE->getLocation(); + } else if (DeclRefExpr *DRE = dyn_cast(*i)) { + Var = cast(DRE->getDecl()); + Loc = DRE->getLocation(); + } else if (MemberExpr *ME = dyn_cast(*i)) { + Var = cast(ME->getMemberDecl()); + Loc = ME->getMemberLoc(); + } else { + llvm_unreachable("Unexpcted expression"); + } + + MarkVarDeclODRUsed(*this, Var, Loc); + } + + MaybeODRUseExprs.clear(); +} + +// Mark a VarDecl referenced, and perform the necessary handling to compute +// odr-uses. +static void DoMarkVarDeclReferenced(Sema &SemaRef, SourceLocation Loc, + VarDecl *Var, Expr *E) { Var->setReferenced(); if (Var->isUsed(false)) return; - if (!IsPotentiallyEvaluatedContext(*this)) + if (!IsPotentiallyEvaluatedContext(SemaRef)) return; // Implicit instantiation of static data members of class templates. @@ -9625,35 +9680,47 @@ void Sema::MarkVariableReferenced(SourceLocation Loc, VarDecl *Var) { MSInfo->getTemplateSpecializationKind()== TSK_ImplicitInstantiation) { MSInfo->setPointOfInstantiation(Loc); // This is a modification of an existing AST node. Notify listeners. - if (ASTMutationListener *L = getASTMutationListener()) + if (ASTMutationListener *L = SemaRef.getASTMutationListener()) L->StaticDataMemberInstantiated(Var); if (Var->isUsableInConstantExpressions()) // Do not defer instantiations of variables which could be used in a // constant expression. - InstantiateStaticDataMemberDefinition(Loc, Var); + SemaRef.InstantiateStaticDataMemberDefinition(Loc, Var); else - PendingInstantiations.push_back(std::make_pair(Var, Loc)); - } - } - - // Keep track of used but undefined variables. We make a hole in - // the warning for static const data members with in-line - // initializers. - // FIXME: The hole we make for static const data members is too wide! - // We need to implement the C++11 rules for odr-used. - if (Var->hasDefinition() == VarDecl::DeclarationOnly - && Var->getLinkage() != ExternalLinkage - && !(Var->isStaticDataMember() && Var->hasInit())) { - SourceLocation &old = UndefinedInternals[Var->getCanonicalDecl()]; - if (old.isInvalid()) old = Loc; - } + SemaRef.PendingInstantiations.push_back(std::make_pair(Var, Loc)); + } + } + + // Per C++11 [basic.def.odr], a variable is odr-used "unless it is + // an object that satisfies the requirements for appearing in a + // constant expression (5.19) and the lvalue-to-rvalue conversion (4.1) + // is immediately applied." We check the first part here, and + // Sema::UpdateMarkingForLValueToRValue deals with the second part. + // Note that we use the C++11 definition everywhere because nothing in + // C++03 depends on whether we get the C++03 version correct. + const VarDecl *DefVD; + if (E && !isa(Var) && + Var->isUsableInConstantExpressions() && + Var->getAnyInitializer(DefVD) && DefVD->checkInitIsICE()) + SemaRef.MaybeODRUseExprs.insert(E); + else + MarkVarDeclODRUsed(SemaRef, Var, Loc); +} - Var->setUsed(true); +/// \brief Mark a variable referenced, and check whether it is odr-used +/// (C++ [basic.def.odr]p2, C99 6.9p3). Note that this should not be +/// used directly for normal expressions referring to VarDecl. +void Sema::MarkVariableReferenced(SourceLocation Loc, VarDecl *Var) { + DoMarkVarDeclReferenced(*this, Loc, Var, 0); } static void MarkExprReferenced(Sema &SemaRef, SourceLocation Loc, Decl *D, Expr *E) { - // TODO: Add special handling for variables + if (VarDecl *Var = dyn_cast(D)) { + DoMarkVarDeclReferenced(SemaRef, Loc, Var, E); + return; + } + SemaRef.MarkAnyDeclReferenced(Loc, D); } @@ -9789,6 +9856,13 @@ namespace { void VisitCXXDefaultArgExpr(CXXDefaultArgExpr *E) { Visit(E->getExpr()); } + + void VisitImplicitCastExpr(ImplicitCastExpr *E) { + Inherited::VisitImplicitCastExpr(E); + + if (E->getCastKind() == CK_LValueToRValue) + S.UpdateMarkingForLValueToRValue(E->getSubExpr()); + } }; } diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 2fe0974bda..6542629d35 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -4232,6 +4232,8 @@ Sema::MaybeCreateExprWithCleanups(ExprResult SubExpr) { Expr *Sema::MaybeCreateExprWithCleanups(Expr *SubExpr) { assert(SubExpr && "sub expression can't be null!"); + CleanupVarDeclMarking(); + unsigned FirstCleanup = ExprEvalContexts.back().NumCleanupObjects; assert(ExprCleanupObjects.size() >= FirstCleanup); assert(ExprNeedsCleanups || ExprCleanupObjects.size() == FirstCleanup); @@ -4251,6 +4253,8 @@ Expr *Sema::MaybeCreateExprWithCleanups(Expr *SubExpr) { Stmt *Sema::MaybeCreateStmtWithCleanups(Stmt *SubStmt) { assert(SubStmt && "sub statement can't be null!"); + CleanupVarDeclMarking(); + if (!ExprNeedsCleanups) return SubStmt; diff --git a/test/SemaCXX/undefined-internal.cpp b/test/SemaCXX/undefined-internal.cpp index e926f18d5f..6fe64b8ced 100644 --- a/test/SemaCXX/undefined-internal.cpp +++ b/test/SemaCXX/undefined-internal.cpp @@ -122,3 +122,48 @@ namespace PR9323 { f(Uncopyable()); // expected-warning {{C++98 requires an accessible copy constructor}} }; } + + +namespace std { class type_info; }; +namespace cxx11_odr_rules { + // Note: the way this test is written isn't really ideal, but there really + // isn't any other way to check that the odr-used logic for constants + // is working without working implicit capture in lambda-expressions. + // (The more accurate used-but-not-defined warning is the only other visible + // effect of accurate odr-used computation.) + // + // Note that the warning in question can trigger in cases some people would + // consider false positives; hopefully that happens rarely in practice. + + namespace { + struct A { + static const int unused = 10; + static const int used1 = 20; // expected-warning {{internal linkage}} + static const int used2 = 20; // expected-warning {{internal linkage}} + virtual ~A() {} + }; + } + + void a(int,int); + A& p(const int&) { static A a; return a; } + + // Check handling of default arguments + void b(int = A::unused); + + void tests() { + // Basic test + a(A::unused, A::unused); + + // Check that nesting an unevaluated or constant-evaluated context does + // the right thing. + a(A::unused, sizeof(int[10])); + + // Check that the checks work with unevaluated contexts + (void)sizeof(p(A::used1)); + (void)typeid(p(A::used1)); // expected-note {{used here}} + + // Misc other testing + a(A::unused, 1 ? A::used2 : A::used2); // expected-note {{used here}} + b(); + } +}