[clang] 5296d01 - [clang][bytecode] Assert on virtual func call from array elem (#158502)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Oct 6 06:08:42 PDT 2025
Author: marius doerner
Date: 2025-10-06T15:08:38+02:00
New Revision: 5296d017381f5bb4e3b29644767b98ce336698ce
URL: https://github.com/llvm/llvm-project/commit/5296d017381f5bb4e3b29644767b98ce336698ce
DIFF: https://github.com/llvm/llvm-project/commit/5296d017381f5bb4e3b29644767b98ce336698ce.diff
LOG: [clang][bytecode] Assert on virtual func call from array elem (#158502)
Fixes #152893.
An assert was raised when a constexpr virtual function was called from
an constexpr array element with -fexperimental-new-constant-interpreter
set.
Added:
Modified:
clang/lib/AST/ByteCode/Interp.cpp
clang/test/AST/ByteCode/cxx20.cpp
clang/test/SemaCXX/constant-expression-p2280r4.cpp
Removed:
################################################################################
diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp
index 21af3d6ac7f90..89043968915a9 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -1638,6 +1638,36 @@ bool Call(InterpState &S, CodePtr OpPC, const Function *Func,
return true;
}
+static bool GetDynamicDecl(InterpState &S, CodePtr OpPC, Pointer TypePtr,
+ const CXXRecordDecl *&DynamicDecl) {
+ while (TypePtr.isBaseClass())
+ TypePtr = TypePtr.getBase();
+
+ QualType DynamicType = TypePtr.getType();
+ if (TypePtr.isStatic() || TypePtr.isConst()) {
+ const VarDecl *VD = TypePtr.getDeclDesc()->asVarDecl();
+ if (!VD->isConstexpr()) {
+ const Expr *E = S.Current->getExpr(OpPC);
+ APValue V = TypePtr.toAPValue(S.getASTContext());
+ QualType TT = S.getASTContext().getLValueReferenceType(DynamicType);
+ S.FFDiag(E, diag::note_constexpr_polymorphic_unknown_dynamic_type)
+ << AccessKinds::AK_MemberCall << V.getAsString(S.getASTContext(), TT);
+ return false;
+ }
+ }
+
+ if (DynamicType->isPointerType() || DynamicType->isReferenceType()) {
+ DynamicDecl = DynamicType->getPointeeCXXRecordDecl();
+ } else if (DynamicType->isArrayType()) {
+ const Type *ElemType = DynamicType->getPointeeOrArrayElementType();
+ assert(ElemType);
+ DynamicDecl = ElemType->getAsCXXRecordDecl();
+ } else {
+ DynamicDecl = DynamicType->getAsCXXRecordDecl();
+ }
+ return true;
+}
+
bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func,
uint32_t VarArgSize) {
assert(Func->hasThisPointer());
@@ -1662,17 +1692,8 @@ bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func,
}
const CXXRecordDecl *DynamicDecl = nullptr;
- {
- Pointer TypePtr = ThisPtr;
- while (TypePtr.isBaseClass())
- TypePtr = TypePtr.getBase();
-
- QualType DynamicType = TypePtr.getType();
- if (DynamicType->isPointerType() || DynamicType->isReferenceType())
- DynamicDecl = DynamicType->getPointeeCXXRecordDecl();
- else
- DynamicDecl = DynamicType->getAsCXXRecordDecl();
- }
+ if (!GetDynamicDecl(S, OpPC, ThisPtr, DynamicDecl))
+ return false;
assert(DynamicDecl);
const auto *StaticDecl = cast<CXXRecordDecl>(Func->getParentDecl());
diff --git a/clang/test/AST/ByteCode/cxx20.cpp b/clang/test/AST/ByteCode/cxx20.cpp
index 67bf9a732d8b7..1888998ebe3dd 100644
--- a/clang/test/AST/ByteCode/cxx20.cpp
+++ b/clang/test/AST/ByteCode/cxx20.cpp
@@ -1070,9 +1070,30 @@ namespace Virtual {
public:
int a = f();
- virtual constexpr int f() { return 10; }
+ virtual constexpr int f() const { return 10; }
};
+ K k;
+ static_assert(k.f() == 10); // both-error {{not an integral constant expression}} \
+ // both-note {{virtual function called on object 'k' whose dynamic type is not constant}}
+
+ void f() {
+ constexpr K k;
+ static_assert(k.f() == 10);
+ }
+
+ void f2() {
+ K k;
+ static_assert(k.f() == 10); // both-error {{not an integral constant expression}} \
+ // both-note {{virtual function called on object 'k' whose dynamic type is not constant}}
+ }
+
+ static_assert(K().f() == 10);
+
+ void f3() {
+ static_assert(K().f() == 10);
+ }
+
class L : public K {
public:
int b = f();
@@ -1083,6 +1104,42 @@ namespace Virtual {
static_assert(l.a == 10);
static_assert(l.b == 10);
static_assert(l.c == 10);
+ static_assert(l.f() == 10);
+
+ struct M {
+ K& mk = k;
+ };
+ static_assert(M{}.mk.f() == 10); // both-error {{not an integral constant expression}} \
+ // both-note {{virtual function called on object 'k' whose dynamic type is not constant}}
+
+ struct N {
+ K* mk = &k;
+ };
+ static_assert(N{}.mk->f() == 10); // both-error {{not an integral constant expression}} \
+ // both-note {{virtual function called on object 'k' whose dynamic type is not constant}}
+
+ extern K o;
+ static_assert(o.f() == 10); // both-error {{not an integral constant expression}} \
+ // both-note {{virtual function called on object 'o' whose dynamic type is not constant}}
+ static K p;
+ static_assert(p.f() == 10); // both-error {{not an integral constant expression}} \
+ // both-note {{virtual function called on object 'p' whose dynamic type is not constant}}
+
+ void f4() {
+ static K p;
+ static_assert(p.f() == 10); // both-error {{not an integral constant expression}} \
+ // both-note {{virtual function called on object 'p' whose dynamic type is not constant}}
+ }
+
+ const K q;
+ static_assert(q.f() == 10); // both-error {{not an integral constant expression}} \
+ // both-note {{virtual function called on object 'q' whose dynamic type is not constant}}
+
+ void f5() {
+ const K q;
+ static_assert(q.f() == 10); // both-error {{not an integral constant expression}} \
+ // both-note {{virtual function called on object 'q' whose dynamic type is not constant}}
+ }
}
namespace DiscardedTrivialCXXConstructExpr {
@@ -1100,3 +1157,29 @@ namespace DiscardedTrivialCXXConstructExpr {
constexpr int y = foo(12); // both-error {{must be initialized by a constant expression}} \
// both-note {{in call to}}
}
+
+namespace VirtualFunctionCallThroughArrayElem {
+ struct X {
+ constexpr virtual int foo() const {
+ return 3;
+ }
+ };
+ constexpr X xs[5];
+ static_assert(xs[3].foo() == 3);
+
+ constexpr X xs2[1][2];
+ static_assert(xs2[0].foo() == 3); // both-error {{is not a structure or union}}
+ static_assert(xs2[0][0].foo() == 3);
+
+ struct Y: public X {
+ constexpr int foo() const override {
+ return 1;
+ }
+ };
+ constexpr Y ys[20];
+ static_assert(ys[12].foo() == static_cast<const X&>(ys[12]).foo());
+
+ X a[3][4];
+ static_assert(a[2][3].foo()); // both-error {{not an integral constant expression}} \
+ // both-note {{virtual function called on object 'a[2][3]' whose dynamic type is not constant}}
+}
diff --git a/clang/test/SemaCXX/constant-expression-p2280r4.cpp b/clang/test/SemaCXX/constant-expression-p2280r4.cpp
index 78e2e17016280..5cbfaffd04aa0 100644
--- a/clang/test/SemaCXX/constant-expression-p2280r4.cpp
+++ b/clang/test/SemaCXX/constant-expression-p2280r4.cpp
@@ -44,7 +44,7 @@ void splash(Swim& swam) { // nointerpreter-note {{declared here}
static_assert(how_many(swam) == 28); // ok
static_assert(Swim().lochte() == 12); // ok
static_assert(swam.lochte() == 12); // expected-error {{static assertion expression is not an integral constant expression}} \
- // nointerpreter-note {{virtual function called on object 'swam' whose dynamic type is not constant}}
+ // expected-note {{virtual function called on object 'swam' whose dynamic type is not constant}}
static_assert(swam.coughlin == 12); // expected-error {{static assertion expression is not an integral constant expression}} \
// nointerpreter-note {{read of variable 'swam' whose value is not known}}
}
More information about the cfe-commits
mailing list