[PATCH] D157619: [clang][Interp] Reject calling virtual constexpr functions in pre-c++20
This revision was landed with ongoing or failed builds. This revision was automatically updated to reflect the committed changes. Closed by commit rG5704dde30735: [clang][Interp] Reject calling virtual constexpr functions in pre-c++20 (authored by tbaeder). Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D157619/new/ https://reviews.llvm.org/D157619 Files: clang/lib/AST/Interp/ByteCodeExprGen.cpp clang/lib/AST/Interp/Context.cpp clang/lib/AST/Interp/Context.h clang/lib/AST/Interp/Interp.h clang/test/AST/Interp/records.cpp Index: clang/test/AST/Interp/records.cpp === --- clang/test/AST/Interp/records.cpp +++ 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>*>(); + static_assert(p->f() == sizeof(S1), ""); + + // Virtual f(), not OK. + constexpr X> xxs2; + constexpr X *q = const_cast>*>(); + 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; Index: clang/lib/AST/Interp/Interp.h === --- clang/lib/AST/Interp/Interp.h +++ 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(); Index: clang/lib/AST/Interp/Context.h === --- clang/lib/AST/Interp/Context.h +++ 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) { Index: clang/lib/AST/Interp/Context.cpp === --- clang/lib/AST/Interp/Context.cpp +++ 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; +} Index: clang/lib/AST/Interp/ByteCodeExprGen.cpp === --- clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ 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 ___ cfe-commits mailing
[PATCH] D157619: [clang][Interp] Reject calling virtual constexpr functions in pre-c++20
aaron.ballman accepted this revision. aaron.ballman added a comment. This revision is now accepted and ready to land. LGTM! Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D157619/new/ https://reviews.llvm.org/D157619 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D157619: [clang][Interp] Reject calling virtual constexpr functions in pre-c++20
tbaeder created this revision. tbaeder added reviewers: aaron.ballman, erichkeane, shafik, cor3ntin. Herald added a project: All. tbaeder requested review of this revision. Herald added a project: clang. Herald added a subscriber: cfe-commits. Create a new `getOrCreateFunction()` in `Context`, so we can lazily compile functions when invoking them in `CallVirt`. This was necessary in the added test case for example. Also, reject calling virtual constexpr functions in pre-c++20. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D157619 Files: clang/lib/AST/Interp/ByteCodeExprGen.cpp clang/lib/AST/Interp/Context.cpp clang/lib/AST/Interp/Context.h clang/lib/AST/Interp/Interp.h clang/test/AST/Interp/records.cpp Index: clang/test/AST/Interp/records.cpp === --- clang/test/AST/Interp/records.cpp +++ clang/test/AST/Interp/records.cpp @@ -857,6 +857,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>*>(); + static_assert(p->f() == sizeof(S1), ""); + + // Virtual f(), not OK. + constexpr X> xxs2; + constexpr X *q = const_cast>*>(); + 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; Index: clang/lib/AST/Interp/Interp.h === --- clang/lib/AST/Interp/Interp.h +++ clang/lib/AST/Interp/Interp.h @@ -1835,7 +1835,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(); Index: clang/lib/AST/Interp/Context.h === --- clang/lib/AST/Interp/Context.h +++ 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) { Index: clang/lib/AST/Interp/Context.cpp === --- clang/lib/AST/Interp/Context.cpp +++ clang/lib/AST/Interp/Context.cpp @@ -215,3 +215,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; +} Index: clang/lib/AST/Interp/ByteCodeExprGen.cpp === --- clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -2063,24 +2063,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