Author: Timm Baeder
Date: 2026-07-07T15:40:22+02:00
New Revision: 0ac35389e9d47fb9aa2934cbfab274aa69d05317

URL: 
https://github.com/llvm/llvm-project/commit/0ac35389e9d47fb9aa2934cbfab274aa69d05317
DIFF: 
https://github.com/llvm/llvm-project/commit/0ac35389e9d47fb9aa2934cbfab274aa69d05317.diff

LOG: [clang][bytecode] Fix `getDynamicDecl()` wrt. multiple base classes 
(#207722)

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.

Added: 
    

Modified: 
    clang/lib/AST/ByteCode/Interp.cpp
    clang/lib/AST/ByteCode/Pointer.h
    clang/test/AST/ByteCode/cxx20.cpp
    clang/test/AST/ByteCode/cxx2a.cpp
    clang/test/CXX/dcl.decl/dcl.init/dcl.init.general/p16-cxx20.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/AST/ByteCode/Interp.cpp 
b/clang/lib/AST/ByteCode/Interp.cpp
index 82e93baba853d..b2af27fc88542 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -1966,19 +1966,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;
     }
   }
@@ -2213,7 +2240,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);
 
@@ -2221,7 +2248,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 cc9ec1666440c..2f2f2b95745b3 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.


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to