[clang] [clang][bytecode] Fix dynamic cast wrt. multiple base classes (PR #207722)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Jul 6 05:48:08 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Timm Baeder (tbaederr)
<details>
<summary>Changes</summary>
The previous code did not consider that the base class we're getting might not be on the path from `TypePtr` to `TypePtr.stripBaseCasts()` at all, because of multiple bases.
---
Full diff: https://github.com/llvm/llvm-project/pull/207722.diff
5 Files Affected:
- (modified) clang/lib/AST/ByteCode/Interp.cpp (+35-8)
- (modified) clang/lib/AST/ByteCode/Pointer.h (+13-5)
- (modified) clang/test/AST/ByteCode/cxx20.cpp (+1-2)
- (modified) clang/test/AST/ByteCode/cxx2a.cpp (+33)
- (modified) clang/test/CXX/dcl.decl/dcl.init/dcl.init.general/p16-cxx20.cpp (+1)
``````````diff
diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp
index 7884aa5b5b745..fcd2e9a10f625 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -1939,19 +1939,46 @@ bool Call(InterpState &S, CodePtr OpPC, const Function *Func,
return true;
}
-static bool getDynamicDecl(InterpState &S, CodePtr OpPC, Pointer TypePtr,
+static bool getDynamicDecl(InterpState &S, CodePtr OpPC, PtrView TypePtr,
const CXXRecordDecl *&DynamicDecl) {
- TypePtr = TypePtr.stripBaseCasts();
+
+ if (S.InitializingPtrs.empty()) {
+ TypePtr = TypePtr.stripBaseCasts();
+ } else {
+ auto depth = [](PtrView V) -> unsigned {
+ unsigned C = 1;
+ while (!V.isRoot()) {
+ ++C;
+ V = V.getBase();
+ }
+ return C;
+ };
+ // Consider a 'normal' diamond hierarchy:
+ // A A 3
+ // | |
+ // B C 2
+ // \ /
+ // \ /
+ // D 1
+ // When we use a pointer of D*, cast it to B's A* and
+ // use it during the construction of C*, the expected
+ // dynamic type is B.
+ PtrView InitPtr = S.InitializingPtrs.back();
+ assert(depth(TypePtr) >= depth(InitPtr));
+ unsigned D = depth(TypePtr) - depth(InitPtr);
+ for (unsigned I = 0; I != D; ++I)
+ TypePtr = TypePtr.getBase();
+ }
QualType DynamicType = TypePtr.getType();
- if (TypePtr.isStatic() || TypePtr.isConst()) {
- if (const VarDecl *VD = TypePtr.getRootVarDecl();
+ if (TypePtr.Pointee->isStatic() || TypePtr.isConst()) {
+ if (const VarDecl *VD = Pointer(TypePtr).getRootVarDecl();
VD && !VD->isConstexpr()) {
const Expr *E = S.Current->getExpr(OpPC);
- APValue V = TypePtr.toAPValue(S.getASTContext());
+ APValue V = Pointer(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);
+ << AK_MemberCall << V.getAsString(S.getASTContext(), TT);
return false;
}
}
@@ -2186,7 +2213,7 @@ bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func,
const FunctionDecl *Callee = Func->getDecl();
const CXXRecordDecl *DynamicDecl = nullptr;
- if (!getDynamicDecl(S, OpPC, ThisPtr, DynamicDecl))
+ if (!getDynamicDecl(S, OpPC, ThisPtr.view(), DynamicDecl))
return false;
assert(DynamicDecl);
@@ -2194,7 +2221,7 @@ bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func,
const auto *InitialFunction = cast<CXXMethodDecl>(Callee);
const CXXMethodDecl *Overrider;
- if (StaticDecl != DynamicDecl && !S.initializingBlock(ThisPtr.block())) {
+ if (StaticDecl != DynamicDecl) {
if (!DynamicDecl->isDerivedFrom(StaticDecl))
return false;
Overrider = S.getContext().getOverridingFunction(DynamicDecl, StaticDecl,
diff --git a/clang/lib/AST/ByteCode/Pointer.h b/clang/lib/AST/ByteCode/Pointer.h
index 8a413d01d2dd5..dda5ac61ef126 100644
--- a/clang/lib/AST/ByteCode/Pointer.h
+++ b/clang/lib/AST/ByteCode/Pointer.h
@@ -61,6 +61,10 @@ struct PtrView {
return Base == Pointee->getDescriptor()->getMetadataSize();
}
+ bool isConst() const {
+ return isRoot() ? getDeclDesc()->IsConst : getInlineDesc()->IsConst;
+ }
+
InlineDescriptor *getInlineDesc() const {
assert(Base != sizeof(GlobalInlineDescriptor));
assert(Base <= Pointee->getSize());
@@ -137,6 +141,13 @@ struct PtrView {
return PtrView{Pointee, Next, Offset};
}
+ [[nodiscard]] PtrView stripBaseCasts() const {
+ PtrView V = *this;
+ while (V.isBaseClass())
+ V = V.getBase();
+ return V;
+ }
+
[[nodiscard]] PtrView getArray() const {
assert(Offset != Base && "not an array element");
return PtrView{Pointee, Base, Base};
@@ -758,7 +769,7 @@ class Pointer {
bool isConst() const {
if (isIntegralPointer())
return true;
- return isRoot() ? getDeclDesc()->IsConst : getInlineDesc()->IsConst;
+ return view().isConst();
}
bool isConstInMutable() const {
if (!isBlockPointer())
@@ -985,10 +996,7 @@ class Pointer {
/// The result is either a root pointer or something
/// that isn't a base class anymore.
[[nodiscard]] Pointer stripBaseCasts() const {
- PtrView V = view();
- while (V.isBaseClass())
- V = V.getBase();
- return Pointer(V);
+ return Pointer(view().stripBaseCasts());
}
/// Compare two pointers.
diff --git a/clang/test/AST/ByteCode/cxx20.cpp b/clang/test/AST/ByteCode/cxx20.cpp
index b7ca154a0a987..70ff1045a74da 100644
--- a/clang/test/AST/ByteCode/cxx20.cpp
+++ b/clang/test/AST/ByteCode/cxx20.cpp
@@ -1099,8 +1099,7 @@ namespace Virtual {
static_assert(d.b == 'B');
static_assert(d.c == 'C');
// During the construction of C, the dynamic type of B's A is B.
- static_assert(d.ba == 'B'); // expected-error {{failed}} \
- // expected-note {{expression evaluates to}}
+ static_assert(d.ba == 'B');
static_assert(d.d == 'D');
static_assert(d.f() == 'D');
constexpr const A &a = (B&)d;
diff --git a/clang/test/AST/ByteCode/cxx2a.cpp b/clang/test/AST/ByteCode/cxx2a.cpp
index 78769a4def5b0..03c41edf7a70a 100644
--- a/clang/test/AST/ByteCode/cxx2a.cpp
+++ b/clang/test/AST/ByteCode/cxx2a.cpp
@@ -54,6 +54,39 @@ namespace Covariant {
static_assert(cb1->f()->a == 'Z');
}
+namespace Covariant2 {
+ struct A {
+ };
+ struct B : A {
+ };
+ struct C : A {
+ };
+ struct D : B, C {
+ };
+
+ // Check that we apply a proper adjustment for a covariant return type.
+ struct Covariant1 {
+ D d;
+ virtual const A *f() const;
+ };
+
+ template<typename T>
+ struct Covariant2 : Covariant1 {
+ virtual const T *f() const;
+ };
+
+ template<typename T>
+ struct Covariant3 : Covariant2<T> {
+ constexpr virtual const D *f() const { return &this->d; }
+ };
+
+ constexpr Covariant3<C> cc;
+ constexpr const Covariant1 *cc1 = &cc;
+
+ // LHS static type is A*.
+ static_assert(cc1->f() == (C*)&cc.d); // expected-error {{static assertion failed}}
+}
+
namespace DtorOrder {
struct Buf {
char buf[64];
diff --git a/clang/test/CXX/dcl.decl/dcl.init/dcl.init.general/p16-cxx20.cpp b/clang/test/CXX/dcl.decl/dcl.init/dcl.init.general/p16-cxx20.cpp
index fc6908fefac85..0d49a93af86cc 100644
--- a/clang/test/CXX/dcl.decl/dcl.init/dcl.init.general/p16-cxx20.cpp
+++ b/clang/test/CXX/dcl.decl/dcl.init/dcl.init.general/p16-cxx20.cpp
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s -fexperimental-new-constant-interpreter
namespace GH69890 {
// If the initializer is (), the object is value-initialized.
``````````
</details>
https://github.com/llvm/llvm-project/pull/207722
More information about the cfe-commits
mailing list