[clang] 5704dde - [clang][Interp] Reject calling virtual constexpr functions in pre-c++20
Timm Bäder via cfe-commits
cfe-commits at lists.llvm.org
Tue Sep 5 03:31:16 PDT 2023
Author: Timm Bäder
Date: 2023-09-05T12:21:26+02:00
New Revision: 5704dde307359e9155e2fcd99c413c0cf932aa13
URL: https://github.com/llvm/llvm-project/commit/5704dde307359e9155e2fcd99c413c0cf932aa13
DIFF: https://github.com/llvm/llvm-project/commit/5704dde307359e9155e2fcd99c413c0cf932aa13.diff
LOG: [clang][Interp] Reject calling virtual constexpr functions in pre-c++20
Differential Revision: https://reviews.llvm.org/D157619
Added:
Modified:
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
Removed:
################################################################################
diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index d13a805f0714ff..244290dc6393f4 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -1816,24 +1816,7 @@ Record *ByteCodeExprGen<Emitter>::getRecord(const RecordDecl *RD) {
template <class Emitter>
const Function *ByteCodeExprGen<Emitter>::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<ByteCodeEmitter>(Ctx, P).compileFunc(FD))
- Func = *R;
- else {
- llvm::consumeError(R.takeError());
- return nullptr;
- }
- }
-
- return Func;
+ return Ctx.getOrCreateFunction(FD);
}
template <class Emitter>
diff --git a/clang/lib/AST/Interp/Context.cpp b/clang/lib/AST/Interp/Context.cpp
index 6e0d949457d673..1a732b6c1a092a 100644
--- a/clang/lib/AST/Interp/Context.cpp
+++ b/clang/lib/AST/Interp/Context.cpp
@@ -210,3 +210,24 @@ Context::getOverridingFunction(const CXXRecordDecl *DynamicDecl,
"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<ByteCodeEmitter>(*this, *P).compileFunc(FD))
+ Func = *R;
+ else {
+ llvm::consumeError(R.takeError());
+ return nullptr;
+ }
+ }
+
+ return Func;
+}
diff --git a/clang/lib/AST/Interp/Context.h b/clang/lib/AST/Interp/Context.h
index f5dc80af757a14..617fbaa928f40e 100644
--- a/clang/lib/AST/Interp/Context.h
+++ b/clang/lib/AST/Interp/Context.h
@@ -72,6 +72,9 @@ class Context final {
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/Interp.h b/clang/lib/AST/Interp/Interp.h
index b87a5c1cbd0e48..5006f72fe7237f 100644
--- a/clang/lib/AST/Interp/Interp.h
+++ b/clang/lib/AST/Interp/Interp.h
@@ -1765,7 +1765,15 @@ inline bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func) {
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
index 7976f2d91e99c0..4e39ded41ad25f 100644
--- a/clang/test/AST/Interp/records.cpp
+++ b/clang/test/AST/Interp/records.cpp
@@ -871,6 +871,35 @@ namespace VirtualFunctionPointers {
};
#endif
+#if __cplusplus < 202002L
+namespace VirtualFromBase {
+ struct S1 {
+ virtual int f() const;
+ };
+ struct S2 {
+ virtual int f();
+ };
+ template <typename T> struct X : T {
+ constexpr X() {}
+ double d = 0.0;
+ constexpr int f() { return sizeof(T); }
+ };
+
+ // Non-virtual f(), OK.
+ constexpr X<X<S1>> xxs1;
+ constexpr X<S1> *p = const_cast<X<X<S1>>*>(&xxs1);
+ static_assert(p->f() == sizeof(S1), "");
+
+ // Virtual f(), not OK.
+ constexpr X<X<S2>> xxs2;
+ constexpr X<S2> *q = const_cast<X<X<S2>>*>(&xxs2);
+ static_assert(q->f() == sizeof(X<S2>), ""); // 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;
More information about the cfe-commits
mailing list