[cfe-commits] r144926 - in /cfe/trunk: include/clang/AST/APValue.h lib/AST/APValue.cpp lib/AST/ExprConstant.cpp lib/CodeGen/CGExprConstant.cpp test/CodeGen/object-size.c test/CodeGenCXX/const-base-cast.cpp test/SemaCXX/constant-expression-cxx11.cpp

Richard Smith richard-llvm at metafoo.co.uk
Thu Nov 17 14:56:20 PST 2011


Author: rsmith
Date: Thu Nov 17 16:56:20 2011
New Revision: 144926

URL: http://llvm.org/viewvc/llvm-project?rev=144926&view=rev
Log:
Constant expression evaluation: add support for evaluation of member pointers
and base-to-derived casts, and add proper handling of temporaries.

Modified:
    cfe/trunk/include/clang/AST/APValue.h
    cfe/trunk/lib/AST/APValue.cpp
    cfe/trunk/lib/AST/ExprConstant.cpp
    cfe/trunk/lib/CodeGen/CGExprConstant.cpp
    cfe/trunk/test/CodeGen/object-size.c
    cfe/trunk/test/CodeGenCXX/const-base-cast.cpp
    cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp

Modified: cfe/trunk/include/clang/AST/APValue.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/APValue.h?rev=144926&r1=144925&r2=144926&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/APValue.h (original)
+++ cfe/trunk/include/clang/AST/APValue.h Thu Nov 17 16:56:20 2011
@@ -27,6 +27,7 @@
   class FieldDecl;
   class Decl;
   class ValueDecl;
+  class CXXRecordDecl;
 
 /// APValue - This class implements a discriminated union of [uninitialized]
 /// [APSInt] [APFloat], [Complex APSInt] [Complex APFloat], [Expr + Offset],
@@ -45,7 +46,8 @@
     Vector,
     Array,
     Struct,
-    Union
+    Union,
+    MemberPointer
   };
   typedef llvm::PointerUnion<const ValueDecl *, const Expr *> LValueBase;
   typedef llvm::PointerIntPair<const Decl *, 1, bool> BaseOrMemberType;
@@ -96,6 +98,7 @@
     UnionData();
     ~UnionData();
   };
+  struct MemberPointerData;
 
   enum {
     MaxSize = (sizeof(ComplexAPSInt) > sizeof(ComplexAPFloat) ?
@@ -131,9 +134,10 @@
       : Kind(Uninitialized) {
     MakeLValue(); setLValue(B, O, N);
   }
-  APValue(LValueBase B, const CharUnits &O, ArrayRef<LValuePathEntry> Path)
+  APValue(LValueBase B, const CharUnits &O, ArrayRef<LValuePathEntry> Path,
+          bool OnePastTheEnd)
       : Kind(Uninitialized) {
-    MakeLValue(); setLValue(B, O, Path);
+    MakeLValue(); setLValue(B, O, Path, OnePastTheEnd);
   }
   APValue(UninitArray, unsigned InitElts, unsigned Size) : Kind(Uninitialized) {
     MakeArray(InitElts, Size);
@@ -145,6 +149,10 @@
       : Kind(Uninitialized) {
     MakeUnion(); setUnion(D, V);
   }
+  APValue(const ValueDecl *Member, bool IsDerivedMember,
+          ArrayRef<const CXXRecordDecl*> Path) : Kind(Uninitialized) {
+    MakeMemberPointer(Member, IsDerivedMember, Path);
+  }
 
   ~APValue() {
     MakeUninit();
@@ -161,6 +169,7 @@
   bool isArray() const { return Kind == Array; }
   bool isStruct() const { return Kind == Struct; }
   bool isUnion() const { return Kind == Union; }
+  bool isMemberPointer() const { return Kind == MemberPointer; }
 
   void print(raw_ostream &OS) const;
   void dump() const;
@@ -218,6 +227,7 @@
   const CharUnits &getLValueOffset() const {
     return const_cast<APValue*>(this)->getLValueOffset();
   }
+  bool isLValueOnePastTheEnd() const;
   bool hasLValuePath() const;
   ArrayRef<LValuePathEntry> getLValuePath() const;
 
@@ -297,6 +307,10 @@
     return const_cast<APValue*>(this)->getUnionValue();
   }
 
+  const ValueDecl *getMemberPointerDecl() const;
+  bool isMemberPointerToDerivedMember() const;
+  ArrayRef<const CXXRecordDecl*> getMemberPointerPath() const;
+
   void setInt(const APSInt &I) {
     assert(isInt() && "Invalid accessor");
     *(APSInt*)(char*)Data = I;
@@ -328,7 +342,7 @@
   }
   void setLValue(LValueBase B, const CharUnits &O, NoLValuePath);
   void setLValue(LValueBase B, const CharUnits &O,
-                 ArrayRef<LValuePathEntry> Path);
+                 ArrayRef<LValuePathEntry> Path, bool OnePastTheEnd);
   void setUnion(const FieldDecl *Field, const APValue &Value) {
     assert(isUnion() && "Invalid accessor");
     ((UnionData*)(char*)Data)->Field = Field;
@@ -376,6 +390,8 @@
     new ((void*)(char*)Data) UnionData();
     Kind = Union;
   }
+  void MakeMemberPointer(const ValueDecl *Member, bool IsDerivedMember,
+                         ArrayRef<const CXXRecordDecl*> Path);
 };
 
 inline raw_ostream &operator<<(raw_ostream &OS, const APValue &V) {

Modified: cfe/trunk/lib/AST/APValue.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/APValue.cpp?rev=144926&r1=144925&r2=144926&view=diff
==============================================================================
--- cfe/trunk/lib/AST/APValue.cpp (original)
+++ cfe/trunk/lib/AST/APValue.cpp Thu Nov 17 16:56:20 2011
@@ -21,7 +21,7 @@
 
 namespace {
   struct LVBase {
-    APValue::LValueBase Base;
+    llvm::PointerIntPair<APValue::LValueBase, 1, bool> BaseAndIsOnePastTheEnd;
     CharUnits Offset;
     unsigned PathLength;
   };
@@ -40,12 +40,17 @@
   };
 
   LV() { PathLength = (unsigned)-1; }
-  ~LV() { if (hasPathPtr()) delete [] PathPtr; }
+  ~LV() { resizePath(0); }
 
-  void allocPath() {
-    if (hasPathPtr()) PathPtr = new LValuePathEntry[PathLength];
+  void resizePath(unsigned Length) {
+    if (Length == PathLength)
+      return;
+    if (hasPathPtr())
+      delete [] PathPtr;
+    PathLength = Length;
+    if (hasPathPtr())
+      PathPtr = new LValuePathEntry[Length];
   }
-  void freePath() { if (hasPathPtr()) delete [] PathPtr; }
 
   bool hasPath() const { return PathLength != (unsigned)-1; }
   bool hasPathPtr() const { return hasPath() && PathLength > InlinePathSpace; }
@@ -56,6 +61,43 @@
   }
 };
 
+namespace {
+  struct MemberPointerBase {
+    llvm::PointerIntPair<const ValueDecl*, 1, bool> MemberAndIsDerivedMember;
+    unsigned PathLength;
+  };
+}
+
+struct APValue::MemberPointerData : MemberPointerBase {
+  static const unsigned InlinePathSpace =
+      (MaxSize - sizeof(MemberPointerBase)) / sizeof(const CXXRecordDecl*);
+  typedef const CXXRecordDecl *PathElem;
+  union {
+    PathElem Path[InlinePathSpace];
+    PathElem *PathPtr;
+  };
+
+  MemberPointerData() { PathLength = 0; }
+  ~MemberPointerData() { resizePath(0); }
+
+  void resizePath(unsigned Length) {
+    if (Length == PathLength)
+      return;
+    if (hasPathPtr())
+      delete [] PathPtr;
+    PathLength = Length;
+    if (hasPathPtr())
+      PathPtr = new PathElem[Length];
+  }
+
+  bool hasPathPtr() const { return PathLength > InlinePathSpace; }
+
+  PathElem *getPath() { return hasPathPtr() ? PathPtr : Path; }
+  const PathElem *getPath() const {
+    return hasPathPtr() ? PathPtr : Path;
+  }
+};
+
 // FIXME: Reduce the malloc traffic here.
 
 APValue::Arr::Arr(unsigned NumElts, unsigned Size) :
@@ -78,7 +120,8 @@
 const APValue &APValue::operator=(const APValue &RHS) {
   if (this == &RHS)
     return *this;
-  if (Kind != RHS.Kind || Kind == Array || Kind == Struct) {
+  if (Kind != RHS.Kind || Kind == Array || Kind == Struct ||
+      Kind == MemberPointer) {
     MakeUninit();
     if (RHS.isInt())
       MakeInt();
@@ -98,6 +141,10 @@
       MakeStruct(RHS.getStructNumBases(), RHS.getStructNumFields());
     else if (RHS.isUnion())
       MakeUnion();
+    else if (RHS.isMemberPointer())
+      MakeMemberPointer(RHS.getMemberPointerDecl(),
+                        RHS.isMemberPointerToDerivedMember(),
+                        RHS.getMemberPointerPath());
   }
   if (isInt())
     setInt(RHS.getInt());
@@ -112,7 +159,8 @@
     setComplexFloat(RHS.getComplexFloatReal(), RHS.getComplexFloatImag());
   else if (isLValue()) {
     if (RHS.hasLValuePath())
-      setLValue(RHS.getLValueBase(), RHS.getLValueOffset(),RHS.getLValuePath());
+      setLValue(RHS.getLValueBase(), RHS.getLValueOffset(), RHS.getLValuePath(),
+                RHS.isLValueOnePastTheEnd());
     else
       setLValue(RHS.getLValueBase(), RHS.getLValueOffset(), NoLValuePath());
   } else if (isArray()) {
@@ -149,6 +197,8 @@
     ((StructData*)(char*)Data)->~StructData();
   else if (Kind == Union)
     ((UnionData*)(char*)Data)->~UnionData();
+  else if (Kind == MemberPointer)
+    ((MemberPointerData*)(char*)Data)->~MemberPointerData();
   Kind = Uninitialized;
 }
 
@@ -217,6 +267,9 @@
   case Union:
     OS << "Union: " << getUnionValue();
     return;
+  case MemberPointer:
+    OS << "MemberPointer: <todo>";
+    return;
   }
   llvm_unreachable("Unknown APValue kind!");
 }
@@ -280,6 +333,9 @@
   case APValue::Union:
     Out << '{' << V.getUnionValue() << '}';
     return;
+  case APValue::MemberPointer:
+    Out << "MemberPointer: <todo>";
+    return;
   }
   llvm_unreachable("Unknown APValue kind!");
 }
@@ -294,7 +350,12 @@
 
 const APValue::LValueBase APValue::getLValueBase() const {
   assert(isLValue() && "Invalid accessor");
-  return ((const LV*)(const void*)Data)->Base;
+  return ((const LV*)(const void*)Data)->BaseAndIsOnePastTheEnd.getPointer();
+}
+
+bool APValue::isLValueOnePastTheEnd() const {
+  assert(isLValue() && "Invalid accessor");
+  return ((const LV*)(const void*)Data)->BaseAndIsOnePastTheEnd.getInt();
 }
 
 CharUnits &APValue::getLValueOffset() {
@@ -316,24 +377,41 @@
 void APValue::setLValue(LValueBase B, const CharUnits &O, NoLValuePath) {
   assert(isLValue() && "Invalid accessor");
   LV &LVal = *((LV*)(char*)Data);
-  LVal.freePath();
-  LVal.Base = B;
+  LVal.BaseAndIsOnePastTheEnd.setPointer(B);
+  LVal.BaseAndIsOnePastTheEnd.setInt(false);
   LVal.Offset = O;
-  LVal.PathLength = (unsigned)-1;
+  LVal.resizePath((unsigned)-1);
 }
 
 void APValue::setLValue(LValueBase B, const CharUnits &O,
-                        ArrayRef<LValuePathEntry> Path) {
+                        ArrayRef<LValuePathEntry> Path, bool IsOnePastTheEnd) {
   assert(isLValue() && "Invalid accessor");
   LV &LVal = *((LV*)(char*)Data);
-  LVal.freePath();
-  LVal.Base = B;
+  LVal.BaseAndIsOnePastTheEnd.setPointer(B);
+  LVal.BaseAndIsOnePastTheEnd.setInt(IsOnePastTheEnd);
   LVal.Offset = O;
-  LVal.PathLength = Path.size();
-  LVal.allocPath();
+  LVal.resizePath(Path.size());
   memcpy(LVal.getPath(), Path.data(), Path.size() * sizeof(LValuePathEntry));
 }
 
+const ValueDecl *APValue::getMemberPointerDecl() const {
+  assert(isMemberPointer() && "Invalid accessor");
+  const MemberPointerData &MPD = *((const MemberPointerData*)(const char*)Data);
+  return MPD.MemberAndIsDerivedMember.getPointer();
+}
+
+bool APValue::isMemberPointerToDerivedMember() const {
+  assert(isMemberPointer() && "Invalid accessor");
+  const MemberPointerData &MPD = *((const MemberPointerData*)(const char*)Data);
+  return MPD.MemberAndIsDerivedMember.getInt();
+}
+
+ArrayRef<const CXXRecordDecl*> APValue::getMemberPointerPath() const {
+  assert(isMemberPointer() && "Invalid accessor");
+  const MemberPointerData &MPD = *((const MemberPointerData*)(const char*)Data);
+  return ArrayRef<const CXXRecordDecl*>(MPD.getPath(), MPD.PathLength);
+}
+
 void APValue::MakeLValue() {
   assert(isUninit() && "Bad state change");
   assert(sizeof(LV) <= MaxSize && "LV too big");
@@ -346,3 +424,14 @@
   new ((void*)(char*)Data) Arr(InitElts, Size);
   Kind = Array;
 }
+
+void APValue::MakeMemberPointer(const ValueDecl *Member, bool IsDerivedMember,
+                                ArrayRef<const CXXRecordDecl*> Path) {
+  assert(isUninit() && "Bad state change");
+  MemberPointerData *MPD = new ((void*)(char*)Data) MemberPointerData;
+  Kind = MemberPointer;
+  MPD->MemberAndIsDerivedMember.setPointer(Member);
+  MPD->MemberAndIsDerivedMember.setInt(IsDerivedMember);
+  MPD->resizePath(Path.size());
+  memcpy(MPD->getPath(), Path.data(), Path.size()*sizeof(const CXXRecordDecl*));
+}

Modified: cfe/trunk/lib/AST/ExprConstant.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprConstant.cpp?rev=144926&r1=144925&r2=144926&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ExprConstant.cpp (original)
+++ cfe/trunk/lib/AST/ExprConstant.cpp Thu Nov 17 16:56:20 2011
@@ -126,6 +126,7 @@
                                                  V.getLValuePath());
         else
           assert(V.getLValuePath().empty() &&"Null pointer with nonempty path");
+        OnePastTheEnd = V.isLValueOnePastTheEnd();
       }
     }
 
@@ -178,6 +179,10 @@
 
   /// A core constant value. This can be the value of any constant expression,
   /// or a pointer or reference to a non-static object or function parameter.
+  ///
+  /// For an LValue, the base and offset are stored in the APValue subobject,
+  /// but the other information is stored in the SubobjectDesignator. For all
+  /// other value kinds, the value is stored directly in the APValue subobject.
   class CCValue : public APValue {
     typedef llvm::APSInt APSInt;
     typedef llvm::APFloat APFloat;
@@ -202,6 +207,9 @@
       APValue(B, O, APValue::NoLValuePath()), CallFrame(F), Designator(D) {}
     CCValue(const APValue &V, GlobalValue) :
       APValue(V), CallFrame(0), Designator(V) {}
+    CCValue(const ValueDecl *D, bool IsDerivedMember,
+            ArrayRef<const CXXRecordDecl*> Path) :
+      APValue(D, IsDerivedMember, Path) {}
 
     CallStackFrame *getLValueFrame() const {
       assert(getKind() == LValue);
@@ -375,6 +383,95 @@
       Designator = SubobjectDesignator();
     }
   };
+
+  struct MemberPtr {
+    MemberPtr() {}
+    explicit MemberPtr(const ValueDecl *Decl) :
+      DeclAndIsDerivedMember(Decl, false), Path() {}
+
+    /// The member or (direct or indirect) field referred to by this member
+    /// pointer, or 0 if this is a null member pointer.
+    const ValueDecl *getDecl() const {
+      return DeclAndIsDerivedMember.getPointer();
+    }
+    /// Is this actually a member of some type derived from the relevant class?
+    bool isDerivedMember() const {
+      return DeclAndIsDerivedMember.getInt();
+    }
+    /// Get the class which the declaration actually lives in.
+    const CXXRecordDecl *getContainingRecord() const {
+      return cast<CXXRecordDecl>(
+          DeclAndIsDerivedMember.getPointer()->getDeclContext());
+    }
+
+    void moveInto(CCValue &V) const {
+      V = CCValue(getDecl(), isDerivedMember(), Path);
+    }
+    void setFrom(const CCValue &V) {
+      assert(V.isMemberPointer());
+      DeclAndIsDerivedMember.setPointer(V.getMemberPointerDecl());
+      DeclAndIsDerivedMember.setInt(V.isMemberPointerToDerivedMember());
+      Path.clear();
+      ArrayRef<const CXXRecordDecl*> P = V.getMemberPointerPath();
+      Path.insert(Path.end(), P.begin(), P.end());
+    }
+
+    /// DeclAndIsDerivedMember - The member declaration, and a flag indicating
+    /// whether the member is a member of some class derived from the class type
+    /// of the member pointer.
+    llvm::PointerIntPair<const ValueDecl*, 1, bool> DeclAndIsDerivedMember;
+    /// Path - The path of base/derived classes from the member declaration's
+    /// class (exclusive) to the class type of the member pointer (inclusive).
+    SmallVector<const CXXRecordDecl*, 4> Path;
+
+    /// Perform a cast towards the class of the Decl (either up or down the
+    /// hierarchy).
+    bool castBack(const CXXRecordDecl *Class) {
+      assert(!Path.empty());
+      const CXXRecordDecl *Expected;
+      if (Path.size() >= 2)
+        Expected = Path[Path.size() - 2];
+      else
+        Expected = getContainingRecord();
+      if (Expected->getCanonicalDecl() != Class->getCanonicalDecl()) {
+        // C++11 [expr.static.cast]p12: In a conversion from (D::*) to (B::*),
+        // if B does not contain the original member and is not a base or
+        // derived class of the class containing the original member, the result
+        // of the cast is undefined.
+        // C++11 [conv.mem]p2 does not cover this case for a cast from (B::*) to
+        // (D::*). We consider that to be a language defect.
+        return false;
+      }
+      Path.pop_back();
+      return true;
+    }
+    /// Perform a base-to-derived member pointer cast.
+    bool castToDerived(const CXXRecordDecl *Derived) {
+      if (!getDecl())
+        return true;
+      if (!isDerivedMember()) {
+        Path.push_back(Derived);
+        return true;
+      }
+      if (!castBack(Derived))
+        return false;
+      if (Path.empty())
+        DeclAndIsDerivedMember.setInt(false);
+      return true;
+    }
+    /// Perform a derived-to-base member pointer cast.
+    bool castToBase(const CXXRecordDecl *Base) {
+      if (!getDecl())
+        return true;
+      if (Path.empty())
+        DeclAndIsDerivedMember.setInt(true);
+      if (isDerivedMember()) {
+        Path.push_back(Base);
+        return true;
+      }
+      return castBack(Base);
+    }
+  };
 }
 
 static bool Evaluate(CCValue &Result, EvalInfo &Info, const Expr *E);
@@ -382,6 +479,9 @@
                                        const LValue &This, const Expr *E);
 static bool EvaluateLValue(const Expr *E, LValue &Result, EvalInfo &Info);
 static bool EvaluatePointer(const Expr *E, LValue &Result, EvalInfo &Info);
+static bool EvaluateMemberPointer(const Expr *E, MemberPtr &Result,
+                                  EvalInfo &Info);
+static bool EvaluateTemporary(const Expr *E, LValue &Result, EvalInfo &Info);
 static bool EvaluateInteger(const Expr *E, APSInt  &Result, EvalInfo &Info);
 static bool EvaluateIntegerOrLValue(const Expr *E, CCValue &Result,
                                     EvalInfo &Info);
@@ -448,19 +548,16 @@
 
   const SubobjectDesignator &Designator = LVal.getLValueDesignator();
   // A constant expression must refer to an object or be a null pointer.
-  if (Designator.Invalid || Designator.OnePastTheEnd ||
+  if (Designator.Invalid ||
       (!LVal.getLValueBase() && !Designator.Entries.empty())) {
-    // FIXME: Check for out-of-bounds array indices.
     // FIXME: This is not a constant expression.
     Value = APValue(LVal.getLValueBase(), LVal.getLValueOffset(),
                     APValue::NoLValuePath());
     return true;
   }
 
-  // FIXME: Null references are not constant expressions.
-
   Value = APValue(LVal.getLValueBase(), LVal.getLValueOffset(),
-                  Designator.Entries);
+                  Designator.Entries, Designator.OnePastTheEnd);
   return true;
 }
 
@@ -493,23 +590,23 @@
   return Decl && IsWeakDecl(Decl);
 }
 
-static bool EvalPointerValueAsBool(const LValue &Value, bool &Result) {
+static bool EvalPointerValueAsBool(const CCValue &Value, bool &Result) {
   // A null base expression indicates a null pointer.  These are always
   // evaluatable, and they are false unless the offset is zero.
-  if (!Value.Base) {
-    Result = !Value.Offset.isZero();
+  if (!Value.getLValueBase()) {
+    Result = !Value.getLValueOffset().isZero();
     return true;
   }
 
   // Require the base expression to be a global l-value.
   // FIXME: C++11 requires such conversions. Remove this check.
-  if (!IsGlobalLValue(Value.Base)) return false;
+  if (!IsGlobalLValue(Value.getLValueBase())) return false;
 
-  // We have a non-null base expression.  These are generally known to
-  // be true, but if it'a decl-ref to a weak symbol it can be null at
-  // runtime.
+  // We have a non-null base.  These are generally known to be true, but if it's
+  // a weak declaration it can be null at runtime.
   Result = true;
-  return !IsWeakLValue(Value);
+  const ValueDecl *Decl = Value.getLValueBase().dyn_cast<const ValueDecl*>();
+  return !Decl || !IsWeakDecl(Decl);
 }
 
 static bool HandleConversionToBool(const CCValue &Val, bool &Result) {
@@ -530,11 +627,11 @@
     Result = !Val.getComplexFloatReal().isZero() ||
              !Val.getComplexFloatImag().isZero();
     return true;
-  case APValue::LValue: {
-    LValue PointerResult;
-    PointerResult.setFrom(Val);
-    return EvalPointerValueAsBool(PointerResult, Result);
-  }
+  case APValue::LValue:
+    return EvalPointerValueAsBool(Val, Result);
+  case APValue::MemberPointer:
+    Result = Val.getMemberPointerDecl();
+    return true;
   case APValue::Vector:
   case APValue::Array:
   case APValue::Struct:
@@ -596,21 +693,20 @@
   return Result;
 }
 
-/// If the given LValue refers to a base subobject of some object, find the most
-/// derived object and the corresponding complete record type. This is necessary
-/// in order to find the offset of a virtual base class.
-static bool ExtractMostDerivedObject(EvalInfo &Info, LValue &Result,
-                                     const CXXRecordDecl *&MostDerivedType) {
-  SubobjectDesignator &D = Result.Designator;
-  if (D.Invalid || !Result.Base)
+static bool FindMostDerivedObject(EvalInfo &Info, const LValue &LVal,
+                                  const CXXRecordDecl *&MostDerivedType,
+                                  unsigned &MostDerivedPathLength,
+                                  bool &MostDerivedIsArrayElement) {
+  const SubobjectDesignator &D = LVal.Designator;
+  if (D.Invalid || !LVal.Base)
     return false;
 
-  const Type *T = getType(Result.Base).getTypePtr();
+  const Type *T = getType(LVal.Base).getTypePtr();
 
   // Find path prefix which leads to the most-derived subobject.
-  unsigned MostDerivedPathLength = 0;
   MostDerivedType = T->getAsCXXRecordDecl();
-  bool MostDerivedIsArrayElement = false;
+  MostDerivedPathLength = 0;
+  MostDerivedIsArrayElement = false;
 
   for (unsigned I = 0, N = D.Entries.size(); I != N; ++I) {
     bool IsArray = T && T->isArrayType();
@@ -628,28 +724,46 @@
     }
   }
 
-  if (!MostDerivedType)
-    return false;
-
   // (B*)&d + 1 has no most-derived object.
   if (D.OnePastTheEnd && MostDerivedPathLength != D.Entries.size())
     return false;
 
-  // Remove the trailing base class path entries and their offsets.
-  const RecordDecl *RD = MostDerivedType;
-  for (unsigned I = MostDerivedPathLength, N = D.Entries.size(); I != N; ++I) {
+  return MostDerivedType != 0;
+}
+
+static void TruncateLValueBasePath(EvalInfo &Info, LValue &Result,
+                                   const RecordDecl *TruncatedType,
+                                   unsigned TruncatedElements,
+                                   bool IsArrayElement) {
+  SubobjectDesignator &D = Result.Designator;
+  const RecordDecl *RD = TruncatedType;
+  for (unsigned I = TruncatedElements, N = D.Entries.size(); I != N; ++I) {
     const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD);
     const CXXRecordDecl *Base = getAsBaseClass(D.Entries[I]);
-    if (isVirtualBaseClass(D.Entries[I])) {
-      assert(I == MostDerivedPathLength &&
-             "virtual base class must be immediately after most-derived class");
+    if (isVirtualBaseClass(D.Entries[I]))
       Result.Offset -= Layout.getVBaseClassOffset(Base);
-    } else
+    else
       Result.Offset -= Layout.getBaseClassOffset(Base);
     RD = Base;
   }
-  D.Entries.resize(MostDerivedPathLength);
-  D.ArrayElement = MostDerivedIsArrayElement;
+  D.Entries.resize(TruncatedElements);
+  D.ArrayElement = IsArrayElement;
+}
+
+/// If the given LValue refers to a base subobject of some object, find the most
+/// derived object and the corresponding complete record type. This is necessary
+/// in order to find the offset of a virtual base class.
+static bool ExtractMostDerivedObject(EvalInfo &Info, LValue &Result,
+                                     const CXXRecordDecl *&MostDerivedType) {
+  unsigned MostDerivedPathLength;
+  bool MostDerivedIsArrayElement;
+  if (!FindMostDerivedObject(Info, Result, MostDerivedType,
+                             MostDerivedPathLength, MostDerivedIsArrayElement))
+    return false;
+
+  // Remove the trailing base class path entries and their offsets.
+  TruncateLValueBasePath(Info, Result, MostDerivedType, MostDerivedPathLength,
+                         MostDerivedIsArrayElement);
   return true;
 }
 
@@ -969,10 +1083,141 @@
   if (Object->isGLValue())
     return EvaluateLValue(Object, This, Info);
 
-  // Implicitly promote a prvalue *this object to a glvalue.
-  This.set(Object, Info.CurrentCall);
-  return EvaluateConstantExpression(Info.CurrentCall->Temporaries[Object], Info,
-                                    This, Object);
+  if (Object->getType()->isLiteralType())
+    return EvaluateTemporary(Object, This, Info);
+
+  return false;
+}
+
+/// HandleMemberPointerAccess - Evaluate a member access operation and build an
+/// lvalue referring to the result.
+///
+/// \param Info - Information about the ongoing evaluation.
+/// \param BO - The member pointer access operation.
+/// \param LV - Filled in with a reference to the resulting object.
+/// \param IncludeMember - Specifies whether the member itself is included in
+///        the resulting LValue subobject designator. This is not possible when
+///        creating a bound member function.
+/// \return The field or method declaration to which the member pointer refers,
+///         or 0 if evaluation fails.
+static const ValueDecl *HandleMemberPointerAccess(EvalInfo &Info,
+                                                  const BinaryOperator *BO,
+                                                  LValue &LV,
+                                                  bool IncludeMember = true) {
+  assert(BO->getOpcode() == BO_PtrMemD || BO->getOpcode() == BO_PtrMemI);
+
+  if (!EvaluateObjectArgument(Info, BO->getLHS(), LV))
+    return 0;
+
+  MemberPtr MemPtr;
+  if (!EvaluateMemberPointer(BO->getRHS(), MemPtr, Info))
+    return 0;
+
+  // C++11 [expr.mptr.oper]p6: If the second operand is the null pointer to
+  // member value, the behavior is undefined.
+  if (!MemPtr.getDecl())
+    return 0;
+
+  if (MemPtr.isDerivedMember()) {
+    // This is a member of some derived class. Truncate LV appropriately.
+    const CXXRecordDecl *MostDerivedType;
+    unsigned MostDerivedPathLength;
+    bool MostDerivedIsArrayElement;
+    if (!FindMostDerivedObject(Info, LV, MostDerivedType, MostDerivedPathLength,
+                               MostDerivedIsArrayElement))
+      return 0;
+
+    // The end of the derived-to-base path for the base object must match the
+    // derived-to-base path for the member pointer.
+    if (MostDerivedPathLength + MemPtr.Path.size() >
+        LV.Designator.Entries.size())
+      return 0;
+    unsigned PathLengthToMember =
+        LV.Designator.Entries.size() - MemPtr.Path.size();
+    for (unsigned I = 0, N = MemPtr.Path.size(); I != N; ++I) {
+      const CXXRecordDecl *LVDecl = getAsBaseClass(
+          LV.Designator.Entries[PathLengthToMember + I]);
+      const CXXRecordDecl *MPDecl = MemPtr.Path[I];
+      if (LVDecl->getCanonicalDecl() != MPDecl->getCanonicalDecl())
+        return 0;
+    }
+
+    // Truncate the lvalue to the appropriate derived class.
+    bool ResultIsArray = false;
+    if (PathLengthToMember == MostDerivedPathLength)
+      ResultIsArray = MostDerivedIsArrayElement;
+    TruncateLValueBasePath(Info, LV, MemPtr.getContainingRecord(),
+                           PathLengthToMember, ResultIsArray);
+  } else if (!MemPtr.Path.empty()) {
+    // Extend the LValue path with the member pointer's path.
+    LV.Designator.Entries.reserve(LV.Designator.Entries.size() +
+                                  MemPtr.Path.size() + IncludeMember);
+
+    // Walk down to the appropriate base class.
+    QualType LVType = BO->getLHS()->getType();
+    if (const PointerType *PT = LVType->getAs<PointerType>())
+      LVType = PT->getPointeeType();
+    const CXXRecordDecl *RD = LVType->getAsCXXRecordDecl();
+    assert(RD && "member pointer access on non-class-type expression");
+    // The first class in the path is that of the lvalue.
+    for (unsigned I = 1, N = MemPtr.Path.size(); I != N; ++I) {
+      const CXXRecordDecl *Base = MemPtr.Path[N - I - 1];
+      HandleLValueDirectBase(Info, LV, RD, Base);
+      RD = Base;
+    }
+    // Finally cast to the class containing the member.
+    HandleLValueDirectBase(Info, LV, RD, MemPtr.getContainingRecord());
+  }
+
+  // Add the member. Note that we cannot build bound member functions here.
+  if (IncludeMember) {
+    // FIXME: Deal with IndirectFieldDecls.
+    const FieldDecl *FD = dyn_cast<FieldDecl>(MemPtr.getDecl());
+    if (!FD) return 0;
+    HandleLValueMember(Info, LV, FD);
+  }
+
+  return MemPtr.getDecl();
+}
+
+/// HandleBaseToDerivedCast - Apply the given base-to-derived cast operation on
+/// the provided lvalue, which currently refers to the base object.
+static bool HandleBaseToDerivedCast(EvalInfo &Info, const CastExpr *E,
+                                    LValue &Result) {
+  const CXXRecordDecl *MostDerivedType;
+  unsigned MostDerivedPathLength;
+  bool MostDerivedIsArrayElement;
+
+  // Check this cast doesn't take us outside the object.
+  if (!FindMostDerivedObject(Info, Result, MostDerivedType,
+                             MostDerivedPathLength,
+                             MostDerivedIsArrayElement))
+    return false;
+  SubobjectDesignator &D = Result.Designator;
+  if (MostDerivedPathLength + E->path_size() > D.Entries.size())
+    return false;
+
+  // Check the type of the final cast. We don't need to check the path,
+  // since a cast can only be formed if the path is unique.
+  unsigned NewEntriesSize = D.Entries.size() - E->path_size();
+  bool ResultIsArray = false;
+  QualType TargetQT = E->getType();
+  if (const PointerType *PT = TargetQT->getAs<PointerType>())
+    TargetQT = PT->getPointeeType();
+  const CXXRecordDecl *TargetType = TargetQT->getAsCXXRecordDecl();
+  const CXXRecordDecl *FinalType;
+  if (NewEntriesSize == MostDerivedPathLength) {
+    ResultIsArray = MostDerivedIsArrayElement;
+    FinalType = MostDerivedType;
+  } else
+    FinalType = getAsBaseClass(D.Entries[NewEntriesSize - 1]);
+  if (FinalType->getCanonicalDecl() != TargetType->getCanonicalDecl())
+    return false;
+
+  // Truncate the lvalue to the appropriate derived class.
+  TruncateLValueBasePath(Info, Result, TargetType, NewEntriesSize,
+                         ResultIsArray);
+  return true;
 }
 
 namespace {
@@ -1273,6 +1518,28 @@
   RetTy VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E)
     { return StmtVisitorTy::Visit(E->getExpr()); }
 
+  RetTy VisitBinaryOperator(const BinaryOperator *E) {
+    switch (E->getOpcode()) {
+    default:
+      return DerivedError(E);
+
+    case BO_Comma:
+      VisitIgnoredValue(E->getLHS());
+      return StmtVisitorTy::Visit(E->getRHS());
+
+    case BO_PtrMemD:
+    case BO_PtrMemI: {
+      LValue Obj;
+      if (!HandleMemberPointerAccess(Info, E, Obj))
+        return false;
+      CCValue Result;
+      if (!HandleLValueToRValueConversion(Info, E->getType(), Obj, Result))
+        return false;
+      return DerivedSuccess(Result, E);
+    }
+    }
+  }
+
   RetTy VisitBinaryConditionalOperator(const BinaryConditionalOperator *E) {
     OpaqueValueEvaluation opaque(Info, E->getOpaqueValue(), E->getCommon());
     if (opaque.hasError())
@@ -1303,7 +1570,7 @@
   }
 
   RetTy VisitCallExpr(const CallExpr *E) {
-    const Expr *Callee = E->getCallee();
+    const Expr *Callee = E->getCallee()->IgnoreParens();
     QualType CalleeType = Callee->getType();
 
     const FunctionDecl *FD = 0;
@@ -1312,17 +1579,24 @@
 
     // Extract function decl and 'this' pointer from the callee.
     if (CalleeType->isSpecificBuiltinType(BuiltinType::BoundMember)) {
-      // Explicit bound member calls, such as x.f() or p->g();
-      // FIXME: Handle a BinaryOperator callee ('.*' or '->*').
-      const MemberExpr *ME = dyn_cast<MemberExpr>(Callee->IgnoreParens());
-      if (!ME)
+      if (const MemberExpr *ME = dyn_cast<MemberExpr>(Callee)) {
+        // Explicit bound member calls, such as x.f() or p->g();
+        if (!EvaluateObjectArgument(Info, ME->getBase(), ThisVal))
+          return DerivedError(ME->getBase());
+        This = &ThisVal;
+        FD = dyn_cast<FunctionDecl>(ME->getMemberDecl());
+        if (!FD)
+          return DerivedError(ME);
+      } else if (const BinaryOperator *BE = dyn_cast<BinaryOperator>(Callee)) {
+        // Indirect bound member calls ('.*' or '->*').
+        const ValueDecl *Member = HandleMemberPointerAccess(Info, BE, ThisVal,
+                                                            false);
+        This = &ThisVal;
+        FD = dyn_cast_or_null<FunctionDecl>(Member);
+        if (!FD)
+          return DerivedError(Callee);
+      } else
         return DerivedError(Callee);
-      if (!EvaluateObjectArgument(Info, ME->getBase(), ThisVal))
-        return DerivedError(ME->getBase());
-      This = &ThisVal;
-      FD = dyn_cast<FunctionDecl>(ME->getMemberDecl());
-      if (!FD)
-        return DerivedError(ME);
     } else if (CalleeType->isFunctionPointerType()) {
       CCValue Call;
       if (!Evaluate(Call, Info, Callee) || !Call.isLValue() ||
@@ -1381,6 +1655,9 @@
   RetTy VisitCXXScalarValueInitExpr(const CXXScalarValueInitExpr *E) {
     return DerivedValueInitialization(E);
   }
+  RetTy VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *E) {
+    return DerivedValueInitialization(E);
+  }
 
   /// A member expression where the object is a prvalue is itself a prvalue.
   RetTy VisitMemberExpr(const MemberExpr *E) {
@@ -1438,6 +1715,118 @@
 }
 
 //===----------------------------------------------------------------------===//
+// Common base class for lvalue and temporary evaluation.
+//===----------------------------------------------------------------------===//
+namespace {
+template<class Derived>
+class LValueExprEvaluatorBase
+  : public ExprEvaluatorBase<Derived, bool> {
+protected:
+  LValue &Result;
+  typedef LValueExprEvaluatorBase LValueExprEvaluatorBaseTy;
+  typedef ExprEvaluatorBase<Derived, bool> ExprEvaluatorBaseTy;
+
+  bool Success(APValue::LValueBase B) {
+    Result.set(B);
+    return true;
+  }
+
+public:
+  LValueExprEvaluatorBase(EvalInfo &Info, LValue &Result) :
+    ExprEvaluatorBaseTy(Info), Result(Result) {}
+
+  bool Success(const CCValue &V, const Expr *E) {
+    Result.setFrom(V);
+    return true;
+  }
+  bool Error(const Expr *E) {
+    return false;
+  }
+
+  bool CheckValidLValue() {
+    // C++11 [basic.lval]p1: An lvalue designates a function or an object. Hence
+    // there are no null references, nor once-past-the-end references.
+    // FIXME: Check for one-past-the-end array indices
+    return Result.Base && !Result.Designator.Invalid &&
+           !Result.Designator.OnePastTheEnd;
+  }
+
+  bool VisitMemberExpr(const MemberExpr *E) {
+    // Handle non-static data members.
+    QualType BaseTy;
+    if (E->isArrow()) {
+      if (!EvaluatePointer(E->getBase(), Result, this->Info))
+        return false;
+      BaseTy = E->getBase()->getType()->getAs<PointerType>()->getPointeeType();
+    } else {
+      if (!this->Visit(E->getBase()))
+        return false;
+      BaseTy = E->getBase()->getType();
+    }
+    // FIXME: In C++11, require the result to be a valid lvalue.
+
+    const FieldDecl *FD = dyn_cast<FieldDecl>(E->getMemberDecl());
+    // FIXME: Handle IndirectFieldDecls
+    if (!FD) return false;
+    assert(BaseTy->getAs<RecordType>()->getDecl()->getCanonicalDecl() ==
+           FD->getParent()->getCanonicalDecl() && "record / field mismatch");
+    (void)BaseTy;
+
+    HandleLValueMember(this->Info, Result, FD);
+
+    if (FD->getType()->isReferenceType()) {
+      CCValue RefValue;
+      if (!HandleLValueToRValueConversion(this->Info, FD->getType(), Result,
+                                          RefValue))
+        return false;
+      return Success(RefValue, E);
+    }
+    return true;
+  }
+
+  bool VisitBinaryOperator(const BinaryOperator *E) {
+    switch (E->getOpcode()) {
+    default:
+      return ExprEvaluatorBaseTy::VisitBinaryOperator(E);
+
+    case BO_PtrMemD:
+    case BO_PtrMemI:
+      return HandleMemberPointerAccess(this->Info, E, Result);
+    }
+  }
+
+  bool VisitCastExpr(const CastExpr *E) {
+    switch (E->getCastKind()) {
+    default:
+      return ExprEvaluatorBaseTy::VisitCastExpr(E);
+
+    case CK_DerivedToBase:
+    case CK_UncheckedDerivedToBase: {
+      if (!this->Visit(E->getSubExpr()))
+        return false;
+      if (!CheckValidLValue())
+        return false;
+
+      // Now figure out the necessary offset to add to the base LV to get from
+      // the derived class to the base class.
+      QualType Type = E->getSubExpr()->getType();
+
+      for (CastExpr::path_const_iterator PathI = E->path_begin(),
+           PathE = E->path_end(); PathI != PathE; ++PathI) {
+        if (!HandleLValueBase(this->Info, Result, Type->getAsCXXRecordDecl(),
+                              *PathI))
+          return false;
+        Type = (*PathI)->getType();
+      }
+
+      return true;
+    }
+    }
+  }
+};
+}
+
+//===----------------------------------------------------------------------===//
 // LValue Evaluation
 //
 // This is used for evaluating lvalues (in C and C++), xvalues (in C++11),
@@ -1465,27 +1854,11 @@
 //===----------------------------------------------------------------------===//
 namespace {
 class LValueExprEvaluator
-  : public ExprEvaluatorBase<LValueExprEvaluator, bool> {
-  LValue &Result;
-  const Decl *PrevDecl;
-
-  bool Success(APValue::LValueBase B) {
-    Result.set(B);
-    return true;
-  }
+  : public LValueExprEvaluatorBase<LValueExprEvaluator> {
 public:
+  LValueExprEvaluator(EvalInfo &Info, LValue &Result) :
+    LValueExprEvaluatorBaseTy(Info, Result) {}
 
-  LValueExprEvaluator(EvalInfo &info, LValue &Result) :
-    ExprEvaluatorBaseTy(info), Result(Result), PrevDecl(0) {}
-
-  bool Success(const CCValue &V, const Expr *E) {
-    Result.setFrom(V);
-    return true;
-  }
-  bool Error(const Expr *E) {
-    return false;
-  }
-  
   bool VisitVarDecl(const Expr *E, const VarDecl *VD);
 
   bool VisitDeclRefExpr(const DeclRefExpr *E);
@@ -1501,7 +1874,7 @@
   bool VisitCastExpr(const CastExpr *E) {
     switch (E->getCastKind()) {
     default:
-      return ExprEvaluatorBaseTy::VisitCastExpr(E);
+      return LValueExprEvaluatorBaseTy::VisitCastExpr(E);
 
     case CK_LValueBitCast:
       if (!Visit(E->getSubExpr()))
@@ -1509,24 +1882,12 @@
       Result.Designator.setInvalid();
       return true;
 
-    case CK_DerivedToBase:
-    case CK_UncheckedDerivedToBase: {
+    case CK_BaseToDerived:
       if (!Visit(E->getSubExpr()))
         return false;
-
-      // Now figure out the necessary offset to add to the base LV to get from
-      // the derived class to the base class.
-      QualType Type = E->getSubExpr()->getType();
-
-      for (CastExpr::path_const_iterator PathI = E->path_begin(),
-           PathE = E->path_end(); PathI != PathE; ++PathI) {
-        if (!HandleLValueBase(Info, Result, Type->getAsCXXRecordDecl(), *PathI))
-          return false;
-        Type = (*PathI)->getType();
-      }
-
-      return true;
-    }
+      if (!CheckValidLValue())
+        return false;
+      return HandleBaseToDerivedCast(Info, E, Result);
     }
   }
 
@@ -1573,9 +1934,25 @@
 
 bool LValueExprEvaluator::VisitMaterializeTemporaryExpr(
     const MaterializeTemporaryExpr *E) {
+  if (E->GetTemporaryExpr()->isRValue()) {
+    if (E->getType()->isRecordType() && E->getType()->isLiteralType())
+      return EvaluateTemporary(E->GetTemporaryExpr(), Result, Info);
+
+    Result.set(E, Info.CurrentCall);
+    return EvaluateConstantExpression(Info.CurrentCall->Temporaries[E], Info,
+                                      Result, E->GetTemporaryExpr());
+  }
+
+  // Materialization of an lvalue temporary occurs when we need to force a copy
+  // (for instance, if it's a bitfield).
+  // FIXME: The AST should contain an lvalue-to-rvalue node for such cases.
+  if (!Visit(E->GetTemporaryExpr()))
+    return false;
+  if (!HandleLValueToRValueConversion(Info, E->getType(), Result,
+                                      Info.CurrentCall->Temporaries[E]))
+    return false;
   Result.set(E, Info.CurrentCall);
-  return EvaluateConstantExpression(Info.CurrentCall->Temporaries[E], Info,
-                                    Result, E->GetTemporaryExpr());
+  return true;
 }
 
 bool
@@ -1602,32 +1979,7 @@
   }
 
   // Handle non-static data members.
-  QualType BaseTy;
-  if (E->isArrow()) {
-    if (!EvaluatePointer(E->getBase(), Result, Info))
-      return false;
-    BaseTy = E->getBase()->getType()->getAs<PointerType>()->getPointeeType();
-  } else {
-    if (!Visit(E->getBase()))
-      return false;
-    BaseTy = E->getBase()->getType();
-  }
-
-  const FieldDecl *FD = dyn_cast<FieldDecl>(E->getMemberDecl());
-  if (!FD) return false;
-  assert(BaseTy->getAs<RecordType>()->getDecl()->getCanonicalDecl() ==
-         FD->getParent()->getCanonicalDecl() && "record / field mismatch");
-  (void)BaseTy;
-
-  HandleLValueMember(Info, Result, FD);
-
-  if (FD->getType()->isReferenceType()) {
-    CCValue RefValue;
-    if (!HandleLValueToRValueConversion(Info, FD->getType(), Result, RefValue))
-      return false;
-    return Success(RefValue, E);
-  }
-  return true;
+  return LValueExprEvaluatorBaseTy::VisitMemberExpr(E);
 }
 
 bool LValueExprEvaluator::VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
@@ -1645,10 +1997,12 @@
     = Index.isSigned() ? Index.getSExtValue()
                        : static_cast<int64_t>(Index.getZExtValue());
 
+  // FIXME: In C++11, require the result to be a valid lvalue.
   return HandleLValueArrayAdjustment(Info, Result, E->getType(), IndexValue);
 }
 
 bool LValueExprEvaluator::VisitUnaryDeref(const UnaryOperator *E) {
+  // FIXME: In C++11, require the result to be a valid lvalue.
   return EvaluatePointer(E->getSubExpr(), Result, Info);
 }
 
@@ -1694,8 +2048,6 @@
       return Success(E);
     return false;
   }
-  bool VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *E)
-      { return ValueInitialization(E); }
   bool VisitCXXThisExpr(const CXXThisExpr *E) {
     if (!Info.CurrentCall->This)
       return false;
@@ -1715,7 +2067,7 @@
 bool PointerExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
   if (E->getOpcode() != BO_Add &&
       E->getOpcode() != BO_Sub)
-    return false;
+    return ExprEvaluatorBaseTy::VisitBinaryOperator(E);
 
   const Expr *PExp = E->getLHS();
   const Expr *IExp = E->getRHS();
@@ -1735,6 +2087,7 @@
     AdditionalOffset = -AdditionalOffset;
 
   QualType Pointee = PExp->getType()->getAs<PointerType>()->getPointeeType();
+  // FIXME: In C++11, require the result to be a valid lvalue.
   return HandleLValueArrayAdjustment(Info, Result, Pointee, AdditionalOffset);
 }
 
@@ -1762,6 +2115,8 @@
   case CK_UncheckedDerivedToBase: {
     if (!EvaluatePointer(E->getSubExpr(), Result, Info))
       return false;
+    if (!Result.Base && Result.Offset.isZero())
+      return true;
 
     // Now figure out the necessary offset to add to the base LV to get from
     // the derived class to the base class.
@@ -1778,6 +2133,13 @@
     return true;
   }
 
+  case CK_BaseToDerived:
+    if (!Visit(E->getSubExpr()))
+      return false;
+    if (!Result.Base && Result.Offset.isZero())
+      return true;
+    return HandleBaseToDerivedCast(Info, E, Result);
+
   case CK_NullToPointer:
     return ValueInitialization(E);
 
@@ -1801,11 +2163,15 @@
     }
   }
   case CK_ArrayToPointerDecay:
-    // FIXME: Support array-to-pointer decay on array rvalues.
-    if (!SubExpr->isGLValue())
-      return Error(E);
-    if (!EvaluateLValue(SubExpr, Result, Info))
-      return false;
+    if (SubExpr->isGLValue()) {
+      if (!EvaluateLValue(SubExpr, Result, Info))
+        return false;
+    } else {
+      Result.set(SubExpr, Info.CurrentCall);
+      if (!EvaluateConstantExpression(Info.CurrentCall->Temporaries[SubExpr],
+                                      Info, Result, SubExpr))
+        return false;
+    }
     // The result is a pointer to the first element of the array.
     Result.Designator.addIndex(0);
     return true;
@@ -1825,6 +2191,96 @@
 }
 
 //===----------------------------------------------------------------------===//
+// Member Pointer Evaluation
+//===----------------------------------------------------------------------===//
+
+namespace {
+class MemberPointerExprEvaluator
+  : public ExprEvaluatorBase<MemberPointerExprEvaluator, bool> {
+  MemberPtr &Result;
+
+  bool Success(const ValueDecl *D) {
+    Result = MemberPtr(D);
+    return true;
+  }
+public:
+
+  MemberPointerExprEvaluator(EvalInfo &Info, MemberPtr &Result)
+    : ExprEvaluatorBaseTy(Info), Result(Result) {}
+
+  bool Success(const CCValue &V, const Expr *E) {
+    Result.setFrom(V);
+    return true;
+  }
+  bool Error(const Stmt *S) {
+    return false;
+  }
+  bool ValueInitialization(const Expr *E) {
+    return Success((const ValueDecl*)0);
+  }
+
+  bool VisitCastExpr(const CastExpr *E);
+  bool VisitUnaryAddrOf(const UnaryOperator *E);
+};
+} // end anonymous namespace
+
+static bool EvaluateMemberPointer(const Expr *E, MemberPtr &Result,
+                                  EvalInfo &Info) {
+  assert(E->isRValue() && E->getType()->isMemberPointerType());
+  return MemberPointerExprEvaluator(Info, Result).Visit(E);
+}
+
+bool MemberPointerExprEvaluator::VisitCastExpr(const CastExpr *E) {
+  switch (E->getCastKind()) {
+  default:
+    return ExprEvaluatorBaseTy::VisitCastExpr(E);
+
+  case CK_NullToMemberPointer:
+    return ValueInitialization(E);
+
+  case CK_BaseToDerivedMemberPointer: {
+    if (!Visit(E->getSubExpr()))
+      return false;
+    if (E->path_empty())
+      return true;
+    // Base-to-derived member pointer casts store the path in derived-to-base
+    // order, so iterate backwards. The CXXBaseSpecifier also provides us with
+    // the wrong end of the derived->base arc, so stagger the path by one class.
+    typedef std::reverse_iterator<CastExpr::path_const_iterator> ReverseIter;
+    for (ReverseIter PathI(E->path_end() - 1), PathE(E->path_begin());
+         PathI != PathE; ++PathI) {
+      assert(!(*PathI)->isVirtual() && "memptr cast through vbase");
+      const CXXRecordDecl *Derived = (*PathI)->getType()->getAsCXXRecordDecl();
+      if (!Result.castToDerived(Derived))
+        return false;
+    }
+    const Type *FinalTy = E->getType()->castAs<MemberPointerType>()->getClass();
+    if (!Result.castToDerived(FinalTy->getAsCXXRecordDecl()))
+      return false;
+    return true;
+  }
+
+  case CK_DerivedToBaseMemberPointer:
+    if (!Visit(E->getSubExpr()))
+      return false;
+    for (CastExpr::path_const_iterator PathI = E->path_begin(),
+         PathE = E->path_end(); PathI != PathE; ++PathI) {
+      assert(!(*PathI)->isVirtual() && "memptr cast through vbase");
+      const CXXRecordDecl *Base = (*PathI)->getType()->getAsCXXRecordDecl();
+      if (!Result.castToBase(Base))
+        return false;
+    }
+    return true;
+  }
+}
+
+bool MemberPointerExprEvaluator::VisitUnaryAddrOf(const UnaryOperator *E) {
+  // C++11 [expr.unary.op]p3 has very strict rules on how the address of a
+  // member can be formed.
+  return Success(cast<DeclRefExpr>(E->getSubExpr())->getDecl());
+}
+
+//===----------------------------------------------------------------------===//
 // Record Evaluation
 //===----------------------------------------------------------------------===//
 
@@ -1957,6 +2413,55 @@
 }
 
 //===----------------------------------------------------------------------===//
+// Temporary Evaluation
+//
+// Temporaries are represented in the AST as rvalues, but generally behave like
+// lvalues. The full-object of which the temporary is a subobject is implicitly
+// materialized so that a reference can bind to it.
+//===----------------------------------------------------------------------===//
+namespace {
+class TemporaryExprEvaluator
+  : public LValueExprEvaluatorBase<TemporaryExprEvaluator> {
+public:
+  TemporaryExprEvaluator(EvalInfo &Info, LValue &Result) :
+    LValueExprEvaluatorBaseTy(Info, Result) {}
+
+  /// Visit an expression which constructs the value of this temporary.
+  bool VisitConstructExpr(const Expr *E) {
+    Result.set(E, Info.CurrentCall);
+    return EvaluateConstantExpression(Info.CurrentCall->Temporaries[E], Info,
+                                      Result, E);
+  }
+
+  bool VisitCastExpr(const CastExpr *E) {
+    switch (E->getCastKind()) {
+    default:
+      return LValueExprEvaluatorBaseTy::VisitCastExpr(E);
+
+    case CK_ConstructorConversion:
+      return VisitConstructExpr(E->getSubExpr());
+    }
+  }
+  bool VisitInitListExpr(const InitListExpr *E) {
+    return VisitConstructExpr(E);
+  }
+  bool VisitCXXConstructExpr(const CXXConstructExpr *E) {
+    return VisitConstructExpr(E);
+  }
+  bool VisitCallExpr(const CallExpr *E) {
+    return VisitConstructExpr(E);
+  }
+};
+} // end anonymous namespace
+
+/// Evaluate an expression of record type as a temporary.
+static bool EvaluateTemporary(const Expr *E, LValue &Result, EvalInfo &Info) {
+  assert(E->isRValue() && E->getType()->isRecordType() &&
+         E->getType()->isLiteralType());
+  return TemporaryExprEvaluator(Info, Result).Visit(E);
+}
+
+//===----------------------------------------------------------------------===//
 // Vector Evaluation
 //===----------------------------------------------------------------------===//
 
@@ -2183,9 +2688,8 @@
                                         Subobject, &VIE);
     }
 
-    // FIXME: We also get CXXConstructExpr, in cases like:
-    //   struct S { constexpr S(); }; constexpr S s[10];
     bool VisitInitListExpr(const InitListExpr *E);
+    bool VisitCXXConstructExpr(const CXXConstructExpr *E);
   };
 } // end anonymous namespace
 
@@ -2225,6 +2729,34 @@
                                     Subobject, E->getArrayFiller());
 }
 
+bool ArrayExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E) {
+  const ConstantArrayType *CAT = Info.Ctx.getAsConstantArrayType(E->getType());
+  if (!CAT)
+    return false;
+
+  Result = APValue(APValue::UninitArray(), 0, CAT->getSize().getZExtValue());
+  if (!Result.hasArrayFiller())
+    return true;
+
+  const CXXConstructorDecl *FD = E->getConstructor();
+  const FunctionDecl *Definition = 0;
+  FD->getBody(Definition);
+
+  if (!Definition || !Definition->isConstexpr() || Definition->isInvalidDecl())
+    return false;
+
+  // FIXME: The Subobject here isn't necessarily right. This rarely matters,
+  // but sometimes does:
+  //   struct S { constexpr S() : p(&p) {} void *p; };
+  //   S s[10];
+  LValue Subobject = This;
+  Subobject.Designator.addIndex(0);
+  llvm::ArrayRef<const Expr*> Args(E->getArgs(), E->getNumArgs());
+  return HandleConstructorCall(Subobject, Args,
+                               cast<CXXConstructorDecl>(Definition),
+                               Info, Result.getArrayFiller());
+}
+
 //===----------------------------------------------------------------------===//
 // Integer Evaluation
 //
@@ -2832,9 +3364,8 @@
   }
   if (!LHSTy->isIntegralOrEnumerationType() ||
       !RHSTy->isIntegralOrEnumerationType()) {
-    // We can't continue from here for non-integral types, and they
-    // could potentially confuse the following operations.
-    return false;
+    // We can't continue from here for non-integral types.
+    return ExprEvaluatorBaseTy::VisitBinaryOperator(E);
   }
 
   // The LHS of a constant expr is always evaluated and needed.
@@ -3423,17 +3954,9 @@
 }
 
 bool FloatExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
-  if (E->getOpcode() == BO_Comma) {
-    VisitIgnoredValue(E->getLHS());
-    return Visit(E->getRHS());
-  }
-
-  // We can't evaluate pointer-to-member operations or assignments.
-  if (E->isPtrMemOp() || E->isAssignmentOp())
-    return false;
+  if (E->isPtrMemOp() || E->isAssignmentOp() || E->getOpcode() == BO_Comma)
+    return ExprEvaluatorBaseTy::VisitBinaryOperator(E);
 
-  // FIXME: Diagnostics?  I really don't understand how the warnings
-  // and errors are supposed to work.
   APFloat RHS(0.0);
   if (!EvaluateFloat(E->getLHS(), Result, Info))
     return false;
@@ -3695,14 +4218,9 @@
 }
 
 bool ComplexExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
-  if (E->isPtrMemOp() || E->isAssignmentOp())
+  if (E->isPtrMemOp() || E->isAssignmentOp() || E->getOpcode() == BO_Comma)
     return ExprEvaluatorBaseTy::VisitBinaryOperator(E);
 
-  if (E->getOpcode() == BO_Comma) {
-    VisitIgnoredValue(E->getLHS());
-    return Visit(E->getRHS());
-  }
-
   if (!Visit(E->getLHS()))
     return false;
 
@@ -3884,8 +4402,11 @@
       return false;
     C.moveInto(Result);
   } else if (E->getType()->isMemberPointerType()) {
-    // FIXME: Implement evaluation of pointer-to-member types.
-    return false;
+    MemberPtr P;
+    if (!EvaluateMemberPointer(E, P, Info))
+      return false;
+    P.moveInto(Result);
+    return true;
   } else if (E->getType()->isArrayType() && E->getType()->isLiteralType()) {
     LValue LV;
     LV.set(E, Info.CurrentCall);
@@ -3935,7 +4456,9 @@
   // FIXME: Evaluating initializers for large arrays can cause performance
   // problems, and we don't use such values yet. Once we have a more efficient
   // array representation, this should be reinstated, and used by CodeGen.
-  if (isRValue() && getType()->isArrayType())
+  // The same problem affects large records.
+  if (isRValue() && (getType()->isArrayType() || getType()->isRecordType()) &&
+      !Ctx.getLangOptions().CPlusPlus0x)
     return false;
 
   EvalInfo Info(Ctx, Result);

Modified: cfe/trunk/lib/CodeGen/CGExprConstant.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGExprConstant.cpp?rev=144926&r1=144925&r2=144926&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGExprConstant.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGExprConstant.cpp Thu Nov 17 16:56:20 2011
@@ -1074,6 +1074,7 @@
     case APValue::Array:
     case APValue::Struct:
     case APValue::Union:
+    case APValue::MemberPointer:
       break;
     }
   }

Modified: cfe/trunk/test/CodeGen/object-size.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/object-size.c?rev=144926&r1=144925&r2=144926&view=diff
==============================================================================
--- cfe/trunk/test/CodeGen/object-size.c (original)
+++ cfe/trunk/test/CodeGen/object-size.c Thu Nov 17 16:56:20 2011
@@ -55,8 +55,7 @@
 // CHECK: define void @test7
 void test7() {
   int i;
-  // CHECK-NOT:   __strcpy_chk
-  // CHECK:       = call i8* @__inline_strcpy_chk(i8* getelementptr inbounds ([63 x i8]* @gbuf, i32 0, i32 0), i8* getelementptr inbounds ([9 x i8]* @.str, i32 0, i32 0))
+  // CHECK:     = call i64 @llvm.objectsize.i64(i8* {{.*}}@gbuf{{.*}}, i1 false)
   strcpy((++i, gbuf), "Hi there");
 }
 

Modified: cfe/trunk/test/CodeGenCXX/const-base-cast.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/const-base-cast.cpp?rev=144926&r1=144925&r2=144926&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCXX/const-base-cast.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/const-base-cast.cpp Thu Nov 17 16:56:20 2011
@@ -1,8 +1,7 @@
-// RUN: %clang_cc1 -O1 -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -emit-llvm %s -o - | FileCheck %s
 
 // Check that the following construct, which is similar to one which occurs
-// in Firefox, is not misfolded (folding it correctly would be a bonus, but
-// that doesn't work at the moment, hence the -O1 in the runline).
+// in Firefox, is folded correctly.
 struct A { char x; };
 struct B { char y; };
 struct C : A,B {};

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=144926&r1=144925&r2=144926&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp (original)
+++ cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp Thu Nov 17 16:56:20 2011
@@ -94,7 +94,7 @@
   template<int n> struct IntParam {};
 
   using IntParam0 = IntParam<0>;
-  // FIXME: This should be accepted once we do constexpr function invocation.
+  // FIXME: This should be accepted once we implement the new ICE rules.
   using IntParam0 = IntParam<id(0)>; // expected-error {{not an integral constant expression}}
   using IntParam0 = IntParam<MemberZero().zero>; // expected-error {{did you mean to call it with no arguments?}} expected-error {{not an integral constant expression}}
 }
@@ -104,7 +104,7 @@
     switch (n) {
     // FIXME: Produce the 'add ()' fixit for this.
     case MemberZero().zero: // desired-error {{did you mean to call it with no arguments?}} expected-error {{not an integer constant expression}}
-    // FIXME: This should be accepted once we do constexpr function invocation.
+    // FIXME: This should be accepted once we implement the new ICE rules.
     case id(1): // expected-error {{not an integer constant expression}}
       return;
     }
@@ -432,6 +432,17 @@
 static_assert_fold(SumNonzero(arr) == 6, "");
 static_assert_fold(CountZero(arr, arr + 40) == 36, "");
 
+struct ArrayElem {
+  constexpr ArrayElem() : n(0) {}
+  int n;
+  constexpr int f() { return n; }
+};
+struct ArrayRVal {
+  constexpr ArrayRVal() {}
+  ArrayElem elems[10];
+};
+static_assert_fold(ArrayRVal().elems[3].f() == 0, "");
+
 }
 
 namespace DependentValues {
@@ -572,12 +583,13 @@
 
 }
 
-struct Base {
+struct Bottom { constexpr Bottom() {} };
+struct Base : Bottom {
   constexpr Base(int a = 42, const char *b = "test") : a(a), b(b) {}
   int a;
   const char *b;
 };
-struct Base2 {
+struct Base2 : Bottom {
   constexpr Base2(const int &r) : r(r) {}
   int q = 123;
   // FIXME: When we track the global for which we are computing the initializer,
@@ -607,6 +619,63 @@
 static_assert_fold(!(derived == base), "");
 static_assert_fold(derived == base2, "");
 
+constexpr Bottom &bot1 = (Base&)derived;
+constexpr Bottom &bot2 = (Base2&)derived;
+static_assert_fold(&bot1 != &bot2, "");
+
+constexpr Bottom *pb1 = (Base*)&derived;
+constexpr Bottom *pb2 = (Base2*)&derived;
+static_assert_fold(pb1 != pb2, "");
+static_assert_fold(pb1 == &bot1, "");
+static_assert_fold(pb2 == &bot2, "");
+
+constexpr Base2 &fail = (Base2&)bot1; // expected-error {{constant expression}}
+constexpr Base &fail2 = (Base&)*pb2; // expected-error {{constant expression}}
+constexpr Base2 &ok2 = (Base2&)bot2;
+static_assert_fold(&ok2 == &derived, "");
+
+constexpr Base2 *pfail = (Base2*)pb1; // expected-error {{constant expression}}
+constexpr Base *pfail2 = (Base*)&bot2; // expected-error {{constant expression}}
+constexpr Base2 *pok2 = (Base2*)pb2;
+static_assert_fold(pok2 == &derived, "");
+static_assert_fold(&ok2 == pok2, "");
+static_assert_fold((Base2*)(Derived*)(Base*)pb1 == pok2, "");
+static_assert_fold((Derived*)(Base*)pb1 == (Derived*)pok2, "");
+
+constexpr Base *nullB = 42 - 6 * 7;
+static_assert_fold((Bottom*)nullB == 0, "");
+static_assert_fold((Derived*)nullB == 0, "");
+static_assert_fold((void*)(Bottom*)nullB == (void*)(Derived*)nullB, "");
+
+}
+
+namespace Temporaries {
+
+struct S {
+  constexpr S() {}
+  constexpr int f();
+};
+struct T : S {
+  constexpr T(int n) : S(), n(n) {}
+  int n;
+};
+constexpr int S::f() {
+  // 'this' must be the postfix-expression in a class member access expression,
+  // so we can't just use
+  //   return static_cast<T*>(this)->n;
+  return this->*(int(S::*))&T::n;
+}
+// The T temporary is implicitly cast to an S subobject, but we can recover the
+// T full-object via a base-to-derived cast, or a derived-to-base-casted member
+// pointer.
+static_assert_fold(T(3).f() == 3, "");
+
+constexpr int f(const S &s) {
+  return static_cast<const T&>(s).n;
+}
+constexpr int n = f(T(5));
+static_assert_fold(f(T(5)) == 5, "");
+
 }
 
 namespace Union {
@@ -626,6 +695,138 @@
 
 }
 
+namespace MemberPointer {
+  struct A {
+    constexpr A(int n) : n(n) {}
+    int n;
+    constexpr int f() { return n + 3; }
+  };
+  constexpr A a(7);
+  static_assert_fold(A(5).*&A::n == 5, "");
+  static_assert_fold((&a)->*&A::n == 7, "");
+  static_assert_fold((A(8).*&A::f)() == 11, "");
+  static_assert_fold(((&a)->*&A::f)() == 10, "");
+
+  struct B : A {
+    constexpr B(int n, int m) : A(n), m(m) {}
+    int m;
+    constexpr int g() { return n + m + 1; }
+  };
+  constexpr B b(9, 13);
+  static_assert_fold(B(4, 11).*&A::n == 4, "");
+  static_assert_fold(B(4, 11).*&B::m == 11, "");
+  static_assert_fold(B(4, 11).*(int(A::*))&B::m == 11, "");
+  static_assert_fold((&b)->*&A::n == 9, "");
+  static_assert_fold((&b)->*&B::m == 13, "");
+  static_assert_fold((&b)->*(int(A::*))&B::m == 13, "");
+  static_assert_fold((B(4, 11).*&A::f)() == 7, "");
+  static_assert_fold((B(4, 11).*&B::g)() == 16, "");
+  static_assert_fold((B(4, 11).*(int(A::*)()const)&B::g)() == 16, "");
+  static_assert_fold(((&b)->*&A::f)() == 12, "");
+  static_assert_fold(((&b)->*&B::g)() == 23, "");
+  static_assert_fold(((&b)->*(int(A::*)()const)&B::g)() == 23, "");
+
+  struct S {
+    constexpr S(int m, int n, int (S::*pf)() const, int S::*pn) :
+      m(m), n(n), pf(pf), pn(pn) {}
+    constexpr S() : m(), n(), pf(&S::f), pn(&S::n) {}
+
+    constexpr int f() { return this->*pn; }
+    virtual int g() const;
+
+    int m, n;
+    int (S::*pf)() const;
+    int S::*pn;
+  };
+
+  constexpr int S::*pm = &S::m;
+  constexpr int S::*pn = &S::n;
+  constexpr int (S::*pf)() const = &S::f;
+  constexpr int (S::*pg)() const = &S::g;
+
+  constexpr S s(2, 5, &S::f, &S::m);
+
+  static_assert_fold((s.*&S::f)() == 2, "");
+  static_assert_fold((s.*s.pf)() == 2, "");
+
+  template<int n> struct T : T<n-1> {};
+  template<> struct T<0> { int n; };
+  template<> struct T<30> : T<29> { int m; };
+
+  T<17> t17;
+  T<30> t30;
+
+  constexpr int (T<10>::*deepn) = &T<0>::n;
+  static_assert_fold(&(t17.*deepn) == &t17.n, "");
+
+  constexpr int (T<15>::*deepm) = (int(T<10>::*))&T<30>::m;
+  constexpr int *pbad = &(t17.*deepm); // expected-error {{constant expression}}
+  static_assert_fold(&(t30.*deepm) == &t30.m, "");
+
+  constexpr T<5> *p17_5 = &t17;
+  constexpr T<13> *p17_13 = (T<13>*)p17_5;
+  constexpr T<23> *p17_23 = (T<23>*)p17_13; // expected-error {{constant expression}}
+  static_assert_fold(&(p17_5->*(int(T<3>::*))deepn) == &t17.n, "");
+  static_assert_fold(&(p17_13->*deepn) == &t17.n, "");
+  constexpr int *pbad2 = &(p17_13->*(int(T<9>::*))deepm); // expected-error {{constant expression}}
+
+  constexpr T<5> *p30_5 = &t30;
+  constexpr T<23> *p30_23 = (T<23>*)p30_5;
+  constexpr T<13> *p30_13 = p30_23;
+  static_assert_fold(&(p30_5->*(int(T<3>::*))deepn) == &t30.n, "");
+  static_assert_fold(&(p30_13->*deepn) == &t30.n, "");
+  static_assert_fold(&(p30_23->*deepn) == &t30.n, "");
+  static_assert_fold(&(p30_5->*(int(T<2>::*))deepm) == &t30.m, "");
+  static_assert_fold(&(((T<17>*)p30_13)->*deepm) == &t30.m, "");
+  static_assert_fold(&(p30_23->*deepm) == &t30.m, "");
+}
+
+namespace ArrayBaseDerived {
+
+  struct Base {
+    constexpr Base() {}
+    int n = 0;
+  };
+  struct Derived : Base {
+    constexpr Derived() {}
+    constexpr const int *f() { return &n; }
+  };
+
+  constexpr Derived a[10];
+  constexpr Derived *pd3 = const_cast<Derived*>(&a[3]);
+  constexpr Base *pb3 = const_cast<Derived*>(&a[3]);
+  static_assert_fold(pb3 == pd3, "");
+
+  // pb3 does not point to an array element.
+  constexpr Base *pb4 = pb3 + 1; // ok, one-past-the-end pointer.
+  constexpr int pb4n = pb4->n; // expected-error {{constant expression}}
+  constexpr Base *err_pb5 = pb3 + 2; // FIXME: reject this.
+  constexpr int err_pb5n = err_pb5->n; // expected-error {{constant expression}}
+  constexpr Base *err_pb2 = pb3 - 1; // FIXME: reject this.
+  constexpr int err_pb2n = err_pb2->n; // expected-error {{constant expression}}
+  constexpr Base *pb3a = pb4 - 1;
+
+  // pb4 does not point to a Derived.
+  constexpr Derived *err_pd4 = (Derived*)pb4; // expected-error {{constant expression}}
+  constexpr Derived *pd3a = (Derived*)pb3a;
+  constexpr int pd3n = pd3a->n;
+
+  // pd3a still points to the Derived array.
+  constexpr Derived *pd6 = pd3a + 3;
+  static_assert_fold(pd6 == &a[6], "");
+  constexpr Derived *pd9 = pd6 + 3;
+  constexpr Derived *pd10 = pd6 + 4;
+  constexpr int pd9n = pd9->n; // ok
+  constexpr int err_pd10n = pd10->n; // expected-error {{constant expression}}
+  constexpr int pd0n = pd10[-10].n;
+  constexpr int err_pdminus1n = pd10[-11].n; // expected-error {{constant expression}}
+
+  constexpr Base *pb9 = pd9;
+  constexpr const int *(Base::*pfb)() const =
+      static_cast<const int *(Base::*)() const>(&Derived::f);
+  static_assert_fold((pb9->*pfb)() == &a[9].n, "");
+}
+
 namespace Complex {
 
 class complex {





More information about the cfe-commits mailing list