[PATCH] Force a load when creating a reference to a temporary copied from a bitfield.

Jordan Rose jordan_rose at apple.com
Tue Apr 9 17:16:56 PDT 2013


Hi doug.gregor, rsmith,

For this source:
  const int &ref = someStruct.bitfield;

We used to generate this AST:

  DeclStmt [...]
  `-VarDecl [...] ref 'const int &'
    `-MaterializeTemporaryExpr [...] 'const int' lvalue
      `-ImplicitCastExpr [...] 'const int' lvalue <NoOp>
        `-MemberExpr [...] 'int' lvalue bitfield .bitfield [...]
          `-DeclRefExpr [...] 'struct X' lvalue ParmVar [...] 'someStruct' 'struct X'

Notice the lvalue inside the MaterializeTemporaryExpr, which is very confusing (and caused an assertion to fire in the analyzer -PR15694).

We now generate this:

  DeclStmt [...]
  `-VarDecl [...] ref 'const int &'
    `-MaterializeTemporaryExpr [...] 'const int' lvalue
      `-ImplicitCastExpr [...] 'int' <LValueToRValue>
        `-MemberExpr [...] 'int' lvalue bitfield .bitfield [...]
          `-DeclRefExpr [...] 'struct X' lvalue ParmVar [...] 'someStruct' 'struct X'

Which makes a lot more sense, and matches what CodeGen would do anyway. (CGExpr.cpp:232).

The commit also makes Clang accept this (legal) C++11 code:

  int &&ref = std::move(someStruct).bitfield

[[http://llvm.org/bugs/show_bug.cgi?id=15694 | PR15694]] / <rdar://problem/13600396>

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

Files:
  lib/Sema/SemaInit.cpp
  test/CXX/dcl.decl/dcl.init/dcl.init.ref/p5-0x.cpp

Index: lib/Sema/SemaInit.cpp
===================================================================
--- lib/Sema/SemaInit.cpp
+++ lib/Sema/SemaInit.cpp
@@ -3351,6 +3351,48 @@
                                  T1Quals, cv2T2, T2, T2Quals, Sequence);
 }
 
+static ExprValueKind
+convertQualifiersAndValueKindIfNecessary(Sema &S,
+                                         InitializationSequence &Sequence,
+                                         Expr *Initializer,
+                                         QualType cv1T1,
+                                         Qualifiers T1Quals,
+                                         Qualifiers T2Quals,
+                                         bool IsLValueRef) {
+  bool IsNonAddressableType = Initializer->getBitField() ||
+                              Initializer->refersToVectorElement();
+
+  if (IsNonAddressableType) {
+    // If this is a non-const or volatile lvalue reference, we can't bind to
+    // this value. Give up.
+    if (IsLValueRef && (!T1Quals.hasConst() || T1Quals.hasVolatile())) {
+      assert(Initializer->isGLValue());
+      return Initializer->getValueKind();
+    }
+
+    // Force an lvalue-to-rvalue conversion from the non-addressable lvalue
+    // types (bitfields and vector elements).
+    ImplicitConversionSequence ICS
+      = S.TryImplicitConversion(Initializer, cv1T1,
+                                /*SuppressUserConversions=*/true,
+                                /*AllowExplicit=*/false,
+                                /*FIXME:InOverloadResolution=*/false,
+                                /*CStyle=*/false,
+                                /*AllowObjCWritebackConversion=*/false);
+    assert(!ICS.isBad());
+    Sequence.AddConversionSequenceStep(ICS, cv1T1);
+    return VK_RValue;
+  }
+
+  if (T1Quals != T2Quals) {
+    Sequence.AddQualificationConversionStep(cv1T1,
+                                            Initializer->getValueKind());
+  }
+
+  return Initializer->getValueKind();
+}
+
+
 /// \brief Reference initialization without resolving overloaded functions.
 static void TryReferenceInitializationCore(Sema &S,
                                            const InitializedEntity &Entity,
@@ -3406,11 +3448,11 @@
         Sequence.AddObjCObjectConversionStep(
                                      S.Context.getQualifiedType(T1, T2Quals));
 
-      if (T1Quals != T2Quals)
-        Sequence.AddQualificationConversionStep(cv1T1, VK_LValue);
-      bool BindingTemporary = T1Quals.hasConst() && !T1Quals.hasVolatile() &&
-        (Initializer->getBitField() || Initializer->refersToVectorElement());
-      Sequence.AddReferenceBindingStep(cv1T1, BindingTemporary);
+      ExprValueKind ValueKind =
+        convertQualifiersAndValueKindIfNecessary(S, Sequence, Initializer,
+                                                 cv1T1, T1Quals, T2Quals,
+                                                 isLValueRef);
+      Sequence.AddReferenceBindingStep(cv1T1, ValueKind == VK_RValue);
       return;
     }
 
@@ -3493,10 +3535,12 @@
       Sequence.AddObjCObjectConversionStep(
                                        S.Context.getQualifiedType(T1, T2Quals));
 
-    if (T1Quals != T2Quals)
-      Sequence.AddQualificationConversionStep(cv1T1, ValueKind);
-    Sequence.AddReferenceBindingStep(cv1T1,
-                                 /*bindingTemporary=*/InitCategory.isPRValue());
+    ValueKind = convertQualifiersAndValueKindIfNecessary(S, Sequence,
+                                                         Initializer, cv1T1,
+                                                         T1Quals, T2Quals,
+                                                         isLValueRef);
+
+    Sequence.AddReferenceBindingStep(cv1T1, ValueKind == VK_RValue);
     return;
   }
 
@@ -5104,6 +5148,9 @@
       break;
 
     case SK_BindReferenceToTemporary:
+      // Make sure the "temporary" is actually an rvalue.
+      assert(CurInit.get()->isRValue() && "not a temporary");
+
       // Check exception specifications
       if (S.CheckExceptionSpecCompatibility(CurInit.get(), DestType))
         return ExprError();
Index: test/CXX/dcl.decl/dcl.init/dcl.init.ref/p5-0x.cpp
===================================================================
--- test/CXX/dcl.decl/dcl.init/dcl.init.ref/p5-0x.cpp
+++ test/CXX/dcl.decl/dcl.init/dcl.init.ref/p5-0x.cpp
@@ -200,3 +200,37 @@
   X &&f1(Y &y) { return y; } // expected-error{{rvalue reference to type 'rdar13278115::X' cannot bind to lvalue of type 'rdar13278115::Y'}}
   const X &&f2(Y &y) { return y; } // expected-error{{rvalue reference to type 'const rdar13278115::X' cannot bind to lvalue of type 'rdar13278115::Y'}}
 }
+
+namespace bitfields {
+  struct IntBitfield {
+    int i : 17; // expected-note 3 {{bit-field is declared here}}
+  };
+
+  // A simplified version of std::move.
+  template <typename T>
+  T &&move(T &obj) {
+    return static_cast<T &&>(obj);
+  }
+
+  void test() {
+    int & ir1 = (lvalue<IntBitfield>().i); // expected-error{{non-const reference cannot bind to bit-field 'i'}}
+    int & ir2 = (xvalue<IntBitfield>().i); // expected-error{{non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'}}
+    int && ir3 = (xvalue<IntBitfield>().i); // no-warning
+    int && ir4 = move(lvalue<IntBitfield>()).i; // no-warning
+
+    volatile int & vir1 = (lvalue<IntBitfield>().i); // expected-error{{non-const reference cannot bind to bit-field 'i'}}
+    volatile int & vir2 = (xvalue<IntBitfield>().i); // expected-error{{volatile lvalue reference to type 'volatile int' cannot bind to a temporary of type 'int'}}
+    volatile int && vir3 = (xvalue<IntBitfield>().i); // no-warning
+    volatile int && vir4 = move(lvalue<IntBitfield>()).i; // no-warning
+
+    const int & cir1 = (lvalue<IntBitfield>().i); // no-warning
+    const int & cir2 = (xvalue<IntBitfield>().i); // no-warning
+    const int && cir3 = (xvalue<IntBitfield>().i); // no-warning
+    const int && cir4 = move(lvalue<IntBitfield>()).i; // no-warning
+
+    const volatile int & cvir1 = (lvalue<IntBitfield>().i); // expected-error{{non-const reference cannot bind to bit-field 'i'}}
+    const volatile int & cvir2 = (xvalue<IntBitfield>().i); // expected-error{{volatile lvalue reference to type 'const volatile int' cannot bind to a temporary of type 'int'}}
+    const volatile int && cvir3 = (xvalue<IntBitfield>().i); // no-warning
+    const volatile int && cvir4 = move(lvalue<IntBitfield>()).i; // no-warning
+  }
+}
-------------- next part --------------
A non-text attachment was scrubbed...
Name: D651.1.patch
Type: text/x-patch
Size: 6512 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20130409/6fa13faa/attachment.bin>


More information about the cfe-commits mailing list