Hi rsmith, rjmccall,

Please see 
http://lists.cs.uiuc.edu/pipermail/cfe-commits/Week-of-Mon-20130708/083442.html 
for context.

(Trying to learn the phabricator interface - is it preferred that patches be 
submitted for review through it?)
thanks!

http://llvm-reviews.chandlerc.com/D1140

Files:
  lib/CodeGen/CGExpr.cpp
  lib/Sema/SemaExpr.cpp
  lib/Sema/SemaExprCXX.cpp
  test/CodeGenCXX/cxx11-DR712-lambda-constexpr.cpp
  test/CodeGenCXX/cxx11-member-ptr-constexpr.cpp
  test/CXX/basic/basic.def.odr/p2-potential-results.cpp
Index: lib/CodeGen/CGExpr.cpp
===================================================================
--- lib/CodeGen/CGExpr.cpp
+++ lib/CodeGen/CGExpr.cpp
@@ -1724,10 +1724,10 @@
   const NamedDecl *ND = E->getDecl();
   CharUnits Alignment = getContext().getDeclAlign(ND);
   QualType T = E->getType();
-
+  const VarDecl *VarD = dyn_cast<VarDecl>(ND);
   // 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<VarDecl>(ND)) {
+  if (const VarDecl *VD = VarD) {
     const Expr *Init = VD->getAnyInitializer(VD);
     if (Init && !isa<ParmVarDecl>(VD) && VD->getType()->isReferenceType() &&
         VD->isUsableInConstantExpressions(getContext()) &&
@@ -1744,16 +1744,17 @@
   // FIXME: We should be able to assert this for all DeclRefExprs, not just
   // those with a valid source location.
   assert((ND->isUsed(false) || !isa<VarDecl>(ND) ||
-          !E->getLocation().isValid()) &&
-         "Should not use decl without marking it used!");
-
+          !E->getLocation().isValid() || 
+          (VarD && VarD->isUsableInConstantExpressions(getContext()))) &&
+         "Should not use non-const decl without marking it used!");
+  
   if (ND->hasAttr<WeakRefAttr>()) {
     const ValueDecl *VD = cast<ValueDecl>(ND);
     llvm::Constant *Aliasee = CGM.GetWeakRefReference(VD);
     return MakeAddrLValue(Aliasee, T, Alignment);
   }
 
-  if (const VarDecl *VD = dyn_cast<VarDecl>(ND)) {
+  if (const VarDecl *VD = VarD) {
     // Check if this is a global variable.
     if (VD->hasLinkage() || VD->isStaticDataMember()) {
       // If it's thread_local, emit a call to its wrapper function instead.
@@ -1776,11 +1777,36 @@
         if (const FieldDecl *FD = CapturedStmtInfo->lookup(VD))
           return EmitCapturedFieldLValue(*this, FD,
                                          CapturedStmtInfo->getContextValue());
-      }
+      }       
+      if (isa<BlockDecl>(CurCodeDecl) && E->refersToEnclosingLocal()) {
+        return MakeAddrLValue(GetAddrOfBlockDecl(VD, isBlockVariable),
+                              T, Alignment);
+      } else {
+        // If the variable declaration was not marked as odr-used, and is not
+        // present in the LocalDeclMap, it could potentially have been 
+        // referred to within a non-odr-use context in a nested 
+        // declaration context (such as within a lambda expression).  
+        // For e.g. consider 'cs' below, which is not captured: 
+        // void test() { 
+        //   struct S { int r; };
+        //   constexpr S cs{3};
+        //   [](bool b) { S s{4}; (b ? s : cs).r; };
+        // }
+        // Force it to behave as an lvalue, by emitting it as a static local
+        // variable (which gets hoisted to global scope).
+        assert(((!VD->isUsed(false) || E->refersToEnclosingLocal()) && 
+                  tryEmitAsConstant(const_cast<DeclRefExpr*>(E)))   &&
+             "Must be either unused, or a non-captured constant expression");
 
-      assert(isa<BlockDecl>(CurCodeDecl) && E->refersToEnclosingLocal());
-      return MakeAddrLValue(GetAddrOfBlockDecl(VD, isBlockVariable),
-                            T, Alignment);
+        V = CGM.getStaticLocalDeclAddress(VD);
+        if (!V) {
+          EmitStaticVarDecl(*VD, llvm::GlobalValue::InternalLinkage);
+          V = CGM.getStaticLocalDeclAddress(VD);
+        }
+        assert(V && 
+          "a constant expression that was not odr-used should be emitted as a static local");
+      } 
+      
     }
 
     assert(V && "DeclRefExpr not entered in LocalDeclMap?");
Index: lib/Sema/SemaExpr.cpp
===================================================================
--- lib/Sema/SemaExpr.cpp
+++ lib/Sema/SemaExpr.cpp
@@ -11559,13 +11559,71 @@
   Var->setUsed(true);
 }
 
+
+namespace {
+struct PotentialResultsSetFinder : 
+  ConstStmtVisitor<PotentialResultsSetFinder> {
+  llvm::SmallPtrSet<Expr*, 2> &MaybeODRUseExprs;
+  ASTContext &Context;
+  PotentialResultsSetFinder(
+      llvm::SmallPtrSet<Expr*, 2>& MaybeODRUseExprs, ASTContext &Context) :
+          MaybeODRUseExprs(MaybeODRUseExprs), Context(Context) { }
+  // C++1y DR712 3.2 para 2
+  // The set of potential results of an expression e is defined as follows:
+
+  // -- If e is an id-expression (5.1.1), the set contains only e.
+  void VisitDeclRefExpr(const DeclRefExpr *E) {
+    MaybeODRUseExprs.erase(const_cast<DeclRefExpr*>(E));
+  }
+  //  -- If e is a class member access expression (5.2.5), the set contains
+  //     the potential results of the object expression.
+  void VisitMemberExpr(const MemberExpr *E) {
+    Expr *ObjectExpression = E->getBase();
+    Visit(ObjectExpression);
+  }
+  // -- If e is a pointer-to-member expression (5.5) whose second operand 
+  //    is a constant expression, the set contains the potential results of 
+  //    the object expression.  
+  void VisitBinPtrMemD(const BinaryOperator *E) {
+    Expr *SecondOperand = E->getRHS();
+    if (SecondOperand->isCXX11ConstantExpr(Context))
+      Visit(E->getLHS());
+  }
+  void VisitBinPtrMemI(const BinaryOperator *E) {
+    VisitBinPtrMemD(E);
+  }
+    
+  // -- If e has the form (e1), the set contains the potential results of e1.
+  void VisitParenExpr(const ParenExpr *E) {
+    Visit(E->getSubExpr());
+  }
+  // -- If e is a glvalue conditional expression (5.16), the set is the 
+  //    union of the sets of potential results of the second and third 
+  //    operands.
+  void VisitConditionalOperator(const ConditionalOperator *CO) {
+    if (CO->isGLValue()) {
+      Visit(CO->getTrueExpr());
+      Visit(CO->getFalseExpr());
+    }
+  }
+
+  // -- If e is a comma expression (5.18), the set contains the potential 
+  //    results of the right operand.
+  void VisitBinComma(const BinaryOperator *E) {
+    Visit(E->getRHS());
+  }
+  // -- Otherwise, the set is empty.
+  void VisitStmt(const Stmt *) { return; }
+};
+}
+
 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());
+  PotentialResultsSetFinder(MaybeODRUseExprs, Context).Visit(E);
 }
 
 ExprResult Sema::ActOnConstantExpression(ExprResult Res) {
Index: lib/Sema/SemaExprCXX.cpp
===================================================================
--- lib/Sema/SemaExprCXX.cpp
+++ lib/Sema/SemaExprCXX.cpp
@@ -5589,9 +5589,9 @@
 
   if (getLangOpts().CPlusPlus)  {
     // The C++11 standard defines the notion of a discarded-value expression;
-    // normally, we don't need to do anything to handle it, but if it is a
-    // volatile lvalue with a special form, we perform an lvalue-to-rvalue
-    // conversion.
+    // normally, we don't need to do any additional conversions to handle it, 
+    // but if it is a volatile lvalue with a special form, we perform an 
+    // lvalue-to-rvalue conversion. 
     if (getLangOpts().CPlusPlus11 && E->isGLValue() &&
         E->getType().isVolatileQualified() &&
         IsSpecialDiscardedValue(E)) {
@@ -5599,6 +5599,11 @@
       if (Res.isInvalid())
         return Owned(E);
       E = Res.take();
+    } else if (getLangOpts().CPlusPlus11 && E->isGLValue()) {
+      // Even if we do not perform an lvalue-to-rvalue conversion, 
+      // we pretend that one was performed when checking for odr-uses
+      // of variables.
+      UpdateMarkingForLValueToRValue(E);
     }
     return Owned(E);
   }
Index: test/CodeGenCXX/cxx11-DR712-lambda-constexpr.cpp
===================================================================
--- test/CodeGenCXX/cxx11-DR712-lambda-constexpr.cpp
+++ test/CodeGenCXX/cxx11-DR712-lambda-constexpr.cpp
@@ -0,0 +1,51 @@
+// RUN: %clang_cc1 %s -std=c++11 -triple x86_64-apple-darwin10 -emit-llvm -o - | FileCheck %s
+
+struct S {
+  char c;
+  int x;
+};
+
+void test_obj_access_in_lambda() {
+  constexpr S cs = {'a', 456};
+  auto L = [](int i) {
+  // CHECK: store i32 456, i32* %i.addr, align 4
+    i = cs.x;
+  };
+  L(3);
+  // CHECK: ret void
+}
+
+
+void test_obj_access_conditional_in_lambda_2() {
+  constexpr S cs = {'a', 456};
+  int i; char c;
+  S s = {c, i};
+  auto L = [s](int i) {
+    //CHECK: %cond-lvalue = phi %struct.S* [ @_ZZ39test_obj_access_conditional_in_lambda_2vE2cs, %cond.true ], [ %1, %cond.false ]
+    //CHECK: %x = getelementptr inbounds %struct.S* %cond-lvalue, i32 0, i32 1
+    i = (i ? cs : s).x;
+  };
+  L(3);
+  // CHECK: ret void
+}
+
+void test_member_ptr_access_in_lambda_3() {
+  constexpr S cs = {'a', 456};
+  constexpr int S::*pmx = &S::x;
+  int i; char c;
+  S s = {c, i};
+  auto L = [s](int i) {
+  //CHECKfv: %cond-lvalue = phi %struct.S* [ @_ZZ34test_member_ptr_access_in_lambda_3vE2cs, %cond.true ], [ %1, %cond.false ]
+  //CHECKfv: %2 = bitcast %struct.S* %cond-lvalue to i8*
+  //CHECKfv: %memptr.offset = getelementptr inbounds i8* %2, i64 4
+  //CHECKfv: %3 = bitcast i8* %memptr.offset to i32*
+  //CHECKfv: %4 = load i32* %3
+  //CHECKfv: store i32 %4, i32* %i.addr, align 4
+    i = (i ? cs : s).*pmx;
+    (cs);
+    0, cs, cs;
+  };
+  L(3);
+  // CHECK: ret void
+}
+
Index: test/CodeGenCXX/cxx11-member-ptr-constexpr.cpp
===================================================================
--- test/CodeGenCXX/cxx11-member-ptr-constexpr.cpp
+++ test/CodeGenCXX/cxx11-member-ptr-constexpr.cpp
@@ -0,0 +1,74 @@
+// RUN: %clang_cc1 %s -std=c++11 -triple x86_64-apple-darwin10 -emit-llvm -o - | FileCheck %s
+struct S {
+  char c;
+  int x;
+};
+
+void test_full_constexpr() {
+  //CHECK:  store i64 4, i64* %cpmx, align 8
+  constexpr int S::*cpmx = &S::x;
+  constexpr S cs = { 'a', 123456 };
+  int i;
+  // This expr is entirely folded to the const 123456
+  //CHECK-NEXT: store i32 123456, i32* %i, align 4
+  i = cs.*cpmx;
+  
+  cs.*cpmx;
+  //CHECK: ret void
+ 
+}
+
+void test_only_obj_is_constexpr() {
+  
+  //CHECK: store i64 4, i64* %pmx, align 8
+  int S::*pmx = &S::x;
+  constexpr S cs = { 'b', 777 };
+  int i;
+  //CHECK-NEXT: %0 = load i64* %pmx, align 8
+  //CHECK-NEXT: %memptr.offset = getelementptr inbounds i8* getelementptr inbounds (%struct.S* @_ZZ26test_only_obj_is_constexprvE2cs, i32 0, i32 0), i64 %0
+  //CHECK-NEXT: %1 = bitcast i8* %memptr.offset to i32*
+  //CHECK-NEXT: %2 = load i32* %1
+  //CHECK-NEXT: store i32 %2, i32* %i, align 4
+  i = cs.*pmx;
+  //CHECK-NEXT: %3 = load i64* %pmx, align 8
+  //CHECK-NEXT: %memptr.offset1 = getelementptr inbounds i8* getelementptr inbounds (%struct.S* @_ZZ26test_only_obj_is_constexprvE2cs, i32 0, i32 0), i64 %3
+  //CHECK-NEXT: %4 = bitcast i8* %memptr.offset1 to i32*
+  cs.*pmx;
+  //CHECK: ret void
+ 
+}
+
+void test_only_pointer_to_mem_is_constexpr() {
+  //CHECK: store i64 4, i64* %cpmx, align 8
+  constexpr int S::*cpmx = &S::x;  
+  int i; char c;
+  //CHECK: %c1 = getelementptr inbounds %struct.S* %s, i32 0, i32 0
+  S s = { c, i };
+  // The constexpr pointer to member gets translated directly into 
+  // a member access for scalars.
+  //CHECK: %x2 = getelementptr inbounds %struct.S* %s, i32 0, i32 1
+  //CHECK: %2 = load i32* %x2, align 4
+  //CHECK: store i32 %2, i32* %i, align 4
+  i = s.*cpmx;
+  
+  // NOTE: The 4 as a constexpr for cpmx
+  //CHECK: %memptr.offset = getelementptr inbounds i8* %3, i64 4
+  s.*cpmx;
+  //CHECK: ret void
+}
+
+void test_obj_and_pointer_to_mem_is_NON_constexpr() {
+  //CHECK: store i64 4, i64* %pmx, align 8
+  int S::*pmx = &S::x;
+  int i; char c;
+  //CHECK:%x = getelementptr inbounds %struct.S* %s, i32 0, i32 1
+  S s = { c, i };
+  //CHECK: %memptr.offset = getelementptr inbounds i8* %3, i64 %2
+  
+  i = s.*pmx;
+  s.*pmx;
+  // CHECK: ret void
+ 
+}
+
+
Index: test/CXX/basic/basic.def.odr/p2-potential-results.cpp
===================================================================
--- test/CXX/basic/basic.def.odr/p2-potential-results.cpp
+++ test/CXX/basic/basic.def.odr/p2-potential-results.cpp
@@ -0,0 +1,195 @@
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s
+
+// C++14 CD (N3690) [basic.def.odr]p2/3:
+// An expression is potentially evaluated unless it is an unevaluated operand 
+// (Clause 5) or a subexpression thereof. The set of potential results of 
+// an expression e is defined as follows:
+// - If e is an id-expression (5.1.1), the set contains only e.
+// - If e is a class member access expression (5.2.5), the set contains 
+// the potential results of the object expression.
+// - If e is a pointer-to-member expression (5.5) whose second operand 
+// is a constant expression, the set contains the potential results of 
+// the object expression.
+// - If e has the form (e1), the set contains the potential results of e1.
+// - If e is a glvalue conditional expression (5.16), the set is the union 
+// of the sets of potential results of the second and third operands.
+// - If e is a comma expression (5.18), the set contains the potential 
+// results of the right operand.
+// - Otherwise, the set is empty.
+
+// 3 A variable x whose name appears as a potentially-evaluated expression 
+// ex is odr-used unless x satisfies the requirements for appearing in a 
+// constant expression (5.19) and, if x is an object, ex is an element of
+// the set of potential results of an expression e, where either the 
+// lvalue-to-rvalue conversion (4.1) is applied to e, or e is a 
+// discarded-value expression (Clause 5).
+
+
+namespace odr_check_through_lambda_capturing {
+
+struct S {
+  const int mi;
+  constexpr S(int i) : mi(i) { }
+  constexpr S(const S& r) : mi(r.mi) { }
+  constexpr int foo() const {
+    return mi;
+  }
+};
+
+struct SS {
+  const S ms;
+  constexpr SS(S s) : ms(s) { }
+};
+
+
+struct AggRS { int x; };
+struct AggRSS { AggRS rs; };
+
+
+int f(const int& i) { return i < 2; }
+void g(int i) { }
+
+void test() {
+  const int x = 10;  
+  const int y = 5;
+  
+  constexpr S r{12};
+  constexpr S r2{r};
+  constexpr S cs{6};
+  S s{8};
+  constexpr SS css{cs};
+  SS ss{s};
+  constexpr const int S::*dmf_outer = &S::mi;
+  
+  constexpr AggRS crs1 = { 1 };
+  constexpr AggRS crs2 = { 2 };
+  constexpr AggRSS crss1 = { crs1 };
+  constexpr AggRSS crss2 = { crs2 };  
+  AggRS rs2 = { 2 };
+  AggRS rs3 = { 3 };
+  AggRSS rss2 = { rs2 };
+  
+  bool k, j, l;
+  int i;
+  i  = (k ? crs1 : rs2).x;
+  i = (k ? (crs1) : (rs2)).x;
+  i = (k ? (j ? (crs1) : (crs2)) : (j ? ((l ? rs2 : crs1)) : rs2)).x;
+  (void) ((k ? (j ? (crs1) : (crs2)) : (j ? ((l ? rs2 : crs1)) : rs2)).x);
+  
+  auto ShouldNotCapture = [rs2,rss2,ss](int i) {
+   // - If e is an id-expression (5.1.1), the set contains only e.
+    x;             //expected-warning{{expression result unused}}
+    cs;             //expected-warning{{expression result unused}}
+	  g(x);
+    (void) crs1;
+    (void) css;
+    
+    // - If e is a comma expression (5.18), the set contains the 
+    //   potential results of the right operand.
+    0, x, 0, g(x); //expected-warning2{{expression result unused}}
+    0, x, i; 		//expected-warning3{{expression result unused}}
+    0, x; 			//expected-warning2{{expression result unused}}
+    0, cs; 			 //expected-warning2{{expression result unused}}
+    0, x, cs, r, r2, css; //expected-warning6{{expression result unused}}
+    x, cs, i = 2; //expected-warning2{{expression result unused}}
+        
+    // - If e is a glvalue conditional expression (5.16), the set 
+    //   is the union of the sets of potential results of the
+    //   second and third operands.
+    f(i) ? x : y; //expected-warning{{expression result unused}}
+    g(f(i) ? x : y); 
+    f(i) ? cs : r; //expected-warning{{expression result unused}}
+    
+    // - If e is a class member access expression (5.2.5), the set 
+    //   contains the potential results of the object
+    //   expression.
+    i = cs.mi;
+    i = css.ms.mi;
+    cs.mi;          //expected-warning{{expression result unused}}
+    g(cs.mi);     
+    css.ms.mi;      //expected-warning{{expression result unused}}
+    g(css.ms.mi);
+    css.ms;       //expected-warning{{expression result unused}}
+    (void)((f(i) ? crs1 : rs2).x);  
+    bool j, k, l;
+    i = (k ? (j ? (crs1) : (crs2)) : (j ? ((l ? rs2 : crs1)) : rs2)).x;
+    i = (k ? (j ? (crss1) : (rss2)).rs : (j ? ((l ? crss1 : crss2).rs) : rs2)).x;
+    (void)((k ? (j ? (crss1) : (rss2)).rs : (j ? ((l ? crss1 : crss2).rs) : rs2)).x);
+    (void)((k ? (j ? (crs1) : (crs2)) : (j ? ((l ? rs2 : crs1)) : rs2)).x);
+   
+    // - If e is a pointer-to-member expression (5.5) whose second operand is a constant expression, the set
+    //   contains the potential results of the object expression.
+    constexpr const int S::*dmf = &S::mi;
+    i = cs.*dmf;
+    i = cs.*dmf_outer;
+    cs.*dmf_outer;   //expected-warning{{expression result unused}}
+    
+    constexpr const S SS::*smf = &SS::ms;
+    (void)(css.*smf);
+    i = ((css.*smf).*dmf);
+    i = css.ms.mi;
+    i = (j ? (k ? css : (ss)).*smf : cs).mi;
+    (void)((cs.*dmf)); 
+    constexpr int AggRS::*pmx = &AggRS::x;
+    i = (k ? (j ? (crs1) : (crs2)) : (j ? ((l ? rs2 : crs1)) : rs2)).*pmx;
+    i = (k ? (j ? (crss1) : (rss2)).rs : (j ? ((l ? crss1 : crss2).rs) : rs2)).*pmx;
+    (void)((k ? (j ? (crss1) : (rss2)).rs : (j ? ((l ? crss1 : crss2).rs) : rs2)).*pmx);
+    (void) ((k ? (j ? (crs1) : (crs2)) : (j ? ((l ? rs2 : crs1)) : rs2)).*pmx);  
+    // - If e has the form (e1), the set contains the potential results of e1.
+    (void)((x));
+    (void)(cs);
+    (void)(x,cs); //expected-warning{{expression result unused}}
+    (void)(f(i) ? x : y);
+    (void)(f(i) ? g(x) : g(y));
+    (void)(f(i) ? cs : r2);
+    (void) cs.mi;
+    (void) css.ms;
+    
+        
+    return 1;
+  }('4');
+  {
+    struct SS {
+      S ms;
+      constexpr SS(const S& s) : ms{s} { }
+    };
+    constexpr S s{2}; //expected-note2{{'s' declared here}}
+    constexpr SS ss{s}; //expected-note{{'ss' declared here}}
+    constexpr int (S::*pmf)() const = &S::foo;
+	  (s.*pmf)();
+	  const int x = 5; //expected-note{{'x' declared here}}
+    
+	  auto ShouldCapture = [](int i) { //expected-note4{{lambda expression begins here}}
+      s.foo(); //expected-error{{variable 's' cannot be implicitly captured}}
+		  const S SS::*smf_nonconst = &SS::ms;
+      ss.*smf_nonconst; //expected-error{{variable 'ss' cannot be implicitly captured}} \
+                        //expected-warning{{expression result unused}} <-- should not be necessary
+      SS ss(s);  //expected-error{{variable 's' cannot be implicitly captured}}
+      f(x); //expected-error{{variable 'x' cannot be implicitly captured}}
+      return 3;
+	  }(4);
+  }
+} // end test()
+} // end const_member_variables
+
+namespace RS_examples {
+  struct S { static const int n = 0; };
+  void doit(bool b, int k) { b ? k : S::n; } //expected-warning {{expression result unused}}
+  
+  void f() {
+    const thread_local int n = 0;
+    [] { return n; };  //expected-warning {{expression result unused}}
+  }
+  struct X { int n; };
+  X f1() {
+    constexpr X x = { 0 };
+    [] { return x.n; } ();
+    return x;
+  }
+  X g1() {
+    constexpr X x = { 0 };
+    X x2 = x;
+    [] { return x.n; } ();
+    return x;
+  }
+}
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to