r180603 - C++1y: support simple variable assignments in constexpr functions.

Richard Smith richard-llvm at metafoo.co.uk
Fri Apr 26 07:36:30 PDT 2013


Author: rsmith
Date: Fri Apr 26 09:36:30 2013
New Revision: 180603

URL: http://llvm.org/viewvc/llvm-project?rev=180603&view=rev
Log:
C++1y: support simple variable assignments in constexpr functions.

Modified:
    cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td
    cfe/trunk/lib/AST/ExprConstant.cpp
    cfe/trunk/lib/Sema/SemaDeclCXX.cpp
    cfe/trunk/lib/Sema/SemaInit.cpp
    cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp
    cfe/trunk/test/SemaCXX/constant-expression-cxx1y.cpp
    cfe/trunk/test/SemaCXX/constexpr-printing.cpp
    cfe/trunk/test/SemaCXX/constexpr-value-init.cpp
    cfe/trunk/test/SemaCXX/i-c-e-cxx.cpp

Modified: cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td?rev=180603&r1=180602&r2=180603&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td Fri Apr 26 09:36:30 2013
@@ -12,7 +12,7 @@ let Component = "AST" in {
 // Constant expression diagnostics. These (and their users) belong in Sema.
 def note_expr_divide_by_zero : Note<"division by zero">;
 def note_constexpr_invalid_cast : Note<
-  "%select{reinterpret_cast|dynamic_cast|cast which performs the conversions of"
+  "%select{reinterpret_cast|dynamic_cast|cast that performs the conversions of"
   " a reinterpret_cast|cast from %1}0 is not allowed in a constant expression">;
 def note_constexpr_invalid_downcast : Note<
   "cannot cast object of dynamic type %0 to type %1">;
@@ -84,11 +84,20 @@ def note_constexpr_depth_limit_exceeded
 def note_constexpr_call_limit_exceeded : Note<
   "constexpr evaluation hit maximum call limit">;
 def note_constexpr_lifetime_ended : Note<
-  "read of %select{temporary|variable}0 whose lifetime has ended">;
-def note_constexpr_ltor_volatile_type : Note<
-  "read of volatile-qualified type %0 is not allowed in a constant expression">;
-def note_constexpr_ltor_volatile_obj : Note<
-  "read of volatile %select{temporary|object %1|member %1}0 is not allowed in "
+  "%select{read of|assignment to}0 %select{temporary|variable}1 "
+  "whose lifetime has ended">;
+def note_constexpr_access_uninit : Note<
+  "%select{read of|assignment to}0 object outside its lifetime "
+  "is not allowed in a constant expression">;
+def note_constexpr_modify_const_type : Note<
+  "modification of object of const-qualified type %0 is not allowed "
+  "in a constant expression">;
+def note_constexpr_access_volatile_type : Note<
+  "%select{read of|assignment to}0 volatile-qualified type %1 "
+  "is not allowed in a constant expression">;
+def note_constexpr_access_volatile_obj : Note<
+  "%select{read of|assignment to}0 volatile "
+  "%select{temporary|object %2|member %2}1 is not allowed in "
   "a constant expression">;
 def note_constexpr_ltor_mutable : Note<
   "read of mutable member %0 is not allowed in a constant expression">;
@@ -96,14 +105,19 @@ def note_constexpr_ltor_non_const_int :
   "read of non-const variable %0 is not allowed in a constant expression">;
 def note_constexpr_ltor_non_constexpr : Note<
   "read of non-constexpr variable %0 is not allowed in a constant expression">;
-def note_constexpr_read_past_end : Note<
-  "read of dereferenced one-past-the-end pointer is not allowed in a "
-  "constant expression">;
-def note_constexpr_read_inactive_union_member : Note<
-  "read of member %0 of union with %select{active member %2|no active member}1 "
+def note_constexpr_access_null : Note<
+  "%select{read of|assignment to}0 dereferenced null pointer "
+  "is not allowed in a constant expression">;
+def note_constexpr_access_past_end : Note<
+  "%select{read of|assignment to}0 dereferenced one-past-the-end pointer "
+  "is not allowed in a constant expression">;
+def note_constexpr_access_inactive_union_member : Note<
+  "%select{read of|assignment to}0 member %1 of union with "
+  "%select{active member %3|no active member}2 "
   "is not allowed in a constant expression">;
-def note_constexpr_read_uninit : Note<
-  "read of uninitialized object is not allowed in a constant expression">;
+def note_constexpr_modify_global : Note<
+  "a constant expression cannot modify an object that is visible outside "
+  "that expression">;
 def note_constexpr_calls_suppressed : Note<
   "(skipping %0 call%s0 in backtrace; use -fconstexpr-backtrace-limit=0 to "
   "see all)">;

Modified: cfe/trunk/lib/AST/ExprConstant.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprConstant.cpp?rev=180603&r1=180602&r2=180603&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ExprConstant.cpp (original)
+++ cfe/trunk/lib/AST/ExprConstant.cpp Fri Apr 26 09:36:30 2013
@@ -286,7 +286,7 @@ namespace {
 
     /// ParmBindings - Parameter bindings for this function call, indexed by
     /// parameters' function scope indices.
-    const APValue *Arguments;
+    APValue *Arguments;
 
     // Note that we intentionally use std::map here so that references to
     // values are stable.
@@ -297,7 +297,7 @@ namespace {
 
     CallStackFrame(EvalInfo &Info, SourceLocation CallLoc,
                    const FunctionDecl *Callee, const LValue *This,
-                   const APValue *Arguments);
+                   APValue *Arguments);
     ~CallStackFrame();
   };
 
@@ -597,7 +597,7 @@ void SubobjectDesignator::diagnosePointe
 
 CallStackFrame::CallStackFrame(EvalInfo &Info, SourceLocation CallLoc,
                                const FunctionDecl *Callee, const LValue *This,
-                               const APValue *Arguments)
+                               APValue *Arguments)
     : Info(Info), Caller(Info.CurrentCall), CallLoc(CallLoc), Callee(Callee),
       Index(Info.NextCallIndex++), This(This), Arguments(Arguments) {
   Info.CurrentCall = this;
@@ -1471,6 +1471,12 @@ static bool EvaluateVarDeclInit(EvalInfo
 
   // If this is a local variable, dig out its value.
   if (VD->hasLocalStorage() && Frame && Frame->Index > 1) {
+    // In C++1y, we can't safely read anything which might have been mutated
+    // when checking a potential constant expression.
+    if (Info.getLangOpts().CPlusPlus1y &&
+        Info.CheckingPotentialConstantExpression)
+      return false;
+
     Result = Frame->Temporaries[VD];
     // If we've carried on past an unevaluatable local variable initializer,
     // we can't go any further. This can happen during potential constant
@@ -1542,15 +1548,15 @@ static unsigned getBaseIndex(const CXXRe
   llvm_unreachable("base class missing from derived class's bases list");
 }
 
-/// Extract the value of a character from a string literal. CharType is used to
-/// determine the expected signedness of the result -- a string literal used to
-/// initialize an array of 'signed char' or 'unsigned char' might contain chars
-/// of the wrong signedness.
-static APSInt ExtractStringLiteralCharacter(EvalInfo &Info, const Expr *Lit,
-                                            uint64_t Index, QualType CharType) {
+/// Extract the value of a character from a string literal.
+static APSInt extractStringLiteralCharacter(EvalInfo &Info, const Expr *Lit,
+                                            uint64_t Index) {
   // FIXME: Support PredefinedExpr, ObjCEncodeExpr, MakeStringConstant
-  const StringLiteral *S = dyn_cast<StringLiteral>(Lit);
-  assert(S && "unexpected string literal expression kind");
+  const StringLiteral *S = cast<StringLiteral>(Lit);
+  const ConstantArrayType *CAT =
+      Info.Ctx.getAsConstantArrayType(S->getType());
+  assert(CAT && "string literal isn't an array");
+  QualType CharType = CAT->getElementType();
   assert(CharType->isIntegerType() && "unexpected character type");
 
   APSInt Value(S->getCharByteWidth() * Info.Ctx.getCharWidth(),
@@ -1560,24 +1566,77 @@ static APSInt ExtractStringLiteralCharac
   return Value;
 }
 
-/// Extract the designated sub-object of an rvalue.
-static bool ExtractSubobject(EvalInfo &Info, const Expr *E,
-                             APValue &Obj, QualType ObjType,
-                             const SubobjectDesignator &Sub, QualType SubType) {
+// Expand a string literal into an array of characters.
+static void expandStringLiteral(EvalInfo &Info, const Expr *Lit,
+                                APValue &Result) {
+  const StringLiteral *S = cast<StringLiteral>(Lit);
+  const ConstantArrayType *CAT =
+      Info.Ctx.getAsConstantArrayType(S->getType());
+  assert(CAT && "string literal isn't an array");
+  QualType CharType = CAT->getElementType();
+  assert(CharType->isIntegerType() && "unexpected character type");
+
+  unsigned Elts = CAT->getSize().getZExtValue();
+  Result = APValue(APValue::UninitArray(),
+                   std::min(S->getLength(), Elts), Elts);
+  APSInt Value(S->getCharByteWidth() * Info.Ctx.getCharWidth(),
+               CharType->isUnsignedIntegerType());
+  if (Result.hasArrayFiller())
+    Result.getArrayFiller() = APValue(Value);
+  for (unsigned I = 0, N = Result.getArrayInitializedElts(); I != N; ++I) {
+    Value = S->getCodeUnit(I);
+    Result.getArrayInitializedElt(I) = APValue(Value);
+  }
+}
+
+// Expand an array so that it has more than Index filled elements.
+static void expandArray(APValue &Array, unsigned Index) {
+  unsigned Size = Array.getArraySize();
+  assert(Index < Size);
+
+  // Always at least double the number of elements for which we store a value.
+  unsigned OldElts = Array.getArrayInitializedElts();
+  unsigned NewElts = std::max(Index+1, OldElts * 2);
+  NewElts = std::min(Size, std::max(NewElts, 8u));
+
+  // Copy the data across.
+  APValue NewValue(APValue::UninitArray(), NewElts, Size);
+  for (unsigned I = 0; I != OldElts; ++I)
+    NewValue.getArrayInitializedElt(I).swap(Array.getArrayInitializedElt(I));
+  for (unsigned I = OldElts; I != NewElts; ++I)
+    NewValue.getArrayInitializedElt(I) = Array.getArrayFiller();
+  if (NewValue.hasArrayFiller())
+    NewValue.getArrayFiller() = Array.getArrayFiller();
+  Array.swap(NewValue);
+}
+
+/// Kinds of access we can perform on an object.
+enum AccessKinds {
+  AK_Read,
+  AK_Assign
+};
+
+/// Find the designated sub-object of an rvalue.
+template<typename SubobjectHandler>
+typename SubobjectHandler::result_type
+findSubobject(EvalInfo &Info, const Expr *E, APValue &Obj, QualType ObjType,
+              const SubobjectDesignator &Sub, SubobjectHandler &handler) {
   if (Sub.Invalid)
     // A diagnostic will have already been produced.
-    return false;
+    return handler.failed();
   if (Sub.isOnePastTheEnd()) {
-    Info.Diag(E, Info.getLangOpts().CPlusPlus11 ?
-                (unsigned)diag::note_constexpr_read_past_end :
-                (unsigned)diag::note_invalid_subexpr_in_const_expr);
-    return false;
+    if (Info.getLangOpts().CPlusPlus11)
+      Info.Diag(E, diag::note_constexpr_access_past_end)
+        << handler.AccessKind;
+    else
+      Info.Diag(E);
+    return handler.failed();
   }
   if (Sub.Entries.empty())
-    return true;
+    return handler.found(Obj, ObjType);
   if (Info.CheckingPotentialConstantExpression && Obj.isUninit())
     // This object might be initialized later.
-    return false;
+    return handler.failed();
 
   APValue *O = &Obj;
   // Walk the designator's path to find the subobject.
@@ -1590,49 +1649,67 @@ static bool ExtractSubobject(EvalInfo &I
       if (CAT->getSize().ule(Index)) {
         // Note, it should not be possible to form a pointer with a valid
         // designator which points more than one past the end of the array.
-        Info.Diag(E, Info.getLangOpts().CPlusPlus11 ?
-                    (unsigned)diag::note_constexpr_read_past_end :
-                    (unsigned)diag::note_invalid_subexpr_in_const_expr);
-        return false;
+        if (Info.getLangOpts().CPlusPlus11)
+          Info.Diag(E, diag::note_constexpr_access_past_end)
+            << handler.AccessKind;
+        else
+          Info.Diag(E);
+        return handler.failed();
       }
+
+      ObjType = CAT->getElementType();
+
       // An array object is represented as either an Array APValue or as an
       // LValue which refers to a string literal.
       if (O->isLValue()) {
         assert(I == N - 1 && "extracting subobject of character?");
         assert(!O->hasLValuePath() || O->getLValuePath().empty());
-        Obj = APValue(ExtractStringLiteralCharacter(
-          Info, O->getLValueBase().get<const Expr*>(), Index, SubType));
-        return true;
-      } else if (O->getArrayInitializedElts() > Index)
+        if (handler.AccessKind != AK_Read)
+          expandStringLiteral(Info, O->getLValueBase().get<const Expr *>(),
+                              *O);
+        else
+          return handler.foundString(*O, ObjType, Index);
+      }
+
+      if (O->getArrayInitializedElts() > Index)
         O = &O->getArrayInitializedElt(Index);
-      else
+      else if (handler.AccessKind != AK_Read) {
+        expandArray(*O, Index);
+        O = &O->getArrayInitializedElt(Index);
+      } else
         O = &O->getArrayFiller();
-      ObjType = CAT->getElementType();
     } else if (ObjType->isAnyComplexType()) {
       // Next subobject is a complex number.
       uint64_t Index = Sub.Entries[I].ArrayIndex;
       if (Index > 1) {
-        Info.Diag(E, Info.getLangOpts().CPlusPlus11 ?
-                    (unsigned)diag::note_constexpr_read_past_end :
-                    (unsigned)diag::note_invalid_subexpr_in_const_expr);
-        return false;
+        if (Info.getLangOpts().CPlusPlus11)
+          Info.Diag(E, diag::note_constexpr_access_past_end)
+            << handler.AccessKind;
+        else
+          Info.Diag(E);
+        return handler.failed();
       }
+
+      bool WasConstQualified = ObjType.isConstQualified();
+      ObjType = ObjType->castAs<ComplexType>()->getElementType();
+      if (WasConstQualified)
+        ObjType.addConst();
+
       assert(I == N - 1 && "extracting subobject of scalar?");
       if (O->isComplexInt()) {
-        Obj = APValue(Index ? O->getComplexIntImag()
-                            : O->getComplexIntReal());
+        return handler.found(Index ? O->getComplexIntImag()
+                                   : O->getComplexIntReal(), ObjType);
       } else {
         assert(O->isComplexFloat());
-        Obj = APValue(Index ? O->getComplexFloatImag()
-                            : O->getComplexFloatReal());
+        return handler.found(Index ? O->getComplexFloatImag()
+                                   : O->getComplexFloatReal(), ObjType);
       }
-      return true;
     } else if (const FieldDecl *Field = getAsField(Sub.Entries[I])) {
-      if (Field->isMutable()) {
+      if (Field->isMutable() && handler.AccessKind == AK_Read) {
         Info.Diag(E, diag::note_constexpr_ltor_mutable, 1)
           << Field;
         Info.Note(Field->getLocation(), diag::note_declared_at);
-        return false;
+        return handler.failed();
       }
 
       // Next subobject is a class, struct or union field.
@@ -1641,49 +1718,151 @@ static bool ExtractSubobject(EvalInfo &I
         const FieldDecl *UnionField = O->getUnionField();
         if (!UnionField ||
             UnionField->getCanonicalDecl() != Field->getCanonicalDecl()) {
-          Info.Diag(E, diag::note_constexpr_read_inactive_union_member)
-            << Field << !UnionField << UnionField;
-          return false;
+          Info.Diag(E, diag::note_constexpr_access_inactive_union_member)
+            << handler.AccessKind << Field << !UnionField << UnionField;
+          return handler.failed();
         }
         O = &O->getUnionValue();
       } else
         O = &O->getStructField(Field->getFieldIndex());
+
+      bool WasConstQualified = ObjType.isConstQualified();
       ObjType = Field->getType();
+      if (WasConstQualified && !Field->isMutable())
+        ObjType.addConst();
 
       if (ObjType.isVolatileQualified()) {
         if (Info.getLangOpts().CPlusPlus) {
           // FIXME: Include a description of the path to the volatile subobject.
-          Info.Diag(E, diag::note_constexpr_ltor_volatile_obj, 1)
-            << 2 << Field;
+          Info.Diag(E, diag::note_constexpr_access_volatile_obj, 1)
+            << handler.AccessKind << 2 << Field;
           Info.Note(Field->getLocation(), diag::note_declared_at);
         } else {
           Info.Diag(E, diag::note_invalid_subexpr_in_const_expr);
         }
-        return false;
+        return handler.failed();
       }
     } else {
       // Next subobject is a base class.
       const CXXRecordDecl *Derived = ObjType->getAsCXXRecordDecl();
       const CXXRecordDecl *Base = getAsBaseClass(Sub.Entries[I]);
       O = &O->getStructBase(getBaseIndex(Derived, Base));
+
+      bool WasConstQualified = ObjType.isConstQualified();
       ObjType = Info.Ctx.getRecordType(Base);
+      if (WasConstQualified)
+        ObjType.addConst();
     }
 
     if (O->isUninit()) {
       if (!Info.CheckingPotentialConstantExpression)
-        Info.Diag(E, diag::note_constexpr_read_uninit);
+        Info.Diag(E, diag::note_constexpr_access_uninit) << handler.AccessKind;
+      return handler.failed();
+    }
+  }
+
+  return handler.found(*O, ObjType);
+}
+
+struct ExtractSubobjectHandler {
+  EvalInfo &Info;
+  APValue &Obj;
+
+  static const AccessKinds AccessKind = AK_Read;
+
+  typedef bool result_type;
+  bool failed() { return false; }
+  bool found(APValue &Subobj, QualType SubobjType) {
+    if (&Subobj != &Obj) {
+      // We can't just swap Obj and Subobj here, because we'd create an object
+      // that has itself as a subobject. To avoid the leak, we ensure that Tmp
+      // ends up owning the original complete object, which is destroyed by
+      // Tmp's destructor.
+      APValue Tmp;
+      Subobj.swap(Tmp);
+      Obj.swap(Tmp);
+    }
+    return true;
+  }
+  bool found(APSInt &Value, QualType SubobjType) {
+    Obj = APValue(Value);
+    return true;
+  }
+  bool found(APFloat &Value, QualType SubobjType) {
+    Obj = APValue(Value);
+    return true;
+  }
+  bool foundString(APValue &Subobj, QualType SubobjType, uint64_t Character) {
+    Obj = APValue(extractStringLiteralCharacter(
+        Info, Subobj.getLValueBase().get<const Expr *>(), Character));
+    return true;
+  }
+};
+const AccessKinds ExtractSubobjectHandler::AccessKind;
+
+/// Extract the designated sub-object of an rvalue.
+static bool extractSubobject(EvalInfo &Info, const Expr *E,
+                             APValue &Obj, QualType ObjType,
+                             const SubobjectDesignator &Sub) {
+  ExtractSubobjectHandler Handler = { Info, Obj };
+  return findSubobject(Info, E, Obj, ObjType, Sub, Handler);
+}
+
+struct ModifySubobjectHandler {
+  EvalInfo &Info;
+  APValue &NewVal;
+  const Expr *E;
+
+  typedef bool result_type;
+  static const AccessKinds AccessKind = AK_Assign;
+
+  bool checkConst(QualType QT) {
+    // Assigning to a const object has undefined behavior.
+    if (QT.isConstQualified()) {
+      Info.Diag(E, diag::note_constexpr_modify_const_type) << QT;
       return false;
     }
+    return true;
   }
 
-  // This may look super-stupid, but it serves an important purpose: if we just
-  // swapped Obj and *O, we'd create an object which had itself as a subobject.
-  // To avoid the leak, we ensure that Tmp ends up owning the original complete
-  // object, which is destroyed by Tmp's destructor.
-  APValue Tmp;
-  O->swap(Tmp);
-  Obj.swap(Tmp);
-  return true;
+  bool failed() { return false; }
+  bool found(APValue &Subobj, QualType SubobjType) {
+    if (!checkConst(SubobjType))
+      return false;
+    // We've been given ownership of NewVal, so just swap it in.
+    Subobj.swap(NewVal);
+    return true;
+  }
+  bool found(APSInt &Value, QualType SubobjType) {
+    if (!checkConst(SubobjType))
+      return false;
+    if (!NewVal.isInt()) {
+      // Maybe trying to write a cast pointer value into a complex?
+      Info.Diag(E);
+      return false;
+    }
+    Value = NewVal.getInt();
+    return true;
+  }
+  bool found(APFloat &Value, QualType SubobjType) {
+    if (!checkConst(SubobjType))
+      return false;
+    Value = NewVal.getFloat();
+    return true;
+  }
+  bool foundString(APValue &Subobj, QualType SubobjType, uint64_t Character) {
+    llvm_unreachable("shouldn't encounter string elements with ExpandArrays");
+  }
+};
+const AccessKinds ModifySubobjectHandler::AccessKind;
+
+/// Update the designated sub-object of an rvalue to the given value.
+static bool modifySubobject(EvalInfo &Info, const Expr *E,
+                            APValue &Obj, QualType ObjType,
+                            const SubobjectDesignator &Sub,
+                            APValue &NewVal) {
+  ModifySubobjectHandler Handler = { Info, NewVal, E };
+  return findSubobject(Info, E, Obj, ObjType, Sub, Handler);
 }
 
 /// Find the position where two subobject designators diverge, or equivalently
@@ -1744,14 +1923,14 @@ static bool AreElementsOfSameArray(QualT
 }
 
 /// HandleLValueToRValueConversion - Perform an lvalue-to-rvalue conversion on
-/// the given lvalue. This can also be used for 'lvalue-to-lvalue' conversions
+/// the given glvalue. This can also be used for 'lvalue-to-lvalue' conversions
 /// for looking up the glvalue referred to by an entity of reference type.
 ///
 /// \param Info - Information about the ongoing evaluation.
 /// \param Conv - The expression for which we are performing the conversion.
 ///               Used for diagnostics.
-/// \param Type - The type we expect this conversion to produce, before
-///               stripping cv-qualifiers in the case of a non-clas type.
+/// \param Type - The type of the glvalue (before stripping cv-qualifiers in the
+///               case of a non-class type).
 /// \param LVal - The glvalue on which we are attempting to perform this action.
 /// \param RVal - The produced value will be placed here.
 static bool HandleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv,
@@ -1764,8 +1943,7 @@ static bool HandleLValueToRValueConversi
   const Expr *Base = LVal.Base.dyn_cast<const Expr*>();
 
   if (!LVal.Base) {
-    // FIXME: Indirection through a null pointer deserves a specific diagnostic.
-    Info.Diag(Conv, diag::note_invalid_subexpr_in_const_expr);
+    Info.Diag(Conv, diag::note_constexpr_access_null) << AK_Read;
     return false;
   }
 
@@ -1773,7 +1951,8 @@ static bool HandleLValueToRValueConversi
   if (LVal.CallIndex) {
     Frame = Info.getCallFrame(LVal.CallIndex);
     if (!Frame) {
-      Info.Diag(Conv, diag::note_constexpr_lifetime_ended, 1) << !Base;
+      Info.Diag(Conv, diag::note_constexpr_lifetime_ended, 1)
+        << AK_Read << !Base;
       NoteLValueLocation(Info, LVal.Base);
       return false;
     }
@@ -1785,7 +1964,8 @@ static bool HandleLValueToRValueConversi
   // semantics.
   if (Type.isVolatileQualified()) {
     if (Info.getLangOpts().CPlusPlus)
-      Info.Diag(Conv, diag::note_constexpr_ltor_volatile_type) << Type;
+      Info.Diag(Conv, diag::note_constexpr_access_volatile_type)
+        << AK_Read << Type;
     else
       Info.Diag(Conv);
     return false;
@@ -1812,7 +1992,8 @@ static bool HandleLValueToRValueConversi
     QualType VT = VD->getType();
     if (VT.isVolatileQualified()) {
       if (Info.getLangOpts().CPlusPlus) {
-        Info.Diag(Conv, diag::note_constexpr_ltor_volatile_obj, 1) << 1 << VD;
+        Info.Diag(Conv, diag::note_constexpr_access_volatile_obj, 1)
+          << AK_Read << 1 << VD;
         Info.Note(VD->getLocation(), diag::note_declared_at);
       } else {
         Info.Diag(Conv);
@@ -1857,37 +2038,15 @@ static bool HandleLValueToRValueConversi
       }
     }
 
-    if (!EvaluateVarDeclInit(Info, Conv, VD, Frame, RVal))
-      return false;
-
-    if (isa<ParmVarDecl>(VD) || !VD->getAnyInitializer()->isLValue())
-      return ExtractSubobject(Info, Conv, RVal, VT, LVal.Designator, Type);
-
-    // The declaration was initialized by an lvalue, with no lvalue-to-rvalue
-    // conversion. This happens when the declaration and the lvalue should be
-    // considered synonymous, for instance when initializing an array of char
-    // from a string literal. Continue as if the initializer lvalue was the
-    // value we were originally given.
-    assert(RVal.getLValueOffset().isZero() &&
-           "offset for lvalue init of non-reference");
-    Base = RVal.getLValueBase().get<const Expr*>();
-
-    if (unsigned CallIndex = RVal.getLValueCallIndex()) {
-      Frame = Info.getCallFrame(CallIndex);
-      if (!Frame) {
-        Info.Diag(Conv, diag::note_constexpr_lifetime_ended, 1) << !Base;
-        NoteLValueLocation(Info, RVal.getLValueBase());
-        return false;
-      }
-    } else {
-      Frame = 0;
-    }
+    return EvaluateVarDeclInit(Info, Conv, VD, Frame, RVal) &&
+           extractSubobject(Info, Conv, RVal, VT, LVal.Designator);
   }
 
   // Volatile temporary objects cannot be read in constant expressions.
   if (Base->getType().isVolatileQualified()) {
     if (Info.getLangOpts().CPlusPlus) {
-      Info.Diag(Conv, diag::note_constexpr_ltor_volatile_obj, 1) << 0;
+      Info.Diag(Conv, diag::note_constexpr_access_volatile_obj, 1)
+        << AK_Read << 0;
       Info.Note(Base->getExprLoc(), diag::note_constexpr_temporary_here);
     } else {
       Info.Diag(Conv);
@@ -1896,6 +2055,12 @@ static bool HandleLValueToRValueConversi
   }
 
   if (Frame) {
+    // In C++1y, we can't safely read anything which might have been mutated
+    // when checking a potential constant expression.
+    if (Info.getLangOpts().CPlusPlus1y &&
+        Info.CheckingPotentialConstantExpression)
+      return false;
+
     // If this is a temporary expression with a nontrivial initializer, grab the
     // value from the relevant stack frame.
     RVal = Frame->Temporaries[Base];
@@ -1908,6 +2073,11 @@ static bool HandleLValueToRValueConversi
     if (!Evaluate(RVal, Info, CLE->getInitializer()))
       return false;
   } else if (isa<StringLiteral>(Base)) {
+    if (Info.getLangOpts().CPlusPlus1y &&
+        Info.CheckingPotentialConstantExpression &&
+        !Base->getType().isConstQualified())
+      return false;
+
     // We represent a string literal array as an lvalue pointing at the
     // corresponding expression, rather than building an array of chars.
     // FIXME: Support PredefinedExpr, ObjCEncodeExpr, MakeStringConstant
@@ -1917,8 +2087,105 @@ static bool HandleLValueToRValueConversi
     return false;
   }
 
-  return ExtractSubobject(Info, Conv, RVal, Base->getType(), LVal.Designator,
-                          Type);
+  return extractSubobject(Info, Conv, RVal, Base->getType(), LVal.Designator);
+}
+
+/// Perform an assignment of Val to LVal. Takes ownership of Val.
+// FIXME: Factor out duplication with HandleLValueToRValueConversion.
+static bool HandleAssignment(EvalInfo &Info, const Expr *E, const LValue &LVal,
+                             QualType LValType, APValue &Val) {
+  if (!Info.getLangOpts().CPlusPlus1y) {
+    Info.Diag(E, diag::note_invalid_subexpr_in_const_expr);
+    return false;
+  }
+
+  if (LVal.Designator.Invalid)
+    // A diagnostic will have already been produced.
+    return false;
+
+  if (Info.CheckingPotentialConstantExpression)
+    return false;
+
+  const Expr *Base = LVal.Base.dyn_cast<const Expr*>();
+
+  if (!LVal.Base) {
+    Info.Diag(E, diag::note_constexpr_access_null) << AK_Assign;
+    return false;
+  }
+
+  if (!LVal.CallIndex) {
+    Info.Diag(E, diag::note_constexpr_modify_global);
+    return false;
+  }
+
+  CallStackFrame *Frame = Info.getCallFrame(LVal.CallIndex);
+  if (!Frame) {
+    Info.Diag(E, diag::note_constexpr_lifetime_ended, 1)
+      << AK_Assign << !Base;
+    NoteLValueLocation(Info, LVal.Base);
+    return false;
+  }
+
+  if (LValType.isVolatileQualified()) {
+    if (Info.getLangOpts().CPlusPlus)
+      Info.Diag(E, diag::note_constexpr_access_volatile_type)
+        << AK_Assign << LValType;
+    else
+      Info.Diag(E);
+    return false;
+  }
+
+  // Compute value storage location and type of base object.
+  APValue *BaseVal = 0;
+  QualType BaseType;
+
+  if (const ValueDecl *D = LVal.Base.dyn_cast<const ValueDecl*>()) {
+    const VarDecl *VD = dyn_cast<VarDecl>(D);
+    if (VD) {
+      if (const VarDecl *VDef = VD->getDefinition(Info.Ctx))
+        VD = VDef;
+    }
+    if (!VD || VD->isInvalidDecl()) {
+      Info.Diag(E);
+      return false;
+    }
+
+    // Modifications to volatile-qualified objects are not allowed.
+    BaseType = VD->getType();
+    if (BaseType.isVolatileQualified()) {
+      Info.Diag(E, diag::note_constexpr_access_volatile_obj, 1)
+        << AK_Assign << 1 << VD;
+      Info.Note(VD->getLocation(), diag::note_declared_at);
+      return false;
+    }
+
+    if (const ParmVarDecl *PVD = dyn_cast<ParmVarDecl>(VD)) {
+      if (!Frame || !Frame->Arguments) {
+        Info.Diag(E, diag::note_invalid_subexpr_in_const_expr);
+        return false;
+      }
+      BaseVal = &Frame->Arguments[PVD->getFunctionScopeIndex()];
+    } else if (VD->hasLocalStorage() && Frame && Frame->Index > 1) {
+      BaseVal = &Frame->Temporaries[VD];
+    } else {
+      // FIXME: Can this happen?
+      Info.Diag(E);
+      return false;
+    }
+  } else {
+    BaseType = Base->getType();
+    BaseVal = &Frame->Temporaries[Base];
+
+    // Volatile temporary objects cannot be modified in constant expressions.
+    if (BaseType.isVolatileQualified()) {
+      Info.Diag(E, diag::note_constexpr_access_volatile_obj, 1)
+        << AK_Assign << 0;
+      Info.Note(Base->getExprLoc(), diag::note_constexpr_temporary_here);
+      return false;
+    }
+  }
+
+  return modifySubobject(Info, E, *BaseVal, BaseType, LVal.Designator, Val);
 }
 
 /// Build an lvalue for the object argument of a member function call.
@@ -2264,8 +2531,11 @@ static bool HandleFunctionCall(SourceLoc
 
   CallStackFrame Frame(Info, CallLoc, Callee, This, ArgValues.data());
   EvalStmtResult ESR = EvaluateStmt(Result, Info, Body);
-  if (ESR == ESR_Succeeded)
+  if (ESR == ESR_Succeeded) {
+    if (Callee->getResultType()->isVoidType())
+      return true;
     Info.Diag(Callee->getLocEnd(), diag::note_constexpr_no_return);
+  }
   return ESR == ESR_Returned;
 }
 
@@ -2715,7 +2985,7 @@ public:
     SubobjectDesignator Designator(BaseTy);
     Designator.addDeclUnchecked(FD);
 
-    return ExtractSubobject(Info, E, Val, BaseTy, Designator, E->getType()) &&
+    return extractSubobject(Info, E, Val, BaseTy, Designator) &&
            DerivedSuccess(Val, E);
   }
 
@@ -2829,6 +3099,16 @@ public:
     case BO_PtrMemD:
     case BO_PtrMemI:
       return HandleMemberPointerAccess(this->Info, E, Result);
+
+    case BO_Assign: {
+      if (!this->Visit(E->getLHS()))
+        return false;
+      APValue NewVal;
+      if (!Evaluate(NewVal, this->Info, E->getRHS()))
+        return false;
+      return HandleAssignment(this->Info, E, Result, E->getLHS()->getType(),
+                              NewVal);
+    }
     }
   }
 

Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=180603&r1=180602&r2=180603&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Fri Apr 26 09:36:30 2013
@@ -845,7 +845,8 @@ static bool CheckConstexprDeclStmt(Sema
             << (VD->getTLSKind() == VarDecl::TLS_Dynamic);
           return false;
         }
-        if (SemaRef.RequireLiteralType(
+        if (!VD->getType()->isDependentType() &&
+            SemaRef.RequireLiteralType(
               VD->getLocation(), VD->getType(),
               diag::err_constexpr_local_var_non_literal_type,
               isa<CXXConstructorDecl>(Dcl)))
@@ -1135,11 +1136,11 @@ bool Sema::CheckConstexprFunctionBody(co
       // statement. We still do, unless the return type is void, because
       // otherwise if there's no return statement, the function cannot
       // be used in a core constant expression.
+      bool OK = getLangOpts().CPlusPlus1y && Dcl->getResultType()->isVoidType();
       Diag(Dcl->getLocation(),
-           getLangOpts().CPlusPlus1y && Dcl->getResultType()->isVoidType()
-             ? diag::warn_cxx11_compat_constexpr_body_no_return
-             : diag::err_constexpr_body_no_return);
-      return false;
+           OK ? diag::warn_cxx11_compat_constexpr_body_no_return
+              : diag::err_constexpr_body_no_return);
+      return OK;
     }
     if (ReturnStmts.size() > 1) {
       Diag(ReturnStmts.back(),

Modified: cfe/trunk/lib/Sema/SemaInit.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaInit.cpp?rev=180603&r1=180602&r2=180603&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaInit.cpp (original)
+++ cfe/trunk/lib/Sema/SemaInit.cpp Fri Apr 26 09:36:30 2013
@@ -97,6 +97,7 @@ static void CheckStringInit(Expr *Str, Q
     DeclT = S.Context.getConstantArrayType(IAT->getElementType(),
                                            ConstVal,
                                            ArrayType::Normal, 0);
+    Str->setType(DeclT);
     return;
   }
 

Modified: cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp?rev=180603&r1=180602&r2=180603&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp (original)
+++ cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp Fri Apr 26 09:36:30 2013
@@ -304,16 +304,16 @@ struct Str {
     expected-note {{reinterpret_cast is not allowed in a constant expression}}
   int c : (S*)(long)(sptr) == (S*)(long)(sptr); // \
     expected-warning {{not an integral constant expression}} \
-    expected-note {{cast which performs the conversions of a reinterpret_cast is not allowed in a constant expression}}
+    expected-note {{cast that performs the conversions of a reinterpret_cast is not allowed in a constant expression}}
   int d : (S*)(42) == (S*)(42); // \
     expected-warning {{not an integral constant expression}} \
-    expected-note {{cast which performs the conversions of a reinterpret_cast is not allowed in a constant expression}}
+    expected-note {{cast that performs the conversions of a reinterpret_cast is not allowed in a constant expression}}
   int e : (Str*)(sptr) == (Str*)(sptr); // \
     expected-warning {{not an integral constant expression}} \
-    expected-note {{cast which performs the conversions of a reinterpret_cast is not allowed in a constant expression}}
+    expected-note {{cast that performs the conversions of a reinterpret_cast is not allowed in a constant expression}}
   int f : &(U&)(*sptr) == &(U&)(*sptr); // \
     expected-warning {{not an integral constant expression}} \
-    expected-note {{cast which performs the conversions of a reinterpret_cast is not allowed in a constant expression}}
+    expected-note {{cast that performs the conversions of a reinterpret_cast is not allowed in a constant expression}}
   int g : (S*)(void*)(sptr) == sptr; // \
     expected-warning {{not an integral constant expression}} \
     expected-note {{cast from 'void *' is not allowed in a constant expression}}
@@ -362,7 +362,7 @@ constexpr char c0 = "nought index"[0];
 constexpr char c1 = "nice index"[10];
 constexpr char c2 = "nasty index"[12]; // expected-error {{must be initialized by a constant expression}} expected-warning {{is past the end}} expected-note {{read of dereferenced one-past-the-end pointer}}
 constexpr char c3 = "negative index"[-1]; // expected-error {{must be initialized by a constant expression}} expected-warning {{is before the beginning}} expected-note {{cannot refer to element -1 of array of 15 elements}}
-constexpr char c4 = ((char*)(int*)"no reinterpret_casts allowed")[14]; // expected-error {{must be initialized by a constant expression}} expected-note {{cast which performs the conversions of a reinterpret_cast}}
+constexpr char c4 = ((char*)(int*)"no reinterpret_casts allowed")[14]; // expected-error {{must be initialized by a constant expression}} expected-note {{cast that performs the conversions of a reinterpret_cast}}
 
 constexpr const char *p = "test" + 2;
 static_assert(*p == 's', "");

Modified: cfe/trunk/test/SemaCXX/constant-expression-cxx1y.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/constant-expression-cxx1y.cpp?rev=180603&r1=180602&r2=180603&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/constant-expression-cxx1y.cpp (original)
+++ cfe/trunk/test/SemaCXX/constant-expression-cxx1y.cpp Fri Apr 26 09:36:30 2013
@@ -124,3 +124,160 @@ constexpr int namespace_alias() {
   namespace N = NS;
   return N::n;
 }
+
+namespace assign {
+  constexpr int a = 0;
+  const int b = 0;
+  int c = 0; // expected-note 2{{here}}
+
+  constexpr void set(const int &a, int b) {
+    const_cast<int&>(a) = b; // expected-note 2{{constant expression cannot modify an object that is visible outside that expression}}
+  }
+  constexpr int wrap(int a, int b) {
+    set(a, b);
+    return a;
+  }
+
+  static_assert((set(a, 1), a) == 1, ""); // expected-error {{constant expression}} expected-note {{in call to 'set(a, 1)'}}
+  static_assert((set(b, 1), b) == 1, ""); // expected-error {{constant expression}} expected-note {{in call to 'set(b, 1)'}}
+  static_assert((set(c, 1), c) == 1, ""); // expected-error {{constant expression}} expected-note {{read of non-const variable 'c'}}
+
+  static_assert(wrap(a, 1) == 1, "");
+  static_assert(wrap(b, 1) == 1, "");
+  static_assert(wrap(c, 1) == 1, ""); // expected-error {{constant expression}} expected-note {{read of non-const variable 'c'}}
+}
+
+namespace string_assign {
+  template<typename T>
+  constexpr void swap(T &a, T &b) {
+    T tmp = a;
+    a = b;
+    b = tmp;
+  }
+  template<typename Iterator>
+  constexpr void reverse(Iterator begin, Iterator end) {
+#if 0 // FIXME: once implementation is complete...
+    while (begin != end && begin != --end)
+      swap(*begin++, *end);
+#else
+    if (begin != end) {
+      end = end - 1;
+      if (begin == end)
+        return;
+      swap(*begin, *end);
+      begin = begin + 1;
+      reverse(begin, end);
+    }
+#endif
+  }
+  template<typename Iterator1, typename Iterator2>
+  constexpr bool equal(Iterator1 a, Iterator1 ae, Iterator2 b, Iterator2 be) {
+#if 0 // FIXME: once implementation is complete...
+    while (a != ae && b != be) {
+      if (*a != *b)
+        return false;
+      ++a, ++b;
+    }
+#else
+    if (a != ae && b != be) {
+      if (*a != *b)
+        return false;
+      a = a + 1;
+      b = b + 1;
+      return equal(a, ae, b, be);
+    }
+#endif
+    return a == ae && b == be;
+  }
+  constexpr bool test1(int n) {
+    char stuff[100] = "foobarfoo";
+    const char stuff2[100] = "oofraboof";
+    reverse(stuff, stuff + n); // expected-note {{cannot refer to element 101 of array of 100 elements}}
+    return equal(stuff, stuff + n, stuff2, stuff2 + n);
+  }
+  static_assert(!test1(1), "");
+  static_assert(test1(3), "");
+  static_assert(!test1(6), "");
+  static_assert(test1(9), "");
+  static_assert(!test1(100), "");
+  static_assert(!test1(101), ""); // expected-error {{constant expression}} expected-note {{in call to 'test1(101)'}}
+
+  // FIXME: We should be able to reject this before it's called
+  constexpr void f() {
+    char foo[10] = { "z" }; // expected-note {{here}}
+    foo[10] = 'x'; // expected-warning {{past the end}} expected-note {{assignment to dereferenced one-past-the-end pointer}}
+  }
+  constexpr int k = (f(), 0); // expected-error {{constant expression}} expected-note {{in call}}
+}
+
+namespace array_resize {
+  constexpr int do_stuff(int k1, int k2) {
+    int arr[1234] = { 1, 2, 3, 4 };
+    arr[k1] = 5; // expected-note {{past-the-end}} expected-note {{cannot refer to element 1235}} expected-note {{cannot refer to element -1}}
+    return arr[k2];
+  }
+  static_assert(do_stuff(1, 2) == 3, "");
+  static_assert(do_stuff(0, 0) == 5, "");
+  static_assert(do_stuff(1233, 1233) == 5, "");
+  static_assert(do_stuff(1233, 0) == 1, "");
+  static_assert(do_stuff(1234, 0) == 1, ""); // expected-error {{constant expression}} expected-note {{in call}}
+  static_assert(do_stuff(1235, 0) == 1, ""); // expected-error {{constant expression}} expected-note {{in call}}
+  static_assert(do_stuff(-1, 0) == 1, ""); // expected-error {{constant expression}} expected-note {{in call}}
+}
+
+namespace potential_const_expr {
+  constexpr void set(int &n) { n = 1; }
+  constexpr int div_zero_1() { int z = 0; set(z); return 100 / z; } // no error
+  constexpr int div_zero_2() { // expected-error {{never produces a constant expression}}
+    int z = 0;
+    return 100 / (set(z), 0); // expected-note {{division by zero}}
+  }
+}
+
+namespace subobject {
+  union A { constexpr A() : y(5) {} int x, y; };
+  struct B { A a; };
+  struct C : B {};
+  union D { constexpr D() : c() {} constexpr D(int n) : n(n) {} C c; int n; };
+  constexpr void f(D &d) {
+    d.c.a.y = 3;
+    // expected-note at -1 {{cannot modify an object that is visible outside}}
+    // expected-note at -2 {{assignment to member 'c' of union with active member 'n'}}
+  }
+  constexpr bool check(D &d) { return d.c.a.y == 3; }
+
+  constexpr bool g() { D d; f(d); return d.c.a.y == 3; }
+  static_assert(g(), "");
+
+  D d;
+  constexpr bool h() { f(d); return check(d); } // expected-note {{in call}}
+  static_assert(h(), ""); // expected-error {{constant expression}} expected-note {{in call}}
+
+  constexpr bool i() { D d(0); f(d); return check(d); } // expected-note {{in call}}
+  static_assert(i(), ""); // expected-error {{constant expression}} expected-note {{in call}}
+
+  constexpr bool j() { D d; d.c.a.x = 3; return check(d); } // expected-note {{assignment to member 'x' of union with active member 'y'}}
+  static_assert(j(), ""); // expected-error {{constant expression}} expected-note {{in call}}
+}
+
+namespace lifetime {
+  constexpr int &&id(int &&n) { return static_cast<int&&>(n); }
+  constexpr int &&dead() { return id(0); } // expected-note {{temporary created here}}
+  constexpr int bad() { int &&n = dead(); n = 1; return n; } // expected-note {{assignment to temporary whose lifetime has ended}}
+  static_assert(bad(), ""); // expected-error {{constant expression}} expected-note {{in call}}
+}
+
+namespace const_modify {
+  constexpr int modify(int &n) { return n = 1; } // expected-note {{modification of object of const-qualified type 'const int'}}
+  constexpr int test1() { int k = 0; return modify(k); }
+  constexpr int test2() { const int k = 0; return modify(const_cast<int&>(k)); } // expected-note {{in call}}
+  static_assert(test1() == 1, "");
+  static_assert(test2() == 1, ""); // expected-error {{constant expression}} expected-note {{in call}}
+}
+
+namespace null {
+  constexpr int test(int *p) {
+    return *p = 123; // expected-note {{assignment to dereferenced null pointer}}
+  }
+  static_assert(test(0), ""); // expected-error {{constant expression}} expected-note {{in call}}
+}

Modified: cfe/trunk/test/SemaCXX/constexpr-printing.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/constexpr-printing.cpp?rev=180603&r1=180602&r2=180603&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/constexpr-printing.cpp (original)
+++ cfe/trunk/test/SemaCXX/constexpr-printing.cpp Fri Apr 26 09:36:30 2013
@@ -9,7 +9,7 @@ struct S {
   int n, m;
 };
 
-constexpr int extract(const S &s) { return s.n; } // expected-note {{read of uninitialized object is not allowed in a constant expression}}
+constexpr int extract(const S &s) { return s.n; } // expected-note {{read of object outside its lifetime is not allowed in a constant expression}}
 
 constexpr S s1; // ok
 void f() {

Modified: cfe/trunk/test/SemaCXX/constexpr-value-init.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/constexpr-value-init.cpp?rev=180603&r1=180602&r2=180603&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/constexpr-value-init.cpp (original)
+++ cfe/trunk/test/SemaCXX/constexpr-value-init.cpp Fri Apr 26 09:36:30 2013
@@ -1,7 +1,7 @@
 // RUN: %clang_cc1 %s -std=c++11 -fsyntax-only -verify
 
 struct A {
-  constexpr A() : a(b + 1), b(a + 1) {} // expected-note {{uninitialized}}
+  constexpr A() : a(b + 1), b(a + 1) {} // expected-note {{outside its lifetime}}
   int a;
   int b;
 };

Modified: cfe/trunk/test/SemaCXX/i-c-e-cxx.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/i-c-e-cxx.cpp?rev=180603&r1=180602&r2=180603&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/i-c-e-cxx.cpp (original)
+++ cfe/trunk/test/SemaCXX/i-c-e-cxx.cpp Fri Apr 26 09:36:30 2013
@@ -60,7 +60,7 @@ int* y = reinterpret_cast<const char&>(x
 
 // This isn't an integral constant expression, but make sure it folds anyway.
 struct PR8836 { char _; long long a; }; // expected-warning {{long long}}
-int PR8836test[(__typeof(sizeof(int)))&reinterpret_cast<const volatile char&>((((PR8836*)0)->a))]; // expected-warning {{folded to constant array as an extension}} expected-note {{cast which performs the conversions of a reinterpret_cast is not allowed in a constant expression}}
+int PR8836test[(__typeof(sizeof(int)))&reinterpret_cast<const volatile char&>((((PR8836*)0)->a))]; // expected-warning {{folded to constant array as an extension}} expected-note {{cast that performs the conversions of a reinterpret_cast is not allowed in a constant expression}}
 
 const int nonconst = 1.0; // expected-note {{declared here}}
 int arr[nonconst]; // expected-warning {{folded to constant array as an extension}} expected-note {{initializer of 'nonconst' is not a constant expression}}





More information about the cfe-commits mailing list