r360635 - [c++20] P1064R0: Allow virtual function calls in constant expression

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Mon May 13 16:35:21 PDT 2019


Author: rsmith
Date: Mon May 13 16:35:21 2019
New Revision: 360635

URL: http://llvm.org/viewvc/llvm-project?rev=360635&view=rev
Log:
[c++20] P1064R0: Allow virtual function calls in constant expression
evaluation.

This reinstates r360559, reverted in r360580, with a fix to avoid
crashing if evaluation-for-overflow mode encounters a virtual call on an
object of a class with a virtual base class, and to generally not try to
resolve virtual function calls to objects whose (notional) vptrs are not
readable. (The standard rules are unclear here, but this seems like a
reasonable approach.)

Modified:
    cfe/trunk/include/clang/AST/DeclCXX.h
    cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/include/clang/Sema/Sema.h
    cfe/trunk/lib/AST/DeclCXX.cpp
    cfe/trunk/lib/AST/ExprConstant.cpp
    cfe/trunk/lib/Sema/SemaDeclCXX.cpp
    cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp
    cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp
    cfe/trunk/test/CXX/drs/dr18xx.cpp
    cfe/trunk/test/CXX/drs/dr6xx.cpp
    cfe/trunk/test/SemaCXX/constant-expression-cxx2a.cpp
    cfe/trunk/test/SemaCXX/cxx17-compat.cpp
    cfe/trunk/test/SemaCXX/integer-overflow.cpp
    cfe/trunk/www/cxx_status.html

Modified: cfe/trunk/include/clang/AST/DeclCXX.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclCXX.h?rev=360635&r1=360634&r2=360635&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/DeclCXX.h (original)
+++ cfe/trunk/include/clang/AST/DeclCXX.h Mon May 13 16:35:21 2019
@@ -2298,6 +2298,17 @@ public:
               ->getCorrespondingMethodInClass(RD, MayBeBase);
   }
 
+  /// Find if \p RD declares a function that overrides this function, and if so,
+  /// return it. Does not search base classes.
+  CXXMethodDecl *getCorrespondingMethodDeclaredInClass(const CXXRecordDecl *RD,
+                                                       bool MayBeBase = false);
+  const CXXMethodDecl *
+  getCorrespondingMethodDeclaredInClass(const CXXRecordDecl *RD,
+                                        bool MayBeBase = false) const {
+    return const_cast<CXXMethodDecl *>(this)
+        ->getCorrespondingMethodDeclaredInClass(RD, MayBeBase);
+  }
+
   // Implement isa/cast/dyncast/etc.
   static bool classof(const Decl *D) { return classofKind(D->getKind()); }
   static bool classofKind(Kind K) {

Modified: cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td?rev=360635&r1=360634&r2=360635&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td Mon May 13 16:35:21 2019
@@ -32,6 +32,10 @@ def note_constexpr_no_return : Note<
   "control reached end of constexpr function">;
 def note_constexpr_virtual_call : Note<
   "cannot evaluate call to virtual function in a constant expression">;
+def note_constexpr_pure_virtual_call : Note<
+  "pure virtual function %q0 called">;
+def note_constexpr_virtual_out_of_lifetime : Note<
+  "virtual function called on object '%0' whose dynamic type is not constant">;
 def note_constexpr_virtual_base : Note<
   "cannot construct object of type %0 with virtual base class "
   "in a constant expression">;

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=360635&r1=360634&r2=360635&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Mon May 13 16:35:21 2019
@@ -2314,6 +2314,9 @@ def err_constexpr_redecl_mismatch : Erro
   "%select{non-constexpr declaration of %0 follows constexpr declaration"
   "|constexpr declaration of %0 follows non-constexpr declaration}1">;
 def err_constexpr_virtual : Error<"virtual function cannot be constexpr">;
+def warn_cxx17_compat_constexpr_virtual : Warning<
+  "virtual constexpr functions are incompatible with "
+  "C++ standards before C++2a">, InGroup<CXXPre2aCompat>, DefaultIgnore;
 def err_constexpr_virtual_base : Error<
   "constexpr %select{member function|constructor}0 not allowed in "
   "%select{struct|interface|class}1 with virtual base "

Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=360635&r1=360634&r2=360635&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Mon May 13 16:35:21 2019
@@ -5985,8 +5985,8 @@ public:
 
   /// MarkVirtualMembersReferenced - Will mark all members of the given
   /// CXXRecordDecl referenced.
-  void MarkVirtualMembersReferenced(SourceLocation Loc,
-                                    const CXXRecordDecl *RD);
+  void MarkVirtualMembersReferenced(SourceLocation Loc, const CXXRecordDecl *RD,
+                                    bool ConstexprOnly = false);
 
   /// Define all of the vtables that have been used in this
   /// translation unit and reference any virtual members used by those

Modified: cfe/trunk/lib/AST/DeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclCXX.cpp?rev=360635&r1=360634&r2=360635&view=diff
==============================================================================
--- cfe/trunk/lib/AST/DeclCXX.cpp (original)
+++ cfe/trunk/lib/AST/DeclCXX.cpp Mon May 13 16:35:21 2019
@@ -1946,8 +1946,8 @@ static bool recursivelyOverrides(const C
 }
 
 CXXMethodDecl *
-CXXMethodDecl::getCorrespondingMethodInClass(const CXXRecordDecl *RD,
-                                             bool MayBeBase) {
+CXXMethodDecl::getCorrespondingMethodDeclaredInClass(const CXXRecordDecl *RD,
+                                                     bool MayBeBase) {
   if (this->getParent()->getCanonicalDecl() == RD->getCanonicalDecl())
     return this;
 
@@ -1973,6 +1973,15 @@ CXXMethodDecl::getCorrespondingMethodInC
       return MD;
   }
 
+  return nullptr;
+}
+
+CXXMethodDecl *
+CXXMethodDecl::getCorrespondingMethodInClass(const CXXRecordDecl *RD,
+                                             bool MayBeBase) {
+  if (auto *MD = getCorrespondingMethodDeclaredInClass(RD, MayBeBase))
+    return MD;
+
   for (const auto &I : RD->bases()) {
     const RecordType *RT = I.getType()->getAs<RecordType>();
     if (!RT)

Modified: cfe/trunk/lib/AST/ExprConstant.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprConstant.cpp?rev=360635&r1=360634&r2=360635&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ExprConstant.cpp (original)
+++ cfe/trunk/lib/AST/ExprConstant.cpp Mon May 13 16:35:21 2019
@@ -37,6 +37,7 @@
 #include "clang/AST/ASTDiagnostic.h"
 #include "clang/AST/ASTLambda.h"
 #include "clang/AST/CharUnits.h"
+#include "clang/AST/CXXInheritance.h"
 #include "clang/AST/Expr.h"
 #include "clang/AST/OSLog.h"
 #include "clang/AST/RecordLayout.h"
@@ -2485,6 +2486,21 @@ static bool HandleLValueBasePath(EvalInf
   return true;
 }
 
+/// Cast an lvalue referring to a derived class to a known base subobject.
+static bool CastToBaseClass(EvalInfo &Info, const Expr *E, LValue &Result,
+                            const CXXRecordDecl *DerivedRD,
+                            const CXXRecordDecl *BaseRD) {
+  CXXBasePaths Paths(/*FindAmbiguities=*/false,
+                     /*RecordPaths=*/true, /*DetectVirtual=*/false);
+  if (!DerivedRD->isDerivedFrom(BaseRD, Paths))
+    llvm_unreachable("Class must be derived from the passed in base class!");
+
+  for (CXXBasePathElement &Elem : Paths.front())
+    if (!HandleLValueBase(Info, E, Result, Elem.Class, Elem.Base))
+      return false;
+  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,
@@ -4461,16 +4477,19 @@ static bool CheckConstexprFunction(EvalI
   }
 
   // DR1872: An instantiated virtual constexpr function can't be called in a
-  // constant expression.
-  if (isa<CXXMethodDecl>(Declaration) &&
-      cast<CXXMethodDecl>(Declaration)->isVirtual()) {
-    Info.FFDiag(CallLoc, diag::note_constexpr_virtual_call);
+  // constant expression (prior to C++20). We can still constant-fold such a
+  // call.
+  if (!Info.Ctx.getLangOpts().CPlusPlus2a && isa<CXXMethodDecl>(Declaration) &&
+      cast<CXXMethodDecl>(Declaration)->isVirtual())
+    Info.CCEDiag(CallLoc, diag::note_constexpr_virtual_call);
+
+  if (Definition && Definition->isInvalidDecl()) {
+    Info.FFDiag(CallLoc, diag::note_invalid_subexpr_in_const_expr);
     return false;
   }
 
   // Can we evaluate this function call?
-  if (Definition && Definition->isConstexpr() &&
-      !Definition->isInvalidDecl() && Body)
+  if (Definition && Definition->isConstexpr() && Body)
     return true;
 
   if (Info.getLangOpts().CPlusPlus11) {
@@ -4517,7 +4536,7 @@ const AccessKinds CheckMemberCallThisPoi
 /// Check that the pointee of the 'this' pointer in a member function call is
 /// either within its lifetime or in its period of construction or destruction.
 static bool checkMemberCallThisPointer(EvalInfo &Info, const Expr *E,
-                                       const LValue &This) {
+                                       const LValue &This, bool IsVirtual) {
   if (This.Designator.Invalid)
     return false;
 
@@ -4538,6 +4557,16 @@ static bool checkMemberCallThisPointer(E
                          : diag::note_constexpr_access_unsized_array)
           << AK_MemberCall;
       return false;
+    } else if (IsVirtual) {
+      // Conservatively refuse to perform a virtual function call if we would
+      // not be able to read a notional 'vptr' value.
+      APValue Val;
+      This.moveInto(Val);
+      QualType StarThisType =
+          Info.Ctx.getLValueReferenceType(This.Designator.getType(Info.Ctx));
+      Info.FFDiag(E, diag::note_constexpr_virtual_out_of_lifetime)
+          << Val.getAsString(Info.Ctx, StarThisType);
+      return false;
     }
     return true;
   }
@@ -4546,6 +4575,155 @@ static bool checkMemberCallThisPointer(E
   return Obj && findSubobject(Info, E, Obj, This.Designator, Handler);
 }
 
+struct DynamicType {
+  /// The dynamic class type of the object.
+  const CXXRecordDecl *Type;
+  /// The corresponding path length in the lvalue.
+  unsigned PathLength;
+};
+
+static const CXXRecordDecl *getBaseClassType(SubobjectDesignator &Designator,
+                                             unsigned PathLength) {
+  assert(PathLength >= Designator.MostDerivedPathLength && PathLength <=
+      Designator.Entries.size() && "invalid path length");
+  return (PathLength == Designator.MostDerivedPathLength)
+             ? Designator.MostDerivedType->getAsCXXRecordDecl()
+             : getAsBaseClass(Designator.Entries[PathLength - 1]);
+}
+
+/// Determine the dynamic type of an object.
+static Optional<DynamicType> ComputeDynamicType(EvalInfo &Info, LValue &This) {
+  // If we don't have an lvalue denoting an object of class type, there is no
+  // meaningful dynamic type. (We consider objects of non-class type to have no
+  // dynamic type.)
+  if (This.Designator.IsOnePastTheEnd || This.Designator.Invalid ||
+      !This.Designator.MostDerivedType->getAsCXXRecordDecl())
+    return None;
+
+  // FIXME: For very deep class hierarchies, it might be beneficial to use a
+  // binary search here instead. But the overwhelmingly common case is that
+  // we're not in the middle of a constructor, so it probably doesn't matter
+  // in practice.
+  ArrayRef<APValue::LValuePathEntry> Path = This.Designator.Entries;
+  for (unsigned PathLength = This.Designator.MostDerivedPathLength;
+       PathLength <= Path.size(); ++PathLength) {
+    switch (Info.isEvaluatingConstructor(This.getLValueBase(),
+                                         Path.slice(0, PathLength))) {
+    case ConstructionPhase::Bases:
+      // We're constructing a base class. This is not the dynamic type.
+      break;
+
+    case ConstructionPhase::None:
+    case ConstructionPhase::AfterBases:
+      // We've finished constructing the base classes, so this is the dynamic
+      // type.
+      return DynamicType{getBaseClassType(This.Designator, PathLength),
+                         PathLength};
+    }
+  }
+
+  // CWG issue 1517: we're constructing a base class of the object described by
+  // 'This', so that object has not yet begun its period of construction and
+  // any polymorphic operation on it results in undefined behavior.
+  return None;
+}
+
+/// Perform virtual dispatch.
+static const CXXMethodDecl *HandleVirtualDispatch(
+    EvalInfo &Info, const Expr *E, LValue &This, const CXXMethodDecl *Found,
+    llvm::SmallVectorImpl<QualType> &CovariantAdjustmentPath) {
+  Optional<DynamicType> DynType = ComputeDynamicType(Info, This);
+  if (!DynType) {
+    Info.FFDiag(E);
+    return nullptr;
+  }
+
+  // Find the final overrider. It must be declared in one of the classes on the
+  // path from the dynamic type to the static type.
+  // FIXME: If we ever allow literal types to have virtual base classes, that
+  // won't be true.
+  const CXXMethodDecl *Callee = Found;
+  unsigned PathLength = DynType->PathLength;
+  for (/**/; PathLength <= This.Designator.Entries.size(); ++PathLength) {
+    const CXXRecordDecl *Class = getBaseClassType(This.Designator, PathLength);
+    if (Class->getNumVBases()) {
+      Info.FFDiag(E);
+      return nullptr;
+    }
+
+    const CXXMethodDecl *Overrider =
+        Found->getCorrespondingMethodDeclaredInClass(Class, false);
+    if (Overrider) {
+      Callee = Overrider;
+      break;
+    }
+  }
+
+  // C++2a [class.abstract]p6:
+  //   the effect of making a virtual call to a pure virtual function [...] is
+  //   undefined
+  if (Callee->isPure()) {
+    Info.FFDiag(E, diag::note_constexpr_pure_virtual_call, 1) << Callee;
+    Info.Note(Callee->getLocation(), diag::note_declared_at);
+    return nullptr;
+  }
+
+  // If necessary, walk the rest of the path to determine the sequence of
+  // covariant adjustment steps to apply.
+  if (!Info.Ctx.hasSameUnqualifiedType(Callee->getReturnType(),
+                                       Found->getReturnType())) {
+    CovariantAdjustmentPath.push_back(Callee->getReturnType());
+    for (unsigned CovariantPathLength = PathLength + 1;
+         CovariantPathLength != This.Designator.Entries.size();
+         ++CovariantPathLength) {
+      const CXXRecordDecl *NextClass =
+          getBaseClassType(This.Designator, CovariantPathLength);
+      const CXXMethodDecl *Next =
+          Found->getCorrespondingMethodDeclaredInClass(NextClass, false);
+      if (Next && !Info.Ctx.hasSameUnqualifiedType(
+                      Next->getReturnType(), CovariantAdjustmentPath.back()))
+        CovariantAdjustmentPath.push_back(Next->getReturnType());
+    }
+    if (!Info.Ctx.hasSameUnqualifiedType(Found->getReturnType(),
+                                         CovariantAdjustmentPath.back()))
+      CovariantAdjustmentPath.push_back(Found->getReturnType());
+  }
+
+  // Perform 'this' adjustment.
+  if (!CastToDerivedClass(Info, E, This, Callee->getParent(), PathLength))
+    return nullptr;
+
+  return Callee;
+}
+
+/// Perform the adjustment from a value returned by a virtual function to
+/// a value of the statically expected type, which may be a pointer or
+/// reference to a base class of the returned type.
+static bool HandleCovariantReturnAdjustment(EvalInfo &Info, const Expr *E,
+                                            APValue &Result,
+                                            ArrayRef<QualType> Path) {
+  assert(Result.isLValue() &&
+         "unexpected kind of APValue for covariant return");
+  if (Result.isNullPointer())
+    return true;
+
+  LValue LVal;
+  LVal.setFrom(Info.Ctx, Result);
+
+  const CXXRecordDecl *OldClass = Path[0]->getPointeeCXXRecordDecl();
+  for (unsigned I = 1; I != Path.size(); ++I) {
+    const CXXRecordDecl *NewClass = Path[I]->getPointeeCXXRecordDecl();
+    assert(OldClass && NewClass && "unexpected kind of covariant return");
+    if (OldClass != NewClass &&
+        !CastToBaseClass(Info, E, LVal, OldClass, NewClass))
+      return false;
+    OldClass = NewClass;
+  }
+
+  LVal.moveInto(Result);
+  return true;
+}
+
 /// Determine if a class has any fields that might need to be copied by a
 /// trivial copy or move operation.
 static bool hasFields(const CXXRecordDecl *RD) {
@@ -4735,11 +4913,6 @@ static bool HandleConstructorCall(const
                                   BaseType->getAsCXXRecordDecl(), &Layout))
         return false;
       Value = &Result.getStructBase(BasesSeen++);
-
-      // This is the point at which the dynamic type of the object becomes this
-      // class type.
-      if (BasesSeen == RD->getNumBases())
-        EvalObj.finishedConstructingBases();
     } else if ((FD = I->getMember())) {
       if (!HandleLValueMember(Info, I->getInit(), Subobject, FD, &Layout))
         return false;
@@ -4800,6 +4973,11 @@ static bool HandleConstructorCall(const
         return false;
       Success = false;
     }
+
+    // This is the point at which the dynamic type of the object becomes this
+    // class type.
+    if (I->isBaseInitializer() && BasesSeen == RD->getNumBases())
+      EvalObj.finishedConstructingBases();
   }
 
   return Success &&
@@ -5040,27 +5218,30 @@ public:
     const FunctionDecl *FD = nullptr;
     LValue *This = nullptr, ThisVal;
     auto Args = llvm::makeArrayRef(E->getArgs(), E->getNumArgs());
+    bool HasQualifier = false;
 
     // Extract function decl and 'this' pointer from the callee.
     if (CalleeType->isSpecificBuiltinType(BuiltinType::BoundMember)) {
-      const ValueDecl *Member = nullptr;
+      const CXXMethodDecl *Member = nullptr;
       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 false;
-        Member = ME->getMemberDecl();
+        Member = dyn_cast<CXXMethodDecl>(ME->getMemberDecl());
+        if (!Member)
+          return Error(Callee);
         This = &ThisVal;
+        HasQualifier = ME->hasQualifier();
       } else if (const BinaryOperator *BE = dyn_cast<BinaryOperator>(Callee)) {
         // Indirect bound member calls ('.*' or '->*').
-        Member = HandleMemberPointerAccess(Info, BE, ThisVal, false);
-        if (!Member) return false;
+        Member = dyn_cast_or_null<CXXMethodDecl>(
+            HandleMemberPointerAccess(Info, BE, ThisVal, false));
+        if (!Member)
+          return Error(Callee);
         This = &ThisVal;
       } else
         return Error(Callee);
-
-      FD = dyn_cast<FunctionDecl>(Member);
-      if (!FD)
-        return Error(Callee);
+      FD = Member;
     } else if (CalleeType->isFunctionPointerType()) {
       LValue Call;
       if (!EvaluatePointer(Callee, Call, Info))
@@ -5130,8 +5311,20 @@ public:
     } else
       return Error(E);
 
-    if (This && !checkMemberCallThisPointer(Info, E, *This))
-      return false;
+    SmallVector<QualType, 4> CovariantAdjustmentPath;
+    if (This) {
+      auto *NamedMember = dyn_cast<CXXMethodDecl>(FD);
+      bool IsVirtual = NamedMember && NamedMember->isVirtual() && !HasQualifier;
+
+      // Check that the 'this' pointer points to an object of the right type.
+      if (!checkMemberCallThisPointer(Info, E, *This, IsVirtual))
+        return false;
+
+      // Perform virtual dispatch, if necessary.
+      if (IsVirtual && !(FD = HandleVirtualDispatch(Info, E, *This, NamedMember,
+                                                    CovariantAdjustmentPath)))
+        return true;
+    }
 
     const FunctionDecl *Definition = nullptr;
     Stmt *Body = FD->getBody(Definition);
@@ -5141,6 +5334,11 @@ public:
                             Result, ResultSlot))
       return false;
 
+    if (!CovariantAdjustmentPath.empty() &&
+        !HandleCovariantReturnAdjustment(Info, E, Result,
+                                         CovariantAdjustmentPath))
+      return false;
+
     return true;
   }
 

Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=360635&r1=360634&r2=360635&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Mon May 13 16:35:21 2019
@@ -1596,6 +1596,9 @@ bool Sema::CheckConstexprFunctionDecl(co
     //  The definition of a constexpr constructor shall satisfy the following
     //  constraints:
     //  - the class shall not have any virtual base classes;
+    //
+    // FIXME: This only applies to constructors, not arbitrary member
+    // functions.
     const CXXRecordDecl *RD = MD->getParent();
     if (RD->getNumVBases()) {
       Diag(NewFD->getLocation(), diag::err_constexpr_virtual_base)
@@ -1612,21 +1615,25 @@ bool Sema::CheckConstexprFunctionDecl(co
     // C++11 [dcl.constexpr]p3:
     //  The definition of a constexpr function shall satisfy the following
     //  constraints:
-    // - it shall not be virtual;
+    // - it shall not be virtual; (removed in C++20)
     const CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(NewFD);
     if (Method && Method->isVirtual()) {
-      Method = Method->getCanonicalDecl();
-      Diag(Method->getLocation(), diag::err_constexpr_virtual);
+      if (getLangOpts().CPlusPlus2a) {
+        Diag(Method->getLocation(), diag::warn_cxx17_compat_constexpr_virtual);
+      } else {
+        Method = Method->getCanonicalDecl();
+        Diag(Method->getLocation(), diag::err_constexpr_virtual);
 
-      // If it's not obvious why this function is virtual, find an overridden
-      // function which uses the 'virtual' keyword.
-      const CXXMethodDecl *WrittenVirtual = Method;
-      while (!WrittenVirtual->isVirtualAsWritten())
-        WrittenVirtual = *WrittenVirtual->begin_overridden_methods();
-      if (WrittenVirtual != Method)
-        Diag(WrittenVirtual->getLocation(),
-             diag::note_overridden_virtual_function);
-      return false;
+        // If it's not obvious why this function is virtual, find an overridden
+        // function which uses the 'virtual' keyword.
+        const CXXMethodDecl *WrittenVirtual = Method;
+        while (!WrittenVirtual->isVirtualAsWritten())
+          WrittenVirtual = *WrittenVirtual->begin_overridden_methods();
+        if (WrittenVirtual != Method)
+          Diag(WrittenVirtual->getLocation(),
+               diag::note_overridden_virtual_function);
+        return false;
+      }
     }
 
     // - its return type shall be a literal type;
@@ -15197,7 +15204,8 @@ void Sema::MarkVirtualMemberExceptionSpe
 }
 
 void Sema::MarkVirtualMembersReferenced(SourceLocation Loc,
-                                        const CXXRecordDecl *RD) {
+                                        const CXXRecordDecl *RD,
+                                        bool ConstexprOnly) {
   // Mark all functions which will appear in RD's vtable as used.
   CXXFinalOverriderMap FinalOverriders;
   RD->getFinalOverriders(FinalOverriders);
@@ -15212,7 +15220,7 @@ void Sema::MarkVirtualMembersReferenced(
 
       // C++ [basic.def.odr]p2:
       //   [...] A virtual member function is used if it is not pure. [...]
-      if (!Overrider->isPure())
+      if (!Overrider->isPure() && (!ConstexprOnly || Overrider->isConstexpr()))
         MarkFunctionReferenced(Loc, Overrider);
     }
   }

Modified: cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp?rev=360635&r1=360634&r2=360635&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp Mon May 13 16:35:21 2019
@@ -2082,6 +2082,7 @@ Sema::InstantiateClass(SourceLocation Po
   LateInstantiatedAttrVec LateAttrs;
   Instantiator.enableLateAttributeInstantiation(&LateAttrs);
 
+  bool MightHaveConstexprVirtualFunctions = false;
   for (auto *Member : Pattern->decls()) {
     // Don't instantiate members not belonging in this semantic context.
     // e.g. for:
@@ -2128,6 +2129,10 @@ Sema::InstantiateClass(SourceLocation Po
           Instantiation->setInvalidDecl();
           break;
         }
+      } else if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(NewMember)) {
+        if (MD->isConstexpr() && !MD->getFriendObjectKind() &&
+            (MD->isVirtualAsWritten() || Instantiation->getNumBases()))
+          MightHaveConstexprVirtualFunctions = true;
       }
 
       if (NewMember->isInvalidDecl())
@@ -2220,9 +2225,14 @@ Sema::InstantiateClass(SourceLocation Po
     Consumer.HandleTagDeclDefinition(Instantiation);
 
     // Always emit the vtable for an explicit instantiation definition
-    // of a polymorphic class template specialization.
+    // of a polymorphic class template specialization. Otherwise, eagerly
+    // instantiate only constexpr virtual functions in preparation for their use
+    // in constant evaluation.
     if (TSK == TSK_ExplicitInstantiationDefinition)
       MarkVTableUsed(PointOfInstantiation, Instantiation, true);
+    else if (MightHaveConstexprVirtualFunctions)
+      MarkVirtualMembersReferenced(PointOfInstantiation, Instantiation,
+                                   /*ConstexprOnly*/ true);
   }
 
   return Instantiation->isInvalidDecl();

Modified: cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp?rev=360635&r1=360634&r2=360635&view=diff
==============================================================================
--- cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp (original)
+++ cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp Mon May 13 16:35:21 2019
@@ -20,7 +20,10 @@ struct Literal {
 };
 
 struct S {
-  virtual int ImplicitlyVirtual() const = 0; // expected-note {{overridden virtual function}}
+  virtual int ImplicitlyVirtual() const = 0;
+#if __cplusplus <= 201703L
+  // expected-note at -2 {{overridden virtual function}}
+#endif
 };
 struct SS : S {
   int ImplicitlyVirtual() const;
@@ -32,12 +35,21 @@ struct T : SS, NonLiteral {
   constexpr T();
   constexpr int f() const;
 
-  //  - it shall not be virtual;
-  virtual constexpr int ExplicitlyVirtual() const { return 0; } // expected-error {{virtual function cannot be constexpr}}
-
-  constexpr int ImplicitlyVirtual() const { return 0; } // expected-error {{virtual function cannot be constexpr}}
-
-  virtual constexpr int OutOfLineVirtual() const; // expected-error {{virtual function cannot be constexpr}}
+  //  - it shall not be virtual; [until C++20]
+  virtual constexpr int ExplicitlyVirtual() const { return 0; }
+#if __cplusplus <= 201703L
+  // expected-error at -2 {{virtual function cannot be constexpr}}
+#endif
+
+  constexpr int ImplicitlyVirtual() const { return 0; }
+#if __cplusplus <= 201703L
+  // expected-error at -2 {{virtual function cannot be constexpr}}
+#endif
+
+  virtual constexpr int OutOfLineVirtual() const;
+#if __cplusplus <= 201703L
+  // expected-error at -2 {{virtual function cannot be constexpr}}
+#endif
 
   //  - its return type shall be a literal type;
   constexpr NonLiteral NonLiteralReturn() const { return {}; } // expected-error {{constexpr function's return type 'NonLiteral' is not a literal type}}

Modified: cfe/trunk/test/CXX/drs/dr18xx.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/drs/dr18xx.cpp?rev=360635&r1=360634&r2=360635&view=diff
==============================================================================
--- cfe/trunk/test/CXX/drs/dr18xx.cpp (original)
+++ cfe/trunk/test/CXX/drs/dr18xx.cpp Mon May 13 16:35:21 2019
@@ -52,9 +52,19 @@ namespace dr1872 { // dr1872: 9
   struct Z : virtual X {};
 
   constexpr int x = A<X>().f();
-  constexpr int y = A<Y>().f(); // expected-error {{constant expression}} expected-note {{call to virtual function}}
+  constexpr int y = A<Y>().f();
+#if __cplusplus <= 201703L
+  // expected-error at -2 {{constant expression}} expected-note at -2 {{call to virtual function}}
+#else
+  static_assert(y == 0);
+#endif
   // Note, this is invalid even though it would not use virtual dispatch.
-  constexpr int y2 = A<Y>().A<Y>::f(); // expected-error {{constant expression}} expected-note {{call to virtual function}}
+  constexpr int y2 = A<Y>().A<Y>::f();
+#if __cplusplus <= 201703L
+  // expected-error at -2 {{constant expression}} expected-note at -2 {{call to virtual function}}
+#else
+  static_assert(y == 0);
+#endif
   constexpr int z = A<Z>().f(); // expected-error {{constant expression}} expected-note {{non-literal type}}
 #endif
 }

Modified: cfe/trunk/test/CXX/drs/dr6xx.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/drs/dr6xx.cpp?rev=360635&r1=360634&r2=360635&view=diff
==============================================================================
--- cfe/trunk/test/CXX/drs/dr6xx.cpp (original)
+++ cfe/trunk/test/CXX/drs/dr6xx.cpp Mon May 13 16:35:21 2019
@@ -479,12 +479,21 @@ namespace dr647 { // dr647: yes
   // This is partially superseded by dr1358.
   struct A {
     constexpr virtual void f() const;
-    constexpr virtual void g() const {} // expected-error {{virtual function cannot be constexpr}}
+    constexpr virtual void g() const {}
+#if __cplusplus <= 201703L
+    // expected-error at -2 {{virtual function cannot be constexpr}}
+#endif
   };
 
-  struct X { virtual void f() const; }; // expected-note {{overridden}}
+  struct X { virtual void f() const; };
+#if __cplusplus <= 201703L
+  // expected-note at -2 {{overridden}}
+#endif
   struct B : X {
-    constexpr void f() const {} // expected-error {{virtual function cannot be constexpr}}
+    constexpr void f() const {}
+#if __cplusplus <= 201703L
+    // expected-error at -2 {{virtual function cannot be constexpr}}
+#endif
   };
 
   struct NonLiteral { NonLiteral() {} }; // expected-note {{not an aggregate and has no constexpr constructors}}

Modified: cfe/trunk/test/SemaCXX/constant-expression-cxx2a.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/constant-expression-cxx2a.cpp?rev=360635&r1=360634&r2=360635&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/constant-expression-cxx2a.cpp (original)
+++ cfe/trunk/test/SemaCXX/constant-expression-cxx2a.cpp Mon May 13 16:35:21 2019
@@ -211,3 +211,94 @@ constexpr bool for_range_init() {
   return k == 6;
 }
 static_assert(for_range_init());
+
+namespace Virtual {
+  struct NonZeroOffset { int padding = 123; };
+
+  // Ensure that we pick the right final overrider during construction.
+  struct A {
+    virtual constexpr char f() const { return 'A'; }
+    char a = f();
+  };
+  struct NoOverrideA : A {};
+  struct B : NonZeroOffset, NoOverrideA {
+    virtual constexpr char f() const { return 'B'; }
+    char b = f();
+  };
+  struct NoOverrideB : B {};
+  struct C : NonZeroOffset, A {
+    virtual constexpr char f() const { return 'C'; }
+    A *pba;
+    char c = ((A*)this)->f();
+    char ba = pba->f();
+    constexpr C(A *pba) : pba(pba) {}
+  };
+  struct D : NonZeroOffset, NoOverrideB, C { // expected-warning {{inaccessible}}
+    virtual constexpr char f() const { return 'D'; }
+    char d = f();
+    constexpr D() : C((B*)this) {}
+  };
+  constexpr D d;
+  static_assert(((B&)d).a == 'A');
+  static_assert(((C&)d).a == 'A');
+  static_assert(d.b == 'B');
+  static_assert(d.c == 'C');
+  // During the construction of C, the dynamic type of B's A is B.
+  static_assert(d.ba == 'B');
+  static_assert(d.d == 'D');
+  static_assert(d.f() == 'D');
+  constexpr const A &a = (B&)d;
+  constexpr const B &b = d;
+  static_assert(a.f() == 'D');
+  static_assert(b.f() == 'D');
+
+  // FIXME: It is unclear whether this should be permitted.
+  D d_not_constexpr;
+  static_assert(d_not_constexpr.f() == 'D'); // expected-error {{constant expression}} expected-note {{virtual function called on object 'd_not_constexpr' whose dynamic type is not constant}}
+
+  // Check that we apply a proper adjustment for a covariant return type.
+  struct Covariant1 {
+    D d;
+    virtual const A *f() const;
+  };
+  template<typename T>
+  struct Covariant2 : Covariant1 {
+    virtual const T *f() const;
+  };
+  template<typename T>
+  struct Covariant3 : Covariant2<T> {
+    constexpr virtual const D *f() const { return &this->d; }
+  };
+
+  constexpr Covariant3<B> cb;
+  constexpr Covariant3<C> cc;
+
+  constexpr const Covariant1 *cb1 = &cb;
+  constexpr const Covariant2<B> *cb2 = &cb;
+  static_assert(cb1->f()->a == 'A');
+  static_assert(cb1->f() == (B*)&cb.d);
+  static_assert(cb1->f()->f() == 'D');
+  static_assert(cb2->f()->b == 'B');
+  static_assert(cb2->f() == &cb.d);
+  static_assert(cb2->f()->f() == 'D');
+
+  constexpr const Covariant1 *cc1 = &cc;
+  constexpr const Covariant2<C> *cc2 = &cc;
+  static_assert(cc1->f()->a == 'A');
+  static_assert(cc1->f() == (C*)&cc.d);
+  static_assert(cc1->f()->f() == 'D');
+  static_assert(cc2->f()->c == 'C');
+  static_assert(cc2->f() == &cc.d);
+  static_assert(cc2->f()->f() == 'D');
+
+  static_assert(cb.f()->d == 'D');
+  static_assert(cc.f()->d == 'D');
+
+  struct Abstract {
+    constexpr virtual void f() = 0; // expected-note {{declared here}}
+    constexpr Abstract() { do_it(); } // expected-note {{in call to}}
+    constexpr void do_it() { f(); } // expected-note {{pure virtual function 'Virtual::Abstract::f' called}}
+  };
+  struct PureVirtualCall : Abstract { void f(); }; // expected-note {{in call to 'Abstract}}
+  constexpr PureVirtualCall pure_virtual_call; // expected-error {{constant expression}} expected-note {{in call to 'PureVirtualCall}}
+}

Modified: cfe/trunk/test/SemaCXX/cxx17-compat.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/cxx17-compat.cpp?rev=360635&r1=360634&r2=360635&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/cxx17-compat.cpp (original)
+++ cfe/trunk/test/SemaCXX/cxx17-compat.cpp Mon May 13 16:35:21 2019
@@ -63,3 +63,12 @@ void ForRangeInit() {
     // expected-warning at -4 {{range-based for loop initialization statements are incompatible with C++ standards before C++2a}}
 #endif
 }
+
+struct ConstexprVirtual {
+  virtual constexpr void f() {}
+#if __cplusplus <= 201703L
+    // expected-error at -2 {{virtual function cannot be constexpr}}
+#else
+    // expected-warning at -4 {{virtual constexpr functions are incompatible with C++ standards before C++2a}}
+#endif
+};

Modified: cfe/trunk/test/SemaCXX/integer-overflow.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/integer-overflow.cpp?rev=360635&r1=360634&r2=360635&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/integer-overflow.cpp (original)
+++ cfe/trunk/test/SemaCXX/integer-overflow.cpp Mon May 13 16:35:21 2019
@@ -1,4 +1,6 @@
 // RUN: %clang_cc1 %s -verify -fsyntax-only -std=gnu++98 -triple x86_64-pc-linux-gnu
+// RUN: %clang_cc1 %s -verify -fsyntax-only -std=gnu++2a -triple x86_64-pc-linux-gnu
+
 typedef unsigned long long uint64_t;
 typedef unsigned int uint32_t;
 
@@ -13,7 +15,7 @@ uint64_t f2(uint64_t, ...);
 
 static const uint64_t overflow = 1 * 4608 * 1024 * 1024; // expected-warning {{overflow in expression; result is 536870912 with type 'int'}}
 
-uint64_t check_integer_overflows(int i) { //expected-note {{declared here}}
+uint64_t check_integer_overflows(int i) { //expected-note 0+{{declared here}}
 // expected-warning at +1 {{overflow in expression; result is 536870912 with type 'int'}}
   uint64_t overflow = 4608 * 1024 * 1024,
 // expected-warning at +1 {{overflow in expression; result is 536870912 with type 'int'}}
@@ -72,6 +74,7 @@ uint64_t check_integer_overflows(int i)
   if ((uint64_t)((uint64_t)(4608 * 1024 * 1024) * (uint64_t)(4608 * 1024 * 1024)))
     return 5;
 
+#if __cplusplus < 201103L
   switch (i) {
 // expected-warning at +1 {{overflow in expression; result is 536870912 with type 'int'}}
   case 4608 * 1024 * 1024:
@@ -94,6 +97,7 @@ uint64_t check_integer_overflows(int i)
   case ((uint64_t)((uint64_t)(4608 * 1024 * 1024) * (uint64_t)(4608 * 1024 * 1024))):
     return 10;
   }
+#endif
 
 // expected-warning at +1 {{overflow in expression; result is 536870912 with type 'int'}}
   while (4608 * 1024 * 1024);
@@ -160,11 +164,13 @@ uint64_t check_integer_overflows(int i)
 // expected-warning at +1 {{overflow in expression; result is 536870912 with type 'int'}}
   (__imag__ x) = 4608 * 1024 * 1024;
 
-// expected-warning at +4 {{overflow in expression; result is 536870912 with type 'int'}}
-// expected-warning at +3 {{array index 536870912 is past the end of the array (which contains 10 elements)}}
-// expected-note at +1 {{array 'a' declared here}}
+// expected-warning at +2 {{overflow in expression; result is 536870912 with type 'int'}}
   uint64_t a[10];
   a[4608 * 1024 * 1024] = 1;
+#if __cplusplus < 201103L
+// expected-warning at -2 {{array index 536870912 is past the end of the array (which contains 10 elements)}}
+// expected-note at -4 {{array 'a' declared here}}
+#endif
 
 // expected-warning at +1 2{{overflow in expression; result is 536870912 with type 'int'}}
   return ((4608 * 1024 * 1024) + ((uint64_t)(4608 * 1024 * 1024)));
@@ -184,3 +190,22 @@ void check_integer_overflows_in_function
 // expected-warning at +1 {{overflow in expression; result is 536870912 with type 'int'}}
   (void)f2(0, f0(4608 * 1024 * 1024));
 }
+
+// Tests that ensure that evaluation-for-overflow of random expressions doesn't
+// crash.
+namespace EvaluationCrashes {
+  namespace VirtualCallWithVbase {
+    struct A {};
+    struct B : virtual A {
+      virtual bool f(const void *, int);
+    };
+    struct C : B {
+      bool f(const void *, int);
+    };
+    int d;
+    bool e(C c) {
+      if (c.f(&d, d)) {}
+      return true;
+    }
+  }
+}

Modified: cfe/trunk/www/cxx_status.html
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/www/cxx_status.html?rev=360635&r1=360634&r2=360635&view=diff
==============================================================================
--- cfe/trunk/www/cxx_status.html (original)
+++ cfe/trunk/www/cxx_status.html Mon May 13 16:35:21 2019
@@ -965,7 +965,7 @@ as the draft C++2a standard evolves.
     <tr>
       <td rowspan=4>Relaxations of <tt>constexpr</tt> restrictions</td>
       <td><a href="http://wg21.link/p1064r0">P1064R0</a></td>
-      <td class="none" align="center">No</td>
+      <td class="svn" align="center">SVN</td>
     </tr>
       <tr> <!-- from San Diego -->
         <td><a href="http://wg21.link/p1002r1">P1002R1</a></td>




More information about the cfe-commits mailing list