Timm =?utf-8?q?Bäder?= <[email protected]>, Timm =?utf-8?q?Bäder?= <[email protected]> Message-ID: In-Reply-To: <llvm.org/llvm/llvm-project/pull/[email protected]>
https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/204289 >From 4640420ada0177403946fbd449c29d9b3875e773 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 1/3] [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()); +} >From a5450711fd20bc7fe9c5416a03aca1f86ad556be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= <[email protected]> Date: Wed, 17 Jun 2026 07:58:15 +0200 Subject: [PATCH 2/3] test more stuff --- clang/test/AST/ByteCode/virtual-bases.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/clang/test/AST/ByteCode/virtual-bases.cpp b/clang/test/AST/ByteCode/virtual-bases.cpp index 67bd996da1546..9ac5f626162da 100644 --- a/clang/test/AST/ByteCode/virtual-bases.cpp +++ b/clang/test/AST/ByteCode/virtual-bases.cpp @@ -3,11 +3,11 @@ namespace PaperSample { struct Superbase { - int a; + int a = 10; }; struct Common: Superbase { - unsigned counter{0}; + unsigned counter = 1337; }; struct Left: virtual Common { @@ -31,12 +31,16 @@ namespace PaperSample { }; struct Child: Left, Right { - unsigned x{0}; - unsigned y{0}; + unsigned x = 12; + unsigned y = 13; constexpr Child() = default; }; constexpr auto ch = Child(); static_assert(&ch.Left::get_counter() == &ch.Right::get_counter()); + static_assert(ch.counter == 1337); + + static_assert(((Common)ch).counter == 1337); + static_assert(ch.a == 10); } >From f109f1a2437a90ef6a8e32b92b70c5b2de45d198 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= <[email protected]> Date: Wed, 17 Jun 2026 13:26:46 +0200 Subject: [PATCH 3/3] Fix DR tests --- clang/test/CXX/drs/cwg16xx.cpp | 16 ++++++++++++++-- clang/test/CXX/drs/cwg6xx.cpp | 12 ++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/clang/test/CXX/drs/cwg16xx.cpp b/clang/test/CXX/drs/cwg16xx.cpp index bcae9e0b6d177..91cc8261eb6be 100644 --- a/clang/test/CXX/drs/cwg16xx.cpp +++ b/clang/test/CXX/drs/cwg16xx.cpp @@ -269,8 +269,20 @@ namespace cwg1658 { // cwg1658: 5 struct D : A { virtual void f() = 0; }; // #cwg1658-D struct X { - friend B::B(const B&) throw(); - friend C::C(C&); +#if __cplusplus >= 202400L + friend constexpr +#else + friend +#endif + B::B(const B&) throw(); + +#if __cplusplus >= 202400L + friend constexpr +#else + friend +#endif + C::C(C&); + friend D::D(D&); // since-cxx23-error@-1 {{non-constexpr declaration of 'D' follows constexpr declaration}} // since-cxx23-note@#cwg1658-D {{previous declaration is here}} diff --git a/clang/test/CXX/drs/cwg6xx.cpp b/clang/test/CXX/drs/cwg6xx.cpp index 3ba2b372cb715..451554a36d70d 100644 --- a/clang/test/CXX/drs/cwg6xx.cpp +++ b/clang/test/CXX/drs/cwg6xx.cpp @@ -534,10 +534,18 @@ namespace cwg644 { // cwg644: partial static_assert(__is_literal_type(B), ""); struct C : virtual A {}; +#if __cplusplus >= 202400L + static_assert(__is_literal_type(C), ""); +#else static_assert(!__is_literal_type(C), ""); +#endif struct D { C c; }; +#if __cplusplus >= 202400L + static_assert(__is_literal_type(D), ""); +#else static_assert(!__is_literal_type(D), ""); +#endif // FIXME: According to CWG644, E<C> is a literal type despite having virtual // base classes. This appears to be a wording defect. @@ -545,8 +553,12 @@ namespace cwg644 { // cwg644: partial struct E : T { constexpr E() = default; }; +#if __cplusplus >= 202400L + static_assert(__is_literal_type(E<C>), ""); +#else static_assert(!__is_literal_type(E<C>), ""); #endif +#endif } // namespace cwg644 // cwg645 increases permission to optimize; it's not clear that it's possible to _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
