[clang] [clang][bytecode] Assert on virtual func call from array elem (PR #158502)
marius doerner via cfe-commits
cfe-commits at lists.llvm.org
Sun Sep 21 02:43:31 PDT 2025
Marius =?utf-8?q?Dörner?= <marius.doerner1 at icloud.com>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/158502 at github.com>
https://github.com/mariusdr updated https://github.com/llvm/llvm-project/pull/158502
>From 7c01c2b38737302d2a4310b40b99ab14002068a3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marius=20D=C3=B6rner?= <marius.doerner1 at icloud.com>
Date: Sun, 14 Sep 2025 21:03:57 +0200
Subject: [PATCH 1/2] [Clang][Interp] Assert on virtual func call from array
elem
Fixes #152893.
An assert was raised when a constexpr virtual function was called from
an constexpr array element with -fexperimental-new-constant-interpreter
set.
---
clang/lib/AST/ByteCode/Interp.cpp | 9 +++++++--
clang/test/AST/ByteCode/cxx20.cpp | 22 ++++++++++++++++++++++
2 files changed, 29 insertions(+), 2 deletions(-)
diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp
index 0f322f6ed42ac..94815975c1b35 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -1666,10 +1666,15 @@ bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func,
TypePtr = TypePtr.getBase();
QualType DynamicType = TypePtr.getType();
- if (DynamicType->isPointerType() || DynamicType->isReferenceType())
+ if (DynamicType->isPointerType() || DynamicType->isReferenceType()) {
DynamicDecl = DynamicType->getPointeeCXXRecordDecl();
- else
+ } else if (DynamicType->isArrayType()) {
+ const Type *ElemType = DynamicType->getPointeeOrArrayElementType();
+ assert(ElemType);
+ DynamicDecl = ElemType->getAsCXXRecordDecl();
+ } else {
DynamicDecl = DynamicType->getAsCXXRecordDecl();
+ }
}
assert(DynamicDecl);
diff --git a/clang/test/AST/ByteCode/cxx20.cpp b/clang/test/AST/ByteCode/cxx20.cpp
index 67bf9a732d8b7..8e77a081e3e60 100644
--- a/clang/test/AST/ByteCode/cxx20.cpp
+++ b/clang/test/AST/ByteCode/cxx20.cpp
@@ -1100,3 +1100,25 @@ 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());
+}
>From 95717b91294fa6b7744df68f46d67f858c872f14 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marius=20D=C3=B6rner?= <marius.doerner1 at icloud.com>
Date: Sun, 21 Sep 2025 11:34:35 +0200
Subject: [PATCH 2/2] Add diagnostic when dynamic type is not constant
+ test cases
---
clang/lib/AST/ByteCode/Interp.cpp | 60 +++++++++++++++++++------------
clang/lib/AST/ByteCode/Interp.h | 2 +-
clang/test/AST/ByteCode/cxx20.cpp | 20 +++++++++++
3 files changed, 59 insertions(+), 23 deletions(-)
diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp
index 94815975c1b35..e76b69df7c050 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -445,7 +445,8 @@ bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
return true;
}
-bool CheckConstant(InterpState &S, CodePtr OpPC, const Descriptor *Desc) {
+bool CheckConstant(InterpState &S, CodePtr OpPC, const Descriptor *Desc,
+ bool NoDiag) {
assert(Desc);
const auto *D = Desc->asVarDecl();
@@ -470,7 +471,7 @@ bool CheckConstant(InterpState &S, CodePtr OpPC, const Descriptor *Desc) {
}
if (IsConstant) {
- if (S.getLangOpts().CPlusPlus) {
+ if (S.getLangOpts().CPlusPlus && !NoDiag) {
S.CCEDiag(S.Current->getLocation(OpPC),
S.getLangOpts().CPlusPlus11
? diag::note_constexpr_ltor_non_constexpr
@@ -478,7 +479,7 @@ bool CheckConstant(InterpState &S, CodePtr OpPC, const Descriptor *Desc) {
1)
<< D << T;
S.Note(D->getLocation(), diag::note_declared_at);
- } else {
+ } else if (!NoDiag) {
S.CCEDiag(S.Current->getLocation(OpPC));
}
return true;
@@ -493,16 +494,18 @@ bool CheckConstant(InterpState &S, CodePtr OpPC, const Descriptor *Desc) {
return true;
}
- diagnoseNonConstVariable(S, OpPC, D);
+ if (!NoDiag)
+ diagnoseNonConstVariable(S, OpPC, D);
return false;
}
-static bool CheckConstant(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
+static bool CheckConstant(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
+ bool NoDiag = false) {
if (!Ptr.isStatic() || !Ptr.isBlockPointer())
return true;
if (!Ptr.getDeclID())
return true;
- return CheckConstant(S, OpPC, Ptr.getDeclDesc());
+ return CheckConstant(S, OpPC, Ptr.getDeclDesc(), NoDiag);
}
bool CheckNull(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
@@ -1636,6 +1639,33 @@ 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 (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();
+ }
+
+ if (!CheckConstant(S, OpPC, TypePtr, true)) {
+ 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;
+ }
+ return true;
+}
+
bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func,
uint32_t VarArgSize) {
assert(Func->hasThisPointer());
@@ -1660,22 +1690,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 if (DynamicType->isArrayType()) {
- const Type *ElemType = DynamicType->getPointeeOrArrayElementType();
- assert(ElemType);
- DynamicDecl = ElemType->getAsCXXRecordDecl();
- } 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/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index b3b4b998439cc..29a8340ed1725 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -78,7 +78,7 @@ bool CheckDowncast(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
/// Checks if the Descriptor is of a constexpr or const global variable.
-bool CheckConstant(InterpState &S, CodePtr OpPC, const Descriptor *Desc);
+bool CheckConstant(InterpState &S, CodePtr OpPC, const Descriptor *Desc, bool NoDiag = false);
/// Checks if a pointer points to a mutable field.
bool CheckMutable(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
diff --git a/clang/test/AST/ByteCode/cxx20.cpp b/clang/test/AST/ByteCode/cxx20.cpp
index 8e77a081e3e60..7dc11314cce4f 100644
--- a/clang/test/AST/ByteCode/cxx20.cpp
+++ b/clang/test/AST/ByteCode/cxx20.cpp
@@ -1073,6 +1073,10 @@ namespace Virtual {
virtual constexpr int f() { 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}}
+
class L : public K {
public:
int b = f();
@@ -1083,6 +1087,18 @@ namespace Virtual {
static_assert(l.a == 10);
static_assert(l.b == 10);
static_assert(l.c == 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}}
}
namespace DiscardedTrivialCXXConstructExpr {
@@ -1121,4 +1137,8 @@ namespace VirtualFunctionCallThroughArrayElem {
};
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}}
}
More information about the cfe-commits
mailing list