r200189 - PR17052 / DR1560 (+DR1550): In a conditional expression between a glvalue and a

Richard Smith richard-llvm at metafoo.co.uk
Sun Jan 26 20:19:57 PST 2014


Author: rsmith
Date: Sun Jan 26 22:19:56 2014
New Revision: 200189

URL: http://llvm.org/viewvc/llvm-project?rev=200189&view=rev
Log:
PR17052 / DR1560 (+DR1550): In a conditional expression between a glvalue and a
throw-expression, the result is also a glvalue and isn't unnecessarily coerced
to a prvalue.

Added:
    cfe/trunk/test/CXX/drs/dr15xx.cpp
Modified:
    cfe/trunk/lib/AST/ExprClassification.cpp
    cfe/trunk/lib/Sema/SemaChecking.cpp
    cfe/trunk/lib/Sema/SemaExprCXX.cpp
    cfe/trunk/test/CodeGenCXX/throw-expressions.cpp
    cfe/trunk/test/SemaCXX/conditional-expr.cpp
    cfe/trunk/test/SemaCXX/expression-traits.cpp
    cfe/trunk/www/cxx_dr_status.html

Modified: cfe/trunk/lib/AST/ExprClassification.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprClassification.cpp?rev=200189&r1=200188&r2=200189&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ExprClassification.cpp (original)
+++ cfe/trunk/lib/AST/ExprClassification.cpp Sun Jan 26 22:19:56 2014
@@ -541,10 +541,21 @@ static Cl::Kinds ClassifyConditional(AST
          "This is only relevant for C++.");
 
   // C++ [expr.cond]p2
-  //   If either the second or the third operand has type (cv) void, [...]
-  //   the result [...] is a prvalue.
-  if (True->getType()->isVoidType() || False->getType()->isVoidType())
+  //   If either the second or the third operand has type (cv) void,
+  //   one of the following shall hold:
+  if (True->getType()->isVoidType() || False->getType()->isVoidType()) {
+    // The second or the third operand (but not both) is a (possibly
+    // parenthesized) throw-expression; the result is of the [...] value
+    // category of the other.
+    bool TrueIsThrow = isa<CXXThrowExpr>(True->IgnoreParenImpCasts());
+    bool FalseIsThrow = isa<CXXThrowExpr>(False->IgnoreParenImpCasts());
+    if (const Expr *NonThrow = TrueIsThrow ? (FalseIsThrow ? 0    : False)
+                                           : (FalseIsThrow ? True : 0))
+      return ClassifyInternal(Ctx, NonThrow);
+
+    //   [Otherwise] the result [...] is a prvalue.
     return Cl::CL_PRValue;
+  }
 
   // Note that at this point, we have already performed all conversions
   // according to [expr.cond]p3.

Modified: cfe/trunk/lib/Sema/SemaChecking.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaChecking.cpp?rev=200189&r1=200188&r2=200189&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaChecking.cpp (original)
+++ cfe/trunk/lib/Sema/SemaChecking.cpp Sun Jan 26 22:19:56 2014
@@ -4185,20 +4185,21 @@ static Expr *EvalAddr(Expr *E, SmallVect
     ConditionalOperator *C = cast<ConditionalOperator>(E);
 
     // Handle the GNU extension for missing LHS.
-    if (Expr *lhsExpr = C->getLHS()) {
-    // In C++, we can have a throw-expression, which has 'void' type.
-      if (!lhsExpr->getType()->isVoidType())
-        if (Expr* LHS = EvalAddr(lhsExpr, refVars, ParentDecl))
+    // FIXME: That isn't a ConditionalOperator, so doesn't get here.
+    if (Expr *LHSExpr = C->getLHS()) {
+      // In C++, we can have a throw-expression, which has 'void' type.
+      if (!LHSExpr->getType()->isVoidType())
+        if (Expr *LHS = EvalAddr(LHSExpr, refVars, ParentDecl))
           return LHS;
     }
 
     // In C++, we can have a throw-expression, which has 'void' type.
     if (C->getRHS()->getType()->isVoidType())
-      return NULL;
+      return 0;
 
     return EvalAddr(C->getRHS(), refVars, ParentDecl);
   }
-  
+
   case Stmt::BlockExprClass:
     if (cast<BlockExpr>(E)->getBlockDecl()->hasCaptures())
       return E; // local block.
@@ -4338,9 +4339,16 @@ do {
     ConditionalOperator *C = cast<ConditionalOperator>(E);
 
     // Handle the GNU extension for missing LHS.
-    if (Expr *lhsExpr = C->getLHS())
-      if (Expr *LHS = EvalVal(lhsExpr, refVars, ParentDecl))
-        return LHS;
+    if (Expr *LHSExpr = C->getLHS()) {
+      // In C++, we can have a throw-expression, which has 'void' type.
+      if (!LHSExpr->getType()->isVoidType())
+        if (Expr *LHS = EvalVal(LHSExpr, refVars, ParentDecl))
+          return LHS;
+    }
+
+    // In C++, we can have a throw-expression, which has 'void' type.
+    if (C->getRHS()->getType()->isVoidType())
+      return 0;
 
     return EvalVal(C->getRHS(), refVars, ParentDecl);
   }

Modified: cfe/trunk/lib/Sema/SemaExprCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprCXX.cpp?rev=200189&r1=200188&r2=200189&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExprCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExprCXX.cpp Sun Jan 26 22:19:56 2014
@@ -4399,42 +4399,21 @@ QualType Sema::CXXCheckConditionalOperan
   bool LVoid = LTy->isVoidType();
   bool RVoid = RTy->isVoidType();
   if (LVoid || RVoid) {
-    //   ... then the [l2r] conversions are performed on the second and third
-    //   operands ...
-    LHS = DefaultFunctionArrayLvalueConversion(LHS.take());
-    RHS = DefaultFunctionArrayLvalueConversion(RHS.take());
-    if (LHS.isInvalid() || RHS.isInvalid())
-      return QualType();
-
-    // Finish off the lvalue-to-rvalue conversion by copy-initializing a
-    // temporary if necessary. DefaultFunctionArrayLvalueConversion doesn't
-    // do this part for us.
-    ExprResult &NonVoid = LVoid ? RHS : LHS;
-    if (NonVoid.get()->getType()->isRecordType() &&
-        NonVoid.get()->isGLValue()) {
-      if (RequireNonAbstractType(QuestionLoc, NonVoid.get()->getType(),
-                             diag::err_allocation_of_abstract_type))
-        return QualType();
-      InitializedEntity Entity =
-          InitializedEntity::InitializeTemporary(NonVoid.get()->getType());
-      NonVoid = PerformCopyInitialization(Entity, SourceLocation(), NonVoid);
-      if (NonVoid.isInvalid())
-        return QualType();
+    //   ... one of the following shall hold:
+    //   -- The second or the third operand (but not both) is a (possibly
+    //      parenthesized) throw-expression; the result is of the type
+    //      and value category of the other.
+    bool LThrow = isa<CXXThrowExpr>(LHS.get()->IgnoreParenImpCasts());
+    bool RThrow = isa<CXXThrowExpr>(RHS.get()->IgnoreParenImpCasts());
+    if (LThrow != RThrow) {
+      Expr *NonThrow = LThrow ? RHS.get() : LHS.get();
+      VK = NonThrow->getValueKind();
+      // DR (no number yet): the result is a bit-field if the
+      // non-throw-expression operand is a bit-field.
+      OK = NonThrow->getObjectKind();
+      return NonThrow->getType();
     }
 
-    LTy = LHS.get()->getType();
-    RTy = RHS.get()->getType();
-
-    //   ... and one of the following shall hold:
-    //   -- The second or the third operand (but not both) is a throw-
-    //      expression; the result is of the type of the other and is a prvalue.
-    bool LThrow = isa<CXXThrowExpr>(LHS.get()->IgnoreParenCasts());
-    bool RThrow = isa<CXXThrowExpr>(RHS.get()->IgnoreParenCasts());
-    if (LThrow && !RThrow)
-      return RTy;
-    if (RThrow && !LThrow)
-      return LTy;
-
     //   -- Both the second and third operands have type void; the result is of
     //      type void and is a prvalue.
     if (LVoid && RVoid)

Added: cfe/trunk/test/CXX/drs/dr15xx.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/drs/dr15xx.cpp?rev=200189&view=auto
==============================================================================
--- cfe/trunk/test/CXX/drs/dr15xx.cpp (added)
+++ cfe/trunk/test/CXX/drs/dr15xx.cpp Sun Jan 26 22:19:56 2014
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -std=c++98 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++1y -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
+
+// expected-no-diagnostics
+
+namespace DR1550 { // dr1550: yes
+  int f(bool b, int n) {
+    return (b ? (throw 0) : n) + (b ? n : (throw 0));
+  }
+}
+
+namespace DR1560 { // dr1560: 3.5
+  void f(bool b, int n) {
+    (b ? throw 0 : n) = (b ? n : throw 0) = 0;
+  }
+  class X { X(const X&); };
+  const X &get();
+  const X &x = true ? get() : throw 0;
+}

Modified: cfe/trunk/test/CodeGenCXX/throw-expressions.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/throw-expressions.cpp?rev=200189&r1=200188&r2=200189&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCXX/throw-expressions.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/throw-expressions.cpp Sun Jan 26 22:19:56 2014
@@ -67,3 +67,16 @@ int test6(bool x, bool y, int z) {
 //
 // end:
 // CHECK: ret i32
+
+namespace DR1560 {
+  struct A {
+    ~A();
+  };
+  extern bool b;
+  A get();
+  // CHECK-LABEL: @_ZN6DR15601bE
+  const A &r = b ? get() : throw 0;
+  // CHECK-NOT: call {{.*}}@_ZN6DR15601AD1Ev
+  // CHECK: call {{.*}} @__cxa_atexit({{.*}} @_ZN6DR15601AD1Ev {{.*}} @_ZGRN6DR15601rE
+  // CHECK-NOT: call {{.*}}@_ZN6DR15601AD1Ev
+}

Modified: cfe/trunk/test/SemaCXX/conditional-expr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/conditional-expr.cpp?rev=200189&r1=200188&r2=200189&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/conditional-expr.cpp (original)
+++ cfe/trunk/test/SemaCXX/conditional-expr.cpp Sun Jan 26 22:19:56 2014
@@ -75,6 +75,7 @@ void test()
   int i1 = ToBool() ? 0 : 1;
 
   // p2 (one or both void, and throwing)
+  Fields flds;
   i1 ? throw 0 : throw 1;
   i1 ? test() : throw 1;
   i1 ? throw 0 : test();
@@ -85,8 +86,16 @@ void test()
   i1 = i1 ? 0 : (throw 0);
   i1 ? 0 : test(); // expected-error {{right operand to ? is void, but left operand is of type 'int'}}
   i1 ? test() : 0; // expected-error {{left operand to ? is void, but right operand is of type 'int'}}
-  (i1 ? throw 0 : i1) = 0; // expected-error {{expression is not assignable}}
-  (i1 ? i1 : throw 0) = 0; // expected-error {{expression is not assignable}}
+  (i1 ? throw 0 : i1) = 0;
+  (i1 ? i1 : throw 0) = 0;
+  (i1 ? (throw 0) : i1) = 0;
+  (i1 ? i1 : (throw 0)) = 0;
+  (i1 ? (void)(throw 0) : i1) = 0; // expected-error {{left operand to ? is void, but right operand is of type 'int'}}
+  (i1 ? i1 : (void)(throw 0)) = 0; // expected-error {{right operand to ? is void, but left operand is of type 'int'}}
+  int &throwRef1 = (i1 ? flds.i1 : throw 0);
+  int &throwRef2 = (i1 ? throw 0 : flds.i1);
+  int &throwRef3 = (i1 ? flds.b1 : throw 0); // expected-error {{non-const reference cannot bind to bit-field}}
+  int &throwRef4 = (i1 ? throw 0 : flds.b1); // expected-error {{non-const reference cannot bind to bit-field}}
 
   // p3 (one or both class type, convert to each other)
   // b1 (lvalues)
@@ -151,7 +160,6 @@ void test()
   &(i1 ? i1 : i2); // expected-error {{cannot take the address of an rvalue}}
 
   // p4 (lvalue, same type)
-  Fields flds;
   int &ir1 = i1 ? flds.i1 : flds.i2;
   (i1 ? flds.b1 : flds.i2) = 0;
   (i1 ? flds.i1 : flds.b2) = 0;
@@ -219,8 +227,8 @@ void test()
   // *must* create a separate temporary copy of class objects. This can only
   // be properly tested at runtime, though.
 
-  const Abstract &a = true ? static_cast<const Abstract&>(Derived1()) : Derived2(); // expected-error {{allocating an object of abstract class type 'const Abstract'}}
-  true ? static_cast<const Abstract&>(Derived1()) : throw 3; // expected-error {{allocating an object of abstract class type 'const Abstract'}}
+  const Abstract &abstract1 = true ? static_cast<const Abstract&>(Derived1()) : Derived2(); // expected-error {{allocating an object of abstract class type 'const Abstract'}}
+  const Abstract &abstract2 = true ? static_cast<const Abstract&>(Derived1()) : throw 3; // ok
 }
 
 namespace PR6595 {
@@ -367,3 +375,12 @@ namespace DR587 {
   const volatile int &cvir2 = b ? cvi : vi;
   const volatile int &cvir3 = b ? ci : vi; // expected-error{{volatile lvalue reference to type 'const volatile int' cannot bind to a temporary of type 'int'}}
 }
+
+namespace PR17052 {
+  struct X {
+    int i_;
+    bool b_;
+
+    int &test() { return b_ ? i_ : throw 1; }
+  };
+}

Modified: cfe/trunk/test/SemaCXX/expression-traits.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/expression-traits.cpp?rev=200189&r1=200188&r2=200189&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/expression-traits.cpp (original)
+++ cfe/trunk/test/SemaCXX/expression-traits.cpp Sun Jan 26 22:19:56 2014
@@ -520,20 +520,19 @@ void expr_cond(bool cond)
     // 5.16 Conditional operator [expr.cond]
     //
     // 2 If either the second or the third operand has type (possibly
-    // cv-qualified) void, then the lvalue-to-rvalue (4.1),
-    // array-to-pointer (4.2), and function-to-pointer (4.3) standard
-    // conversions are performed on the second and third operands, and one
-    // of the following shall hold:
+    // cv-qualified) void, one of the following shall hold:
     //
     // - The second or the third operand (but not both) is a
-    // throw-expression (15.1); the result is of the type of the other and
-    // is an rvalue.
+    // (possibly parenthesized) throw-expression (15.1); the result
+    // is of the type and value category of the other.
 
     Class classLvalue;
     ASSERT_RVALUE(cond ? throw 1 : (void)0);
     ASSERT_RVALUE(cond ? (void)0 : throw 1);
-    ASSERT_RVALUE(cond ? throw 1 : classLvalue);
-    ASSERT_RVALUE(cond ? classLvalue : throw 1);
+    ASSERT_RVALUE(cond ? throw 1 : 0);
+    ASSERT_RVALUE(cond ? 0 : throw 1);
+    ASSERT_LVALUE(cond ? throw 1 : classLvalue);
+    ASSERT_LVALUE(cond ? classLvalue : throw 1);
 
     // - Both the second and the third operands have type void; the result
     // is of type void and is an rvalue. [Note: this includes the case

Modified: cfe/trunk/www/cxx_dr_status.html
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/www/cxx_dr_status.html?rev=200189&r1=200188&r2=200189&view=diff
==============================================================================
--- cfe/trunk/www/cxx_dr_status.html (original)
+++ cfe/trunk/www/cxx_dr_status.html Sun Jan 26 22:19:56 2014
@@ -9115,7 +9115,7 @@ and <I>POD class</I></td>
     <td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1550">1550</a></td>
     <td>DRWP</td>
     <td>Parenthesized <I>throw-expression</I> operand of <I>conditional-expression</I></td>
-    <td class="none" align="center">Unknown</td>
+    <td class="full" align="center">Yes</td>
   </tr>
   <tr>
     <td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1551">1551</a></td>
@@ -9175,7 +9175,7 @@ and <I>POD class</I></td>
     <td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1560">1560</a></td>
     <td>DRWP</td>
     <td>Gratuitous lvalue-to-rvalue conversion in <I>conditional-expression</I> with <I>throw-expression</I> operand</td>
-    <td class="none" align="center">Unknown</td>
+    <td class="svn" align="center">SVN</td>
   </tr>
   <tr class="open">
     <td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#1561">1561</a></td>





More information about the cfe-commits mailing list