diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -1816,24 +1816,7 @@ template const Function *ByteCodeExprGen::getFunction(const FunctionDecl *FD) { - assert(FD); - const Function *Func = P.getFunction(FD); - bool IsBeingCompiled = Func && !Func->isFullyCompiled(); - bool WasNotDefined = Func && !Func->isConstexpr() && !Func->hasBody(); - - if (IsBeingCompiled) - return Func; - - if (!Func || WasNotDefined) { - if (auto R = ByteCodeStmtGen(Ctx, P).compileFunc(FD)) - Func = *R; - else { - llvm::consumeError(R.takeError()); - return nullptr; - } - } - - return Func; + return Ctx.getOrCreateFunction(FD); } template diff --git a/clang/lib/AST/Interp/Context.h b/clang/lib/AST/Interp/Context.h --- a/clang/lib/AST/Interp/Context.h +++ b/clang/lib/AST/Interp/Context.h @@ -72,6 +72,9 @@ getOverridingFunction(const CXXRecordDecl *DynamicDecl, const CXXRecordDecl *StaticDecl, const CXXMethodDecl *InitialFunction) const; + + const Function *getOrCreateFunction(const FunctionDecl *FD); + /// Returns whether we should create a global variable for the /// given ValueDecl. static bool shouldBeGloballyIndexed(const ValueDecl *VD) { diff --git a/clang/lib/AST/Interp/Context.cpp b/clang/lib/AST/Interp/Context.cpp --- a/clang/lib/AST/Interp/Context.cpp +++ b/clang/lib/AST/Interp/Context.cpp @@ -210,3 +210,24 @@ "Couldn't find an overriding function in the class hierarchy?"); return nullptr; } + +const Function *Context::getOrCreateFunction(const FunctionDecl *FD) { + assert(FD); + const Function *Func = P->getFunction(FD); + bool IsBeingCompiled = Func && !Func->isFullyCompiled(); + bool WasNotDefined = Func && !Func->isConstexpr() && !Func->hasBody(); + + if (IsBeingCompiled) + return Func; + + if (!Func || WasNotDefined) { + if (auto R = ByteCodeStmtGen(*this, *P).compileFunc(FD)) + Func = *R; + else { + llvm::consumeError(R.takeError()); + return nullptr; + } + } + + return Func; +} diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h --- a/clang/lib/AST/Interp/Interp.h +++ b/clang/lib/AST/Interp/Interp.h @@ -1765,7 +1765,15 @@ DynamicDecl, StaticDecl, InitialFunction); if (Overrider != InitialFunction) { - Func = S.P.getFunction(Overrider); + // DR1872: An instantiated virtual constexpr function can't be called in a + // constant expression (prior to C++20). We can still constant-fold such a + // call. + if (!S.getLangOpts().CPlusPlus20 && Overrider->isVirtual()) { + const Expr *E = S.Current->getExpr(OpPC); + S.CCEDiag(E, diag::note_constexpr_virtual_call) << E->getSourceRange(); + } + + Func = S.getContext().getOrCreateFunction(Overrider); const CXXRecordDecl *ThisFieldDecl = ThisPtr.getFieldDesc()->getType()->getAsCXXRecordDecl(); diff --git a/clang/test/AST/Interp/records.cpp b/clang/test/AST/Interp/records.cpp --- a/clang/test/AST/Interp/records.cpp +++ b/clang/test/AST/Interp/records.cpp @@ -871,6 +871,35 @@ }; #endif +#if __cplusplus < 202002L +namespace VirtualFromBase { + struct S1 { + virtual int f() const; + }; + struct S2 { + virtual int f(); + }; + template struct X : T { + constexpr X() {} + double d = 0.0; + constexpr int f() { return sizeof(T); } + }; + + // Non-virtual f(), OK. + constexpr X> xxs1; + constexpr X *p = const_cast>*>(&xxs1); + static_assert(p->f() == sizeof(S1), ""); + + // Virtual f(), not OK. + constexpr X> xxs2; + constexpr X *q = const_cast>*>(&xxs2); + static_assert(q->f() == sizeof(X), ""); // ref-error {{not an integral constant expression}} \ + // ref-note {{cannot evaluate call to virtual function}} \ + // expected-error {{not an integral constant expression}} \ + // expected-note {{cannot evaluate call to virtual function}} +} +#endif + namespace CompositeDefaultArgs { struct Foo { int a;