From 5016a70c183a50845a0421802d161093dd0643f6 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Sat, 20 Oct 2012 01:38:33 +0000 Subject: [PATCH] DR1472: A reference isn't odr-used if it has preceding initialization, initialized by a reference constant expression. Our odr-use modeling still needs work here: we don't yet implement the 'set of potential results of an expression' DR. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@166361 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/CGExpr.cpp | 20 +++++++++++++++++--- lib/Sema/SemaExpr.cpp | 19 ++++++++++--------- test/CodeGenCXX/const-init-cxx11.cpp | 6 +----- test/CodeGenCXX/for-range.cpp | 2 +- test/CodeGenCXX/lambda-expressions.cpp | 9 +++++++++ test/SemaCXX/lambda-expressions.cpp | 13 ++++++++----- 6 files changed, 46 insertions(+), 23 deletions(-) diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp index 809c1c1517..8af1889a6a 100644 --- a/lib/CodeGen/CGExpr.cpp +++ b/lib/CodeGen/CGExpr.cpp @@ -1695,6 +1695,21 @@ LValue CodeGenFunction::EmitDeclRefLValue(const DeclRefExpr *E) { CharUnits Alignment = getContext().getDeclAlign(ND); QualType T = E->getType(); + // A DeclRefExpr for a reference initialized by a constant expression can + // appear without being odr-used. Directly emit the constant initializer. + if (const VarDecl *VD = dyn_cast(ND)) { + const Expr *Init = VD->getAnyInitializer(VD); + if (Init && !isa(VD) && VD->getType()->isReferenceType() && + VD->isUsableInConstantExpressions(getContext()) && + VD->checkInitIsICE()) { + llvm::Constant *Val = + CGM.EmitConstantValue(*VD->evaluateValue(), VD->getType(), this); + assert(Val && "failed to emit reference constant expression"); + // FIXME: Eventually we will want to emit vector element references. + return MakeAddrLValue(Val, T, Alignment); + } + } + // FIXME: We should be able to assert this for FunctionDecls as well! // FIXME: We should be able to assert this for all DeclRefExprs, not just // those with a valid source location. @@ -1705,7 +1720,7 @@ LValue CodeGenFunction::EmitDeclRefLValue(const DeclRefExpr *E) { if (ND->hasAttr()) { const ValueDecl *VD = cast(ND); llvm::Constant *Aliasee = CGM.GetWeakRefReference(VD); - return MakeAddrLValue(Aliasee, E->getType(), Alignment); + return MakeAddrLValue(Aliasee, T, Alignment); } if (const VarDecl *VD = dyn_cast(ND)) { @@ -1733,9 +1748,8 @@ LValue CodeGenFunction::EmitDeclRefLValue(const DeclRefExpr *E) { } assert(isa(CurCodeDecl) && E->refersToEnclosingLocal()); - CharUnits alignment = getContext().getDeclAlign(VD); return MakeAddrLValue(GetAddrOfBlockDecl(VD, isBlockVariable), - E->getType(), alignment); + T, Alignment); } assert(V && "DeclRefExpr not entered in LocalDeclMap?"); diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index b75df8fe8d..a71da15322 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -11010,20 +11010,21 @@ static void DoMarkVarDeclReferenced(Sema &SemaRef, SourceLocation 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) + // 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) // 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. This does not - // apply to references, since they are not objects. + // 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. const VarDecl *DefVD; - if (E && !isa(Var) && !Var->getType()->isReferenceType() && + if (E && !isa(Var) && Var->isUsableInConstantExpressions(SemaRef.Context) && - Var->getAnyInitializer(DefVD) && DefVD->checkInitIsICE()) - SemaRef.MaybeODRUseExprs.insert(E); - else + Var->getAnyInitializer(DefVD) && DefVD->checkInitIsICE()) { + if (!Var->getType()->isReferenceType()) + SemaRef.MaybeODRUseExprs.insert(E); + } else MarkVarDeclODRUsed(SemaRef, Var, Loc); } diff --git a/test/CodeGenCXX/const-init-cxx11.cpp b/test/CodeGenCXX/const-init-cxx11.cpp index db1bb41260..833adba8ba 100644 --- a/test/CodeGenCXX/const-init-cxx11.cpp +++ b/test/CodeGenCXX/const-init-cxx11.cpp @@ -432,11 +432,7 @@ namespace InitFromConst { // CHECK: call void @_ZN13InitFromConst7consumeIRKNS_1SEEEvT_(%"struct.InitFromConst::S"* @_ZN13InitFromConstL1sE) consume(s); - // FIXME CHECK-NOT: call void @_ZN13InitFromConst7consumeIRKNS_1SEEEvT_(%"struct.InitFromConst::S"* @_ZN13InitFromConstL1sE) - // There's no lvalue-to-rvalue conversion here, so 'r' is odr-used, and - // we're permitted to emit a load of it. This seems likely to be a defect - // in the standard. If we start emitting a direct reference to 's', update - // this test. + // CHECK: call void @_ZN13InitFromConst7consumeIRKNS_1SEEEvT_(%"struct.InitFromConst::S"* @_ZN13InitFromConstL1sE) consume(r); // CHECK: call void @_ZN13InitFromConst7consumeIPKNS_1SEEEvT_(%"struct.InitFromConst::S"* @_ZN13InitFromConstL1sE) diff --git a/test/CodeGenCXX/for-range.cpp b/test/CodeGenCXX/for-range.cpp index 929e33ca96..926fe445a5 100644 --- a/test/CodeGenCXX/for-range.cpp +++ b/test/CodeGenCXX/for-range.cpp @@ -40,7 +40,7 @@ void for_array() { // CHECK-NOT: 5begin // CHECK-NOT: 3end // CHECK: getelementptr {{.*}}, i32 0 - // CHECK: getelementptr {{.*}}, i64 5 + // CHECK: getelementptr {{.*}}, i64 1, i64 0 // CHECK: br label %[[COND:.*]] // CHECK: [[COND]]: diff --git a/test/CodeGenCXX/lambda-expressions.cpp b/test/CodeGenCXX/lambda-expressions.cpp index e872cc494b..cee4f172a0 100644 --- a/test/CodeGenCXX/lambda-expressions.cpp +++ b/test/CodeGenCXX/lambda-expressions.cpp @@ -71,6 +71,15 @@ void f() { int (*fp)(int, int) = [](int x, int y){ return x + y; }; } +static int k; +int g() { + int &r = k; + // CHECK: define internal i32 @"_ZZ1gvENK3$_6clEv"( + // CHECK-NOT: } + // CHECK: load i32* @_ZL1k, + return [] { return r; } (); +}; + // CHECK: define internal i32 @"_ZZ1fvEN3$_58__invokeEii" // CHECK: store i32 // CHECK-NEXT: store i32 diff --git a/test/SemaCXX/lambda-expressions.cpp b/test/SemaCXX/lambda-expressions.cpp index babe743c65..0630aedf77 100644 --- a/test/SemaCXX/lambda-expressions.cpp +++ b/test/SemaCXX/lambda-expressions.cpp @@ -83,12 +83,15 @@ namespace ImplicitCapture { const int h = a; // expected-note {{declared}} []() { return h; }; // expected-error {{variable 'h' cannot be implicitly captured in a lambda with no capture-default specified}} expected-note {{lambda expression begins here}} - // The exemption for variables which can appear in constant expressions - // applies only to objects (and not to references). - // FIXME: This might be a bug in the standard. - static int i; - constexpr int &ref_i = i; // expected-note {{declared}} + // References can appear in constant expressions if they are initialized by + // reference constant expressions. + int i; + int &ref_i = i; // expected-note {{declared}} [] { return ref_i; }; // expected-error {{variable 'ref_i' cannot be implicitly captured in a lambda with no capture-default specified}} expected-note {{lambda expression begins here}} + + static int j; + int &ref_j = j; + [] { return ref_j; }; // ok } } -- 2.40.0