https://github.com/tbaederr created 
https://github.com/llvm/llvm-project/pull/204289

None

>From 7ad11c79468361c9c0bdc94b1fb8ecac200e7094 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <[email protected]>
Date: Wed, 17 Jun 2026 07:47:18 +0200
Subject: [PATCH] [clang][bytecode] Support virtual bases

---
 clang/include/clang/AST/APValue.h         | 27 +++++++++++++++
 clang/lib/AST/APValue.cpp                 |  7 ++++
 clang/lib/AST/ByteCode/Interp.cpp         |  3 ++
 clang/lib/AST/ByteCode/Pointer.cpp        |  4 +--
 clang/lib/AST/DeclCXX.cpp                 |  5 +--
 clang/lib/AST/ExprConstant.cpp            | 24 +++++++++++++
 clang/lib/Sema/SemaDeclCXX.cpp            |  7 ++--
 clang/lib/Sema/SemaType.cpp               |  2 +-
 clang/test/AST/ByteCode/cxx23.cpp         |  1 -
 clang/test/AST/ByteCode/virtual-bases.cpp | 42 +++++++++++++++++++++++
 10 files changed, 112 insertions(+), 10 deletions(-)
 create mode 100644 clang/test/AST/ByteCode/virtual-bases.cpp

diff --git a/clang/include/clang/AST/APValue.h 
b/clang/include/clang/AST/APValue.h
index acbd922ba5319..a6eec10a966cf 100644
--- a/clang/include/clang/AST/APValue.h
+++ b/clang/include/clang/AST/APValue.h
@@ -297,7 +297,9 @@ class APValue {
     APValue *Elts;
     unsigned NumBases;
     unsigned NumFields;
+    unsigned NumVirtualBases;
     StructData(unsigned NumBases, unsigned NumFields);
+    StructData(unsigned NumBases, unsigned NumFields, unsigned 
NumVirtualBases);
     StructData(const StructData &) = delete;
     StructData &operator=(const StructData &) = delete;
     ~StructData();
@@ -422,6 +424,12 @@ class APValue {
       : Kind(None), AllowConstexprUnknown(false) {
     MakeStruct(NumBases, NumMembers);
   }
+  APValue(UninitStruct, unsigned NumBases, unsigned NumMembers,
+          unsigned NumVirtualBases)
+      : Kind(None), AllowConstexprUnknown(false) {
+    MakeStruct(NumBases, NumMembers, NumVirtualBases);
+  }
+
   /// Creates a new union APValue.
   /// \param ActiveDecl The FieldDecl of the active union member.
   /// \param ActiveValue The value of the active union member.
@@ -659,6 +667,10 @@ class APValue {
     assert(isStruct() && "Invalid accessor");
     return ((const StructData *)(const char *)&Data)->NumFields;
   }
+  unsigned getStructNumVirtualBases() const {
+    assert(isStruct() && "Invalid accessor");
+    return ((const StructData *)(const char *)&Data)->NumVirtualBases;
+  }
   APValue &getStructBase(unsigned i) {
     assert(isStruct() && "Invalid accessor");
     assert(i < getStructNumBases() && "base class index OOB");
@@ -669,12 +681,21 @@ class APValue {
     assert(i < getStructNumFields() && "field index OOB");
     return ((StructData *)(char *)&Data)->Elts[getStructNumBases() + i];
   }
+  APValue &getStructVirtualBase(unsigned i) {
+    assert(isStruct() && "Invalid accessor");
+    assert(i < getStructNumVirtualBases() && "base class index OOB");
+    return ((StructData *)(char *)&Data)
+        ->Elts[getStructNumBases() + getStructNumFields() + i];
+  }
   const APValue &getStructBase(unsigned i) const {
     return const_cast<APValue*>(this)->getStructBase(i);
   }
   const APValue &getStructField(unsigned i) const {
     return const_cast<APValue*>(this)->getStructField(i);
   }
+  const APValue &getStructVirtualBase(unsigned i) const {
+    return const_cast<APValue *>(this)->getStructVirtualBase(i);
+  }
 
   const FieldDecl *getUnionField() const {
     assert(isUnion() && "Invalid accessor");
@@ -793,6 +814,12 @@ class APValue {
     new ((void *)(char *)&Data) StructData(B, M);
     Kind = Struct;
   }
+  void MakeStruct(unsigned B, unsigned M, unsigned V) {
+    assert(isAbsent() && "Bad state change");
+    new ((void *)(char *)&Data) StructData(B, M, V);
+    Kind = Struct;
+  }
+
   void MakeUnion() {
     assert(isAbsent() && "Bad state change");
     new ((void *)(char *)&Data) UnionData();
diff --git a/clang/lib/AST/APValue.cpp b/clang/lib/AST/APValue.cpp
index fd51584f564bb..b762242887471 100644
--- a/clang/lib/AST/APValue.cpp
+++ b/clang/lib/AST/APValue.cpp
@@ -285,6 +285,13 @@ APValue::Arr::~Arr() { delete [] Elts; }
 APValue::StructData::StructData(unsigned NumBases, unsigned NumFields) :
   Elts(new APValue[NumBases+NumFields]),
   NumBases(NumBases), NumFields(NumFields) {}
+
+APValue::StructData::StructData(unsigned NumBases, unsigned NumFields,
+                                unsigned NumVirtualBases)
+    : Elts(new APValue[NumBases + NumFields + NumVirtualBases]),
+      NumBases(NumBases), NumFields(NumFields),
+      NumVirtualBases(NumVirtualBases) {}
+
 APValue::StructData::~StructData() {
   delete [] Elts;
 }
diff --git a/clang/lib/AST/ByteCode/Interp.cpp 
b/clang/lib/AST/ByteCode/Interp.cpp
index e5bf9c0c590ac..f61468079f7aa 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -1623,6 +1623,9 @@ static bool checkConstructor(InterpState &S, CodePtr 
OpPC, const Function *Func,
   if (!D->ElemRecord)
     return true;
 
+  if (S.getLangOpts().CPlusPlus26)
+    return true;
+
   if (D->ElemRecord->getNumVirtualBases() == 0)
     return true;
 
diff --git a/clang/lib/AST/ByteCode/Pointer.cpp 
b/clang/lib/AST/ByteCode/Pointer.cpp
index 96cd8bb9e11c2..ddcd21c504b2a 100644
--- a/clang/lib/AST/ByteCode/Pointer.cpp
+++ b/clang/lib/AST/ByteCode/Pointer.cpp
@@ -847,7 +847,7 @@ std::optional<APValue> Pointer::toRValue(const Context &Ctx,
         unsigned NB = Record->getNumBases();
         unsigned NV = Ptr.isBaseClass() ? 0 : Record->getNumVirtualBases();
 
-        R = APValue(APValue::UninitStruct(), NB, NF);
+        R = APValue(APValue::UninitStruct(), NB, NF, NV);
 
         for (unsigned I = 0; I != NF; ++I) {
           const Record::Field *FD = Record->getField(I);
@@ -875,7 +875,7 @@ std::optional<APValue> Pointer::toRValue(const Context &Ctx,
           QualType VirtBaseTy =
               Ctx.getASTContext().getCanonicalTagType(VD->Decl);
           PtrView VP = Ptr.atField(VD->Offset);
-          Ok &= Composite(VirtBaseTy, VP, R.getStructBase(NB + I));
+          Ok &= Composite(VirtBaseTy, VP, R.getStructVirtualBase(I));
         }
       }
       return Ok;
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index ce4ba971a4631..82510ad885af8 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -335,8 +335,9 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const 
*Bases,
       //   In the definition of a constexpr function [...]
       //    -- if the function is a constructor or destructor,
       //       its class shall not have any virtual base classes
-      data().DefaultedDefaultConstructorIsConstexpr = false;
-      data().DefaultedDestructorIsConstexpr = false;
+      data().DefaultedDefaultConstructorIsConstexpr =
+          C.getLangOpts().CPlusPlus26;
+      data().DefaultedDestructorIsConstexpr = C.getLangOpts().CPlusPlus26;
 
       // C++1z [class.copy]p8:
       //   The implicitly-declared copy constructor for a class X will have
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index bc98c0d86bb65..66d3cbbf44070 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -2472,6 +2472,8 @@ static bool 
CheckEvaluationResult(CheckEvaluationResultKind CERK,
     if (const CXXRecordDecl *CD = dyn_cast<CXXRecordDecl>(RD)) {
       unsigned BaseIndex = 0;
       for (const CXXBaseSpecifier &BS : CD->bases()) {
+        if (BS.isVirtual())
+          continue;
         const APValue &BaseValue = Value.getStructBase(BaseIndex);
         if (!BaseValue.hasValue()) {
           SourceLocation TypeBeginLoc = BS.getBaseTypeLoc();
@@ -2495,6 +2497,28 @@ static bool 
CheckEvaluationResult(CheckEvaluationResultKind CERK,
                                  I, CheckedTemps))
         return false;
     }
+
+#if 0
+    if (const CXXRecordDecl *CD = dyn_cast<CXXRecordDecl>(RD)) {
+      unsigned BaseIndex = 0;
+      for (const CXXBaseSpecifier &BS : CD->vbases()) {
+        if (!BS.isVirtual())
+          continue;
+        const APValue &BaseValue = Value.getStructVirtualBase(BaseIndex);
+        if (!BaseValue.hasValue()) {
+          SourceLocation TypeBeginLoc = BS.getBaseTypeLoc();
+          Info.FFDiag(TypeBeginLoc, diag::note_constexpr_uninitialized_base)
+              << BS.getType() << SourceRange(TypeBeginLoc, BS.getEndLoc());
+          return false;
+        }
+        if (!CheckEvaluationResult(CERK, Info, DiagLoc, BS.getType(), 
BaseValue,
+                                   Kind, /*SubobjectDecl=*/nullptr,
+                                   CheckedTemps))
+          return false;
+        ++BaseIndex;
+      }
+    }
+#endif
   }
 
   if (Value.isLValue() &&
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 418ff01f3d98a..8dc48525802d9 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -1944,7 +1944,7 @@ static bool CheckConstexprMissingReturn(Sema &SemaRef, 
const FunctionDecl *Dcl);
 bool Sema::CheckConstexprFunctionDefinition(const FunctionDecl *NewFD,
                                             CheckConstexprKind Kind) {
   const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(NewFD);
-  if (MD && MD->isInstance()) {
+  if (!getLangOpts().CPlusPlus26 && MD && MD->isInstance()) {
     // C++11 [dcl.constexpr]p4:
     //  The definition of a constexpr constructor shall satisfy the following
     //  constraints:
@@ -7702,12 +7702,12 @@ static bool defaultedSpecialMemberIsConstexpr(
                : true;
 
   //   -- the class shall not have any virtual base classes;
-  if (Ctor && ClassDecl->getNumVBases())
+  if (!S.getLangOpts().CPlusPlus26 && Ctor && ClassDecl->getNumVBases())
     return false;
 
   // C++1y [class.copy]p26:
   //   -- [the class] is a literal type, and
-  if (!Ctor && !ClassDecl->isLiteral() && !S.getLangOpts().CPlusPlus23)
+  if (!S.getLangOpts().CPlusPlus23 && !Ctor && !ClassDecl->isLiteral())
     return false;
 
   //   -- every constructor involved in initializing [...] base class
@@ -8067,7 +8067,6 @@ bool 
Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD,
         HadError = true;
         // FIXME: Explain why the special member can't be constexpr.
   }
-
   if (First) {
     // C++2a [dcl.fct.def.default]p3:
     //   If a function is explicitly defaulted on its first declaration, it is
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index d2bb312feadc1..dfe9b6943fd29 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -9903,7 +9903,7 @@ bool Sema::RequireLiteralType(SourceLocation Loc, 
QualType T,
   // cannot have any constexpr constructors or a trivial default constructor,
   // so is non-literal. This is better to diagnose than the resulting absence
   // of constexpr constructors.
-  if (RD->getNumVBases()) {
+  if (!getLangOpts().CPlusPlus26 && RD->getNumVBases()) {
     Diag(RD->getLocation(), diag::note_non_literal_virtual_base)
       << getLiteralDiagFromTagKind(RD->getTagKind()) << RD->getNumVBases();
     for (const auto &I : RD->vbases())
diff --git a/clang/test/AST/ByteCode/cxx23.cpp 
b/clang/test/AST/ByteCode/cxx23.cpp
index 0050d34a47269..ba7db75a589a1 100644
--- a/clang/test/AST/ByteCode/cxx23.cpp
+++ b/clang/test/AST/ByteCode/cxx23.cpp
@@ -317,7 +317,6 @@ namespace AnonUnionDtor {
   void bar() { foo(); }
 }
 
-/// FIXME: The two interpreters disagree about there to diagnose the 
non-constexpr destructor call.
 namespace NonLiteralDtorInParam {
   class NonLiteral { // all20-note {{is not an aggregate and has no constexpr 
constructors other than copy or move constructors}}
   public:
diff --git a/clang/test/AST/ByteCode/virtual-bases.cpp 
b/clang/test/AST/ByteCode/virtual-bases.cpp
new file mode 100644
index 0000000000000..67bd996da1546
--- /dev/null
+++ b/clang/test/AST/ByteCode/virtual-bases.cpp
@@ -0,0 +1,42 @@
+// RUN: %clang_cc1 -std=c++26 -fexperimental-new-constant-interpreter %s
+
+
+namespace PaperSample {
+  struct Superbase {
+    int a;
+  };
+
+  struct Common: Superbase {
+    unsigned counter{0};
+  };
+
+  struct Left: virtual Common {
+    unsigned value{0};
+
+    constexpr Left() = default;
+
+    constexpr const unsigned & get_counter() const {
+      return Common::counter;
+    }
+  };
+
+
+  struct Right: virtual Common {
+    unsigned value{0};
+
+    constexpr Right() = default;
+    constexpr const unsigned & get_counter() const {
+      return Common::counter;
+    }
+  };
+
+  struct Child: Left, Right {
+    unsigned x{0};
+    unsigned y{0};
+
+    constexpr Child() = default;
+  };
+
+  constexpr auto ch = Child();
+  static_assert(&ch.Left::get_counter() == &ch.Right::get_counter());
+}

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

Reply via email to