Implement the C++11 discarded value expression rules for volatile lvalues. <rdar...
authorEli Friedman <eli.friedman@gmail.com>
Thu, 24 May 2012 22:04:19 +0000 (22:04 +0000)
committerEli Friedman <eli.friedman@gmail.com>
Thu, 24 May 2012 22:04:19 +0000 (22:04 +0000)
llvm-svn: 157420

clang/lib/Sema/SemaExprCXX.cpp
clang/test/CXX/expr/p10-0x.cpp [new file with mode: 0644]

index efc13a9..d98cb87 100644 (file)
@@ -5267,6 +5267,61 @@ ExprResult Sema::ActOnNoexceptExpr(SourceLocation KeyLoc, SourceLocation,
   return BuildCXXNoexceptExpr(KeyLoc, Operand, RParen);
 }
 
+static bool IsSpecialDiscardedValue(Expr *E) {
+  // In C++11, discarded-value expressions of a certain form are special,
+  // according to [expr]p10:
+  //   The lvalue-to-rvalue conversion (4.1) is applied only if the
+  //   expression is an lvalue of volatile-qualified type and it has
+  //   one of the following forms:
+  E = E->IgnoreParens();
+
+  //   — id-expression (5.1.1),
+  if (isa<DeclRefExpr>(E))
+    return true;
+
+  //   — subscripting (5.2.1),
+  if (isa<ArraySubscriptExpr>(E))
+    return true;
+
+  //   — class member access (5.2.5),
+  if (isa<MemberExpr>(E))
+    return true;
+
+  //   — indirection (5.3.1),
+  if (UnaryOperator *UO = dyn_cast<UnaryOperator>(E))
+    if (UO->getOpcode() == UO_Deref)
+      return true;
+
+  if (BinaryOperator *BO = dyn_cast<BinaryOperator>(E)) {
+    //   — pointer-to-member operation (5.5),
+    if (BO->isPtrMemOp())
+      return true;
+
+    //   — comma expression (5.18) where the right operand is one of the above.
+    if (BO->getOpcode() == BO_Comma)
+      return IsSpecialDiscardedValue(BO->getRHS());
+  }
+
+  //   — conditional expression (5.16) where both the second and the third
+  //     operands are one of the above, or
+  if (ConditionalOperator *CO = dyn_cast<ConditionalOperator>(E))
+    return IsSpecialDiscardedValue(CO->getTrueExpr()) &&
+           IsSpecialDiscardedValue(CO->getFalseExpr());
+  // The related edge case of "*x ?: *x".
+  if (BinaryConditionalOperator *BCO =
+          dyn_cast<BinaryConditionalOperator>(E)) {
+    if (OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(BCO->getTrueExpr()))
+      return IsSpecialDiscardedValue(OVE->getSourceExpr()) &&
+             IsSpecialDiscardedValue(BCO->getFalseExpr());
+  }
+
+  // Objective-C++ extensions to the rule.
+  if (isa<PseudoObjectExpr>(E) || isa<ObjCIvarRefExpr>(E))
+    return true;
+
+  return false;
+}
+
 /// Perform the conversions required for an expression used in a
 /// context that ignores the result.
 ExprResult Sema::IgnoredValueConversions(Expr *E) {
@@ -5291,8 +5346,21 @@ ExprResult Sema::IgnoredValueConversions(Expr *E) {
     return Owned(E);
   }
 
-  // Otherwise, this rule does not apply in C++, at least not for the moment.
-  if (getLangOpts().CPlusPlus) return Owned(E);
+  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.
+    if (getLangOpts().CPlusPlus0x && E->isGLValue() &&
+        E->getType().isVolatileQualified() &&
+        IsSpecialDiscardedValue(E)) {
+      ExprResult Res = DefaultLvalueConversion(E);
+      if (Res.isInvalid())
+        return Owned(E);
+      E = Res.take();
+    }
+    return Owned(E);
+  }
 
   // GCC seems to also exclude expressions of incomplete enum type.
   if (const EnumType *T = E->getType()->getAs<EnumType>()) {
diff --git a/clang/test/CXX/expr/p10-0x.cpp b/clang/test/CXX/expr/p10-0x.cpp
new file mode 100644 (file)
index 0000000..564df88
--- /dev/null
@@ -0,0 +1,46 @@
+// RUN: %clang_cc1 -emit-llvm -triple x86_64-pc-linux-gnu %s -o - -std=c++11 | FileCheck %s
+
+volatile int g1;
+struct S {
+  volatile int a;
+} g2;
+
+volatile int& refcall();
+
+// CHECK: define void @_Z2f1PViPV1S
+void f1(volatile int *x, volatile S* s) {
+  // We should perform the load in these cases.
+  // CHECK: load volatile i32*
+  (*x);
+  // CHECK: load volatile i32*
+  __extension__ g1;
+  // CHECK: load volatile i32*
+  s->a;
+  // CHECK: load volatile i32*
+  g2.a;
+  // CHECK: load volatile i32*
+  s->*(&S::a);
+  // CHECK: load volatile i32*
+  // CHECK: load volatile i32*
+  x[0], 1 ? x[0] : *x;
+
+  // CHECK: load volatile i32*
+  // CHECK: load volatile i32*
+  // CHECK: load volatile i32*
+  *x ?: *x;
+
+  // CHECK: load volatile i32*
+  ({ *x; });
+
+  // CHECK-NOT: load volatile
+  // CHECK: ret
+}
+
+// CHECK: define void @_Z2f2PVi
+// CHECK-NOT: load volatile
+// CHECK: ret
+void f2(volatile int *x) {
+  // We shouldn't perform the load in these cases.
+  refcall();
+  1 ? refcall() : *x;
+}