r183093 - Refactor constant expression evaluation to associate the complete object of a

Richard Smith richard-llvm at metafoo.co.uk
Sun Jun 2 22:03:03 PDT 2013


Author: rsmith
Date: Mon Jun  3 00:03:02 2013
New Revision: 183093

URL: http://llvm.org/viewvc/llvm-project?rev=183093&view=rev
Log:
Refactor constant expression evaluation to associate the complete object of a
materialized temporary with the corresponding MaterializeTemporaryExpr. This is
groundwork for providing C++11's guaranteed static initialization for global
references bound to lifetime-extended temporaries (if the initialization is a
constant expression).

In passing, fix a couple of bugs where some evaluation failures didn't trigger
diagnostics, and a rejects-valid where potential constant expression testing
would assume that it knew the dynamic type of *this and would reject programs
which relied on it being some derived type.

Modified:
    cfe/trunk/lib/AST/ExprConstant.cpp
    cfe/trunk/test/CXX/expr/expr.const/p2-0x.cpp
    cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp

Modified: cfe/trunk/lib/AST/ExprConstant.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprConstant.cpp?rev=183093&r1=183092&r2=183093&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ExprConstant.cpp (original)
+++ cfe/trunk/lib/AST/ExprConstant.cpp Mon Jun  3 00:03:02 2013
@@ -63,7 +63,25 @@ namespace {
     if (!B) return QualType();
     if (const ValueDecl *D = B.dyn_cast<const ValueDecl*>())
       return D->getType();
-    return B.get<const Expr*>()->getType();
+
+    const Expr *Base = B.get<const Expr*>();
+
+    // For a materialized temporary, the type of the temporary we materialized
+    // may not be the type of the expression.
+    if (const MaterializeTemporaryExpr *MTE =
+            dyn_cast<MaterializeTemporaryExpr>(Base)) {
+      SmallVector<const Expr *, 2> CommaLHSs;
+      SmallVector<SubobjectAdjustment, 2> Adjustments;
+      const Expr *Temp = MTE->GetTemporaryExpr();
+      const Expr *Inner = Temp->skipRValueSubobjectAdjustments(CommaLHSs,
+                                                               Adjustments);
+      // Keep any cv-qualifiers from the reference if we generated a temporary
+      // for it.
+      if (Inner != Temp)
+        return Inner->getType();
+    }
+
+    return Base->getType();
   }
 
   /// Get an LValue path entry, which is known to not be an array index, as a
@@ -625,31 +643,7 @@ CallStackFrame::~CallStackFrame() {
   Info.CurrentCall = Caller;
 }
 
-/// Produce a string describing the given constexpr call.
-static void describeCall(CallStackFrame *Frame, raw_ostream &Out) {
-  unsigned ArgIndex = 0;
-  bool IsMemberCall = isa<CXXMethodDecl>(Frame->Callee) &&
-                      !isa<CXXConstructorDecl>(Frame->Callee) &&
-                      cast<CXXMethodDecl>(Frame->Callee)->isInstance();
-
-  if (!IsMemberCall)
-    Out << *Frame->Callee << '(';
-
-  for (FunctionDecl::param_const_iterator I = Frame->Callee->param_begin(),
-       E = Frame->Callee->param_end(); I != E; ++I, ++ArgIndex) {
-    if (ArgIndex > (unsigned)IsMemberCall)
-      Out << ", ";
-
-    const ParmVarDecl *Param = *I;
-    const APValue &Arg = Frame->Arguments[ArgIndex];
-    Arg.printPretty(Out, Frame->Info.Ctx, Param->getType());
-
-    if (ArgIndex == 0 && IsMemberCall)
-      Out << "->" << *Frame->Callee << '(';
-  }
-
-  Out << ')';
-}
+static void describeCall(CallStackFrame *Frame, raw_ostream &Out);
 
 void EvalInfo::addCallStack(unsigned Limit) {
   // Determine which calls to skip, if any.
@@ -921,6 +915,42 @@ static bool EvaluateAtomic(const Expr *E
 // Misc utilities
 //===----------------------------------------------------------------------===//
 
+/// Produce a string describing the given constexpr call.
+static void describeCall(CallStackFrame *Frame, raw_ostream &Out) {
+  unsigned ArgIndex = 0;
+  bool IsMemberCall = isa<CXXMethodDecl>(Frame->Callee) &&
+                      !isa<CXXConstructorDecl>(Frame->Callee) &&
+                      cast<CXXMethodDecl>(Frame->Callee)->isInstance();
+
+  if (!IsMemberCall)
+    Out << *Frame->Callee << '(';
+
+  if (Frame->This && IsMemberCall) {
+    APValue Val;
+    Frame->This->moveInto(Val);
+    Val.printPretty(Out, Frame->Info.Ctx,
+                    Frame->This->Designator.MostDerivedType);
+    // FIXME: Add parens around Val if needed.
+    Out << "->" << *Frame->Callee << '(';
+    IsMemberCall = false;
+  }
+
+  for (FunctionDecl::param_const_iterator I = Frame->Callee->param_begin(),
+       E = Frame->Callee->param_end(); I != E; ++I, ++ArgIndex) {
+    if (ArgIndex > (unsigned)IsMemberCall)
+      Out << ", ";
+
+    const ParmVarDecl *Param = *I;
+    const APValue &Arg = Frame->Arguments[ArgIndex];
+    Arg.printPretty(Out, Frame->Info.Ctx, Param->getType());
+
+    if (ArgIndex == 0 && IsMemberCall)
+      Out << "->" << *Frame->Callee << '(';
+  }
+
+  Out << ')';
+}
+
 /// Evaluate an expression to see if it had side-effects, and discard its
 /// result.
 /// \return \c true if the caller should keep evaluating.
@@ -1541,6 +1571,19 @@ static bool HandleLValueBase(EvalInfo &I
   return true;
 }
 
+static bool HandleLValueBasePath(EvalInfo &Info, const CastExpr *E,
+                                 QualType Type, LValue &Result) {
+  for (CastExpr::path_const_iterator PathI = E->path_begin(),
+                                     PathE = E->path_end();
+       PathI != PathE; ++PathI) {
+    if (!HandleLValueBase(Info, E, Result, Type->getAsCXXRecordDecl(),
+                          *PathI))
+      return false;
+    Type = (*PathI)->getType();
+  }
+  return true;
+}
+
 /// Update LVal to refer to the given field, which must be a member of the type
 /// currently described by LVal.
 static bool HandleLValueMember(EvalInfo &Info, const Expr *E, LValue &LVal,
@@ -2152,7 +2195,7 @@ CompleteObject findCompleteObject(EvalIn
 
   // Compute value storage location and type of base object.
   APValue *BaseVal = 0;
-  QualType BaseType;
+  QualType BaseType = getType(LVal.Base);
 
   if (const ValueDecl *D = LVal.Base.dyn_cast<const ValueDecl*>()) {
     // In C++98, const, non-volatile integers initialized with ICEs are ICEs.
@@ -2173,7 +2216,6 @@ CompleteObject findCompleteObject(EvalIn
     }
 
     // Accesses of volatile-qualified objects are not allowed.
-    BaseType = VD->getType();
     if (BaseType.isVolatileQualified()) {
       if (Info.getLangOpts().CPlusPlus) {
         Info.Diag(E, diag::note_constexpr_access_volatile_obj, 1)
@@ -2241,7 +2283,6 @@ CompleteObject findCompleteObject(EvalIn
       return CompleteObject();
     }
 
-    BaseType = Base->getType();
     BaseVal = &Frame->Temporaries[Base];
 
     // Volatile temporary objects cannot be accessed in constant expressions.
@@ -2630,54 +2671,53 @@ static bool EvaluateObjectArgument(EvalI
 /// 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 LV - An lvalue referring to the base of the member pointer.
+/// \param RHS - The member pointer expression.
 /// \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,
+                                                  QualType LVType,
                                                   LValue &LV,
+                                                  const Expr *RHS,
                                                   bool IncludeMember = true) {
-  assert(BO->getOpcode() == BO_PtrMemD || BO->getOpcode() == BO_PtrMemI);
-
-  bool EvalObjOK = EvaluateObjectArgument(Info, BO->getLHS(), LV);
-  if (!EvalObjOK && !Info.keepEvaluatingAfterFailure())
-    return 0;
-
   MemberPtr MemPtr;
-  if (!EvaluateMemberPointer(BO->getRHS(), MemPtr, Info))
+  if (!EvaluateMemberPointer(RHS, 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 (!EvalObjOK)
+  if (!MemPtr.getDecl()) {
+    // FIXME: Specific diagnostic.
+    Info.Diag(RHS);
     return 0;
+  }
 
   if (MemPtr.isDerivedMember()) {
     // This is a member of some derived class. Truncate LV appropriately.
     // The end of the derived-to-base path for the base object must match the
     // derived-to-base path for the member pointer.
     if (LV.Designator.MostDerivedPathLength + MemPtr.Path.size() >
-        LV.Designator.Entries.size())
+        LV.Designator.Entries.size()) {
+      Info.Diag(RHS);
       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())
+      if (LVDecl->getCanonicalDecl() != MPDecl->getCanonicalDecl()) {
+        Info.Diag(RHS);
         return 0;
+      }
     }
 
     // Truncate the lvalue to the appropriate derived class.
-    if (!CastToDerivedClass(Info, BO, LV, MemPtr.getContainingRecord(),
+    if (!CastToDerivedClass(Info, RHS, LV, MemPtr.getContainingRecord(),
                             PathLengthToMember))
       return 0;
   } else if (!MemPtr.Path.empty()) {
@@ -2686,7 +2726,6 @@ static const ValueDecl *HandleMemberPoin
                                   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();
@@ -2694,23 +2733,24 @@ static const ValueDecl *HandleMemberPoin
     // 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];
-      if (!HandleLValueDirectBase(Info, BO, LV, RD, Base))
+      if (!HandleLValueDirectBase(Info, RHS, LV, RD, Base))
         return 0;
       RD = Base;
     }
     // Finally cast to the class containing the member.
-    if (!HandleLValueDirectBase(Info, BO, LV, RD, MemPtr.getContainingRecord()))
+    if (!HandleLValueDirectBase(Info, RHS, LV, RD,
+                                MemPtr.getContainingRecord()))
       return 0;
   }
 
   // Add the member. Note that we cannot build bound member functions here.
   if (IncludeMember) {
     if (const FieldDecl *FD = dyn_cast<FieldDecl>(MemPtr.getDecl())) {
-      if (!HandleLValueMember(Info, BO, LV, FD))
+      if (!HandleLValueMember(Info, RHS, LV, FD))
         return 0;
     } else if (const IndirectFieldDecl *IFD =
                  dyn_cast<IndirectFieldDecl>(MemPtr.getDecl())) {
-      if (!HandleLValueIndirectMember(Info, BO, LV, IFD))
+      if (!HandleLValueIndirectMember(Info, RHS, LV, IFD))
         return 0;
     } else {
       llvm_unreachable("can't construct reference to bound member function");
@@ -2720,6 +2760,24 @@ static const ValueDecl *HandleMemberPoin
   return MemPtr.getDecl();
 }
 
+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)) {
+    if (Info.keepEvaluatingAfterFailure()) {
+      MemberPtr MemPtr;
+      EvaluateMemberPointer(BO->getRHS(), MemPtr, Info);
+    }
+    return 0;
+  }
+
+  return HandleMemberPointerAccess(Info, BO->getLHS()->getType(), LV,
+                                   BO->getRHS(), IncludeMember);
+}
+
 /// 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,
@@ -3842,24 +3900,14 @@ public:
       return ExprEvaluatorBaseTy::VisitCastExpr(E);
 
     case CK_DerivedToBase:
-    case CK_UncheckedDerivedToBase: {
+    case CK_UncheckedDerivedToBase:
       if (!this->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(this->Info, E, Result, Type->getAsCXXRecordDecl(),
-                              *PathI))
-          return false;
-        Type = (*PathI)->getType();
-      }
-
-      return true;
-    }
+      return HandleLValueBasePath(this->Info, E, E->getSubExpr()->getType(),
+                                  Result);
     }
   }
 };
@@ -3888,8 +3936,10 @@ public:
 //  * BlockExpr
 //  * CallExpr for a MakeStringConstant builtin
 // - Locals and temporaries
+//  * MaterializeTemporaryExpr
 //  * Any Expr, with a CallIndex indicating the function in which the temporary
-//    was evaluated.
+//    was evaluated, for cases where the MaterializeTemporaryExpr is missing
+//    from the AST (FIXME).
 // plus an offset in bytes.
 //===----------------------------------------------------------------------===//
 namespace {
@@ -3984,12 +4034,51 @@ bool LValueExprEvaluator::VisitVarDecl(c
 
 bool LValueExprEvaluator::VisitMaterializeTemporaryExpr(
     const MaterializeTemporaryExpr *E) {
-  if (E->getType()->isRecordType())
-    return EvaluateTemporary(E->GetTemporaryExpr(), Result, Info);
+  // Walk through the expression to find the materialized temporary itself.
+  SmallVector<const Expr *, 2> CommaLHSs;
+  SmallVector<SubobjectAdjustment, 2> Adjustments;
+  const Expr *Inner = E->GetTemporaryExpr()->
+      skipRValueSubobjectAdjustments(CommaLHSs, Adjustments);
+
+  // If we passed any comma operators, evaluate their LHSs.
+  for (unsigned I = 0, N = CommaLHSs.size(); I != N; ++I)
+    if (!EvaluateIgnoredValue(Info, CommaLHSs[I]))
+      return false;
 
+  // Materialize the temporary itself.
+  APValue *Value = &Info.CurrentCall->Temporaries[E];
   Result.set(E, Info.CurrentCall->Index);
-  return EvaluateInPlace(Info.CurrentCall->Temporaries[E], Info,
-                         Result, E->GetTemporaryExpr());
+  if (!EvaluateInPlace(*Value, Info, Result, Inner))
+    return false;
+
+  // Adjust our lvalue to refer to the desired subobject.
+  QualType Type = Inner->getType();
+  for (unsigned I = Adjustments.size(); I != 0; /**/) {
+    --I;
+    switch (Adjustments[I].Kind) {
+    case SubobjectAdjustment::DerivedToBaseAdjustment:
+      if (!HandleLValueBasePath(Info, Adjustments[I].DerivedToBase.BasePath,
+                                Type, Result))
+        return false;
+      Type = Adjustments[I].DerivedToBase.BasePath->getType();
+      break;
+
+    case SubobjectAdjustment::FieldAdjustment:
+      if (!HandleLValueMember(Info, E, Result, Adjustments[I].Field))
+        return false;
+      Type = Adjustments[I].Field->getType();
+      break;
+
+    case SubobjectAdjustment::MemberPointerAdjustment:
+      if (!HandleMemberPointerAccess(this->Info, Type, Result,
+                                     Adjustments[I].Ptr.RHS))
+        return false;
+      Type = Adjustments[I].Ptr.MPT->getPointeeType();
+      break;
+    }
+  }
+
+  return true;
 }
 
 bool
@@ -4167,6 +4256,9 @@ public:
     return Error(E);
   }
   bool VisitCXXThisExpr(const CXXThisExpr *E) {
+    // Can't look at 'this' when checking a potential constant expression.
+    if (Info.CheckingPotentialConstantExpression)
+      return false;
     if (!Info.CurrentCall->This)
       return Error(E);
     Result = *Info.CurrentCall->This;
@@ -4240,7 +4332,7 @@ bool PointerExprEvaluator::VisitCastExpr
     return true;
 
   case CK_DerivedToBase:
-  case CK_UncheckedDerivedToBase: {
+  case CK_UncheckedDerivedToBase:
     if (!EvaluatePointer(E->getSubExpr(), Result, Info))
       return false;
     if (!Result.Base && Result.Offset.isZero())
@@ -4248,19 +4340,9 @@ bool PointerExprEvaluator::VisitCastExpr
 
     // 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()->castAs<PointerType>()->getPointeeType();
-
-    for (CastExpr::path_const_iterator PathI = E->path_begin(),
-         PathE = E->path_end(); PathI != PathE; ++PathI) {
-      if (!HandleLValueBase(Info, E, Result, Type->getAsCXXRecordDecl(),
-                            *PathI))
-        return false;
-      Type = (*PathI)->getType();
-    }
-
-    return true;
-  }
+    return HandleLValueBasePath(Info, E, E->getSubExpr()->getType()->
+                                  castAs<PointerType>()->getPointeeType(),
+                                Result);
 
   case CK_BaseToDerived:
     if (!Visit(E->getSubExpr()))

Modified: cfe/trunk/test/CXX/expr/expr.const/p2-0x.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/expr/expr.const/p2-0x.cpp?rev=183093&r1=183092&r2=183093&view=diff
==============================================================================
--- cfe/trunk/test/CXX/expr/expr.const/p2-0x.cpp (original)
+++ cfe/trunk/test/CXX/expr/expr.const/p2-0x.cpp Mon Jun  3 00:03:02 2013
@@ -481,17 +481,19 @@ namespace UnspecifiedRelations {
   public:
     constexpr A() : a(0), b(0) {}
     int a;
-    constexpr bool cmp() const { return &a < &b; } // expected-error {{constexpr function never produces a constant expression}} expected-note {{comparison of address of fields 'a' and 'b' of 'A' with differing access specifiers (public vs private) has unspecified value}}
+    constexpr bool cmp() const { return &a < &b; } // expected-note {{comparison of address of fields 'a' and 'b' of 'A' with differing access specifiers (public vs private) has unspecified value}}
   private:
     int b;
   };
+  static_assert(A().cmp(), ""); // expected-error {{constant expression}} expected-note {{in call}}
   class B {
   public:
     A a;
-    constexpr bool cmp() const { return &a.a < &b.a; } // expected-error {{constexpr function never produces a constant expression}} expected-note {{comparison of address of fields 'a' and 'b' of 'B' with differing access specifiers (public vs protected) has unspecified value}}
+    constexpr bool cmp() const { return &a.a < &b.a; } // expected-note {{comparison of address of fields 'a' and 'b' of 'B' with differing access specifiers (public vs protected) has unspecified value}}
   protected:
     A b;
   };
+  static_assert(B().cmp(), ""); // expected-error {{constant expression}} expected-note {{in call}}
 
   // If two pointers point to different base sub-objects of the same object, or
   // one points to a base subobject and the other points to a member, the result

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=183093&r1=183092&r2=183093&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp (original)
+++ cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp Mon Jun  3 00:03:02 2013
@@ -778,21 +778,26 @@ namespace Temporaries {
 struct S {
   constexpr S() {}
   constexpr int f() const;
+  constexpr int g() const;
 };
 struct T : S {
   constexpr T(int n) : S(), n(n) {}
   int n;
 };
 constexpr int S::f() const {
-  // '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;
+  return static_cast<const T*>(this)->n; // expected-note {{cannot cast}}
+}
+constexpr int S::g() const {
+  // FIXME: Better diagnostic for this.
+  return this->*(int(S::*))&T::n; // expected-note {{subexpression}}
 }
 // 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(S().f(), ""); // expected-error {{constant expression}} expected-note {{in call to '&Temporaries::S()->f()'}}
+static_assert(S().g(), ""); // expected-error {{constant expression}} expected-note {{in call to '&Temporaries::S()->g()'}}
 static_assert(T(3).f() == 3, "");
+static_assert(T(4).g() == 4, "");
 
 constexpr int f(const S &s) {
   return static_cast<const T&>(s).n;





More information about the cfe-commits mailing list