https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/204289
>From 4ac7bbd40cc93ff22359b6cb36fc4a6b8fd12797 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 | 28 ++++- clang/include/clang/AST/PropertiesBase.td | 11 +- clang/lib/AST/APValue.cpp | 30 ++++- clang/lib/AST/ASTImporter.cpp | 6 +- clang/lib/AST/ByteCode/Compiler.cpp | 45 ++++++-- clang/lib/AST/ByteCode/Interp.cpp | 3 + clang/lib/AST/ByteCode/Interp.h | 8 ++ clang/lib/AST/ByteCode/Opcodes.td | 6 + clang/lib/AST/ByteCode/Pointer.cpp | 4 +- clang/lib/AST/DeclCXX.cpp | 5 +- clang/lib/AST/ExprConstant.cpp | 45 ++++++-- clang/lib/AST/TextNodeDumper.cpp | 6 + clang/lib/Sema/SemaDeclCXX.cpp | 9 +- clang/lib/Sema/SemaType.cpp | 2 +- clang/test/AST/ByteCode/virtual-bases.cpp | 133 ++++++++++++++++++++++ clang/test/CXX/drs/cwg16xx.cpp | 16 ++- clang/test/CXX/drs/cwg6xx.cpp | 12 ++ 17 files changed, 327 insertions(+), 42 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..c509addfe5d48 100644 --- a/clang/include/clang/AST/APValue.h +++ b/clang/include/clang/AST/APValue.h @@ -297,7 +297,8 @@ class APValue { APValue *Elts; unsigned NumBases; unsigned NumFields; - StructData(unsigned NumBases, unsigned NumFields); + unsigned NumVirtualBases; + StructData(unsigned NumBases, unsigned NumFields, unsigned NumVirtualBases); StructData(const StructData &) = delete; StructData &operator=(const StructData &) = delete; ~StructData(); @@ -418,10 +419,13 @@ class APValue { /// \param UninitStruct Marker. Pass an empty UninitStruct. /// \param NumBases Number of bases. /// \param NumMembers Number of members. - APValue(UninitStruct, unsigned NumBases, unsigned NumMembers) + /// \param NumVirtualBases Number of virtual bases. + APValue(UninitStruct, unsigned NumBases, unsigned NumMembers, + unsigned NumVirtualBases = 0) : Kind(None), AllowConstexprUnknown(false) { - MakeStruct(NumBases, NumMembers); + 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 +663,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 +677,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"); @@ -788,11 +805,12 @@ class APValue { } void MakeLValue(); void MakeArray(unsigned InitElts, unsigned Size); - void MakeStruct(unsigned B, unsigned M) { + void MakeStruct(unsigned B, unsigned M, unsigned V) { assert(isAbsent() && "Bad state change"); - new ((void *)(char *)&Data) StructData(B, M); + 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/include/clang/AST/PropertiesBase.td b/clang/include/clang/AST/PropertiesBase.td index fd3cce10be303..25ef4c26a9aa1 100644 --- a/clang/include/clang/AST/PropertiesBase.td +++ b/clang/include/clang/AST/PropertiesBase.td @@ -416,6 +416,10 @@ let Class = PropertyTypeCase<APValue, "Struct"> in { unsigned numFields = node.getStructNumFields(); for (unsigned i = 0; i < numFields; ++i) structFields.push_back(node.getStructField(i)); + SmallVector<APValue, 4> structVirtualBases; + unsigned numVirtualBases = node.getStructNumVirtualBases(); + for (unsigned i = 0; i < numVirtualBases; ++i) + structVirtualBases.push_back(node.getStructVirtualBase(i)); }]>; def : Property<"bases", Array<APValue>> { let Read = [{ structBases }]; @@ -423,13 +427,18 @@ let Class = PropertyTypeCase<APValue, "Struct"> in { def : Property<"fields", Array<APValue>> { let Read = [{ structFields }]; } + def : Property<"vbases", Array<APValue>> { + let Read = [{ structVirtualBases }]; + } def : Creator<[{ APValue result; - result.MakeStruct(bases.size(), fields.size()); + result.MakeStruct(bases.size(), fields.size(), vbases.size()); for (unsigned i = 0; i < bases.size(); ++i) result.getStructBase(i) = bases[i]; for (unsigned i = 0; i < fields.size(); ++i) result.getStructField(i) = fields[i]; + for (unsigned i = 0; i < vbases.size(); ++i) + result.getStructVirtualBase(i) = vbases[i]; return result; }]>; } diff --git a/clang/lib/AST/APValue.cpp b/clang/lib/AST/APValue.cpp index fd51584f564bb..727e5f8c00a10 100644 --- a/clang/lib/AST/APValue.cpp +++ b/clang/lib/AST/APValue.cpp @@ -282,9 +282,12 @@ APValue::Arr::Arr(unsigned NumElts, unsigned Size) : NumElts(NumElts), ArrSize(Size) {} 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; } @@ -349,11 +352,14 @@ APValue::APValue(const APValue &RHS) getArrayFiller() = RHS.getArrayFiller(); break; case Struct: - MakeStruct(RHS.getStructNumBases(), RHS.getStructNumFields()); + MakeStruct(RHS.getStructNumBases(), RHS.getStructNumFields(), + RHS.getStructNumVirtualBases()); for (unsigned I = 0, N = RHS.getStructNumBases(); I != N; ++I) getStructBase(I) = RHS.getStructBase(I); for (unsigned I = 0, N = RHS.getStructNumFields(); I != N; ++I) getStructField(I) = RHS.getStructField(I); + for (unsigned I = 0, N = RHS.getStructNumVirtualBases(); I != N; ++I) + getStructVirtualBase(I) = RHS.getStructVirtualBase(I); break; case Union: MakeUnion(); @@ -503,6 +509,8 @@ void APValue::Profile(llvm::FoldingSetNodeID &ID) const { getStructBase(I).Profile(ID); for (unsigned I = 0, N = getStructNumFields(); I != N; ++I) getStructField(I).Profile(ID); + for (unsigned I = 0, N = getStructNumVirtualBases(); I != N; ++I) + getStructVirtualBase(I).Profile(ID); return; case Union: @@ -942,6 +950,17 @@ void APValue::printPretty(raw_ostream &Out, const PrintingPolicy &Policy, printPretty(Out, Policy, FI->getType(), Ctx); First = false; } + if (unsigned N = getStructNumVirtualBases()) { + const CXXRecordDecl *CD = cast<CXXRecordDecl>(RD); + CXXRecordDecl::base_class_const_iterator BI = CD->vbases_begin(); + for (unsigned I = 0; I != N; ++I, ++BI) { + assert(BI != CD->vbases_end()); + if (!First) + Out << ", "; + getStructVirtualBase(I).printPretty(Out, Policy, BI->getType(), Ctx); + First = false; + } + } Out << '}'; return; } @@ -1172,6 +1191,9 @@ LinkageInfo LinkageComputer::getLVForValue(const APValue &V, for (unsigned I = 0, N = V.getStructNumFields(); I != N; ++I) if (Merge(V.getStructField(I))) break; + for (unsigned I = 0, N = V.getStructNumVirtualBases(); I != N; ++I) + if (Merge(V.getStructVirtualBase(I))) + break; break; } diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 567d2d07298a3..9d38b02218bc2 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -10694,11 +10694,13 @@ ASTNodeImporter::ImportAPValue(const APValue &FromValue) { break; case APValue::Struct: Result.MakeStruct(FromValue.getStructNumBases(), - FromValue.getStructNumFields()); + FromValue.getStructNumFields(), + FromValue.getStructNumVirtualBases()); ImportLoop( ((const APValue::StructData *)(const char *)&FromValue.Data)->Elts, ((const APValue::StructData *)(const char *)&Result.Data)->Elts, - FromValue.getStructNumBases() + FromValue.getStructNumFields()); + FromValue.getStructNumBases() + FromValue.getStructNumFields() + + FromValue.getStructNumVirtualBases()); break; case APValue::Union: { Result.MakeUnion(); diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp index 638e6ecafb295..1b7e8ea6948a3 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -4861,7 +4861,14 @@ bool Compiler<Emitter>::visitZeroRecordInitializer(const Record *R, return false; } - // FIXME: Virtual bases. + for (const Record::Base &B : R->virtual_bases()) { + if (!this->emitGetPtrVirtBase(cast<CXXRecordDecl>(B.R->getDecl()), E)) + return false; + if (!this->visitZeroRecordInitializer(B.R, E)) + return false; + if (!this->emitFinishInitPop(E)) + return false; + } return true; } @@ -5517,11 +5524,25 @@ bool Compiler<Emitter>::visitAPValueInitializer(const APValue &Val, if (Val.isStruct()) { const Record *R = this->getRecord(T); assert(R); + + // Bases. + for (unsigned I = 0, N = Val.getStructNumBases(); I != N; ++I) { + const APValue &B = Val.getStructBase(I); + const Record::Base *RB = R->getBase(I); + QualType BaseType = Ctx.getASTContext().getCanonicalTagType(RB->Decl); + + if (!this->emitGetPtrBase(RB->Offset, E)) + return false; + if (!this->visitAPValueInitializer(B, E, BaseType)) + return false; + if (!this->emitFinishInitPop(E)) + return false; + } + for (unsigned I = 0, N = Val.getStructNumFields(); I != N; ++I) { const APValue &F = Val.getStructField(I); const Record::Field *RF = R->getField(I); QualType FieldType = RF->Decl->getType(); - // Fields. if (OptPrimType PT = classify(FieldType)) { if (!this->visitAPValue(F, *PT, E)) @@ -5538,13 +5559,13 @@ bool Compiler<Emitter>::visitAPValueInitializer(const APValue &Val, } } - // Bases. - for (unsigned I = 0, N = Val.getStructNumBases(); I != N; ++I) { - const APValue &B = Val.getStructBase(I); - const Record::Base *RB = R->getBase(I); + // Virtual Bases. + for (unsigned I = 0, N = Val.getStructNumVirtualBases(); I != N; ++I) { + const APValue &B = Val.getStructVirtualBase(I); + const Record::Base *RB = R->getVirtualBase(I); QualType BaseType = Ctx.getASTContext().getCanonicalTagType(RB->Decl); - if (!this->emitGetPtrBase(RB->Offset, E)) + if (!this->emitGetPtrVirtBase(cast<CXXRecordDecl>(RB->R->getDecl()), E)) return false; if (!this->visitAPValueInitializer(B, E, BaseType)) return false; @@ -7098,6 +7119,16 @@ bool Compiler<Emitter>::compileDestructor(const CXXDestructorDecl *Dtor) { return false; } + for (const Record::Base &Base : llvm::reverse(R->virtual_bases())) { + if (Base.R->hasTrivialDtor()) + continue; + if (!this->emitGetPtrVirtBase(cast<CXXRecordDecl>(Base.R->getDecl()), + SourceInfo{})) + return false; + if (!this->emitRecordDestructionPop(Base.R, {})) + return false; + } + if (!this->emitMarkDestroyed(Dtor)) return false; 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/Interp.h b/clang/lib/AST/ByteCode/Interp.h index 0f9e585e19769..4beda945c1a14 100644 --- a/clang/lib/AST/ByteCode/Interp.h +++ b/clang/lib/AST/ByteCode/Interp.h @@ -2182,6 +2182,14 @@ inline bool GetPtrVirtBasePop(InterpState &S, CodePtr OpPC, return VirtBaseHelper(S, OpPC, D, Ptr); } +inline bool GetPtrVirtBase(InterpState &S, CodePtr OpPC, const RecordDecl *D) { + assert(D); + const Pointer &Ptr = S.Stk.peek<Pointer>(); + if (!CheckNull(S, OpPC, Ptr, CSK_Base)) + return false; + return VirtBaseHelper(S, OpPC, D, Ptr); +} + inline bool GetPtrThisVirtBase(InterpState &S, CodePtr OpPC, const RecordDecl *D) { assert(D); diff --git a/clang/lib/AST/ByteCode/Opcodes.td b/clang/lib/AST/ByteCode/Opcodes.td index e350d7b2e547d..39a164dd52718 100644 --- a/clang/lib/AST/ByteCode/Opcodes.td +++ b/clang/lib/AST/ByteCode/Opcodes.td @@ -383,6 +383,12 @@ def GetPtrVirtBasePop : Opcode { // RecordDecl of base class. let Args = [ArgRecordDecl]; } +def GetPtrVirtBase : Opcode { + // RecordDecl of base class. + let Args = [ArgRecordDecl]; +} + + // [] -> [Pointer] def GetPtrThisBase : Opcode { // Offset of field, which is a base. 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..7d20d8a2dbeb3 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(); @@ -5443,7 +5445,8 @@ static bool HandleBaseToDerivedCast(EvalInfo &Info, const CastExpr *E, /// Get the value to use for a default-initialized object of type T. /// Return false if it encounters something invalid. -static bool handleDefaultInitValue(QualType T, APValue &Result) { +static bool handleDefaultInitValue(QualType T, APValue &Result, + bool Toplevel = true) { bool Success = true; // If there is already a value present don't overwrite it. @@ -5459,22 +5462,44 @@ static bool handleDefaultInitValue(QualType T, APValue &Result) { Result = APValue((const FieldDecl *)nullptr); return true; } - Result = - APValue(APValue::UninitStruct(), RD->getNumBases(), RD->getNumFields()); + + // bases() includes directly specified virtual bases as well. + unsigned NonVirtualBases = + llvm::count_if(RD->bases(), [](auto &B) { return !B.isVirtual(); }); + Result = APValue(APValue::UninitStruct(), NonVirtualBases, + RD->getNumFields(), Toplevel ? RD->getNumVBases() : 0); unsigned Index = 0; - for (CXXRecordDecl::base_class_const_iterator I = RD->bases_begin(), + for (CXXRecordDecl::base_class_const_iterator B = RD->bases_begin(), End = RD->bases_end(); - I != End; ++I, ++Index) - Success &= - handleDefaultInitValue(I->getType(), Result.getStructBase(Index)); + B != End; ++B) { + if (B->isVirtual()) + continue; + Success &= handleDefaultInitValue(B->getType(), + Result.getStructBase(Index), false); + ++Index; + } for (const auto *I : RD->fields()) { if (I->isUnnamedBitField()) continue; Success &= handleDefaultInitValue( - I->getType(), Result.getStructField(I->getFieldIndex())); + I->getType(), Result.getStructField(I->getFieldIndex()), false); } + + if (Toplevel) { + Index = 0; + + for (const auto &B : RD->vbases()) { + Success &= handleDefaultInitValue( + B.getType(), Result.getStructVirtualBase(Index), false); + ++Index; + } + } else { + // Virtual bases should only exist at the top level of an APValue. + assert(Result.getStructNumVirtualBases() == 0); + } + return Success; } @@ -5482,8 +5507,8 @@ static bool handleDefaultInitValue(QualType T, APValue &Result) { dyn_cast_or_null<ConstantArrayType>(T->getAsArrayTypeUnsafe())) { Result = APValue(APValue::UninitArray(), 0, AT->getZExtSize()); if (Result.hasArrayFiller()) - Success &= - handleDefaultInitValue(AT->getElementType(), Result.getArrayFiller()); + Success &= handleDefaultInitValue(AT->getElementType(), + Result.getArrayFiller(), false); return Success; } diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp index 2b1c0cac25b6d..8d758609727ad 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -811,6 +811,12 @@ void TextNodeDumper::Visit(const APValue &Value, QualType Ty) { }, Value.getStructNumFields(), "field", "fields"); + dumpAPValueChildren( + Value, Ty, + [](const APValue &Value, unsigned Index) -> const APValue & { + return Value.getStructVirtualBase(Index); + }, + Value.getStructNumVirtualBases(), "vbase", "vbases"); return; } case APValue::Matrix: { diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 418ff01f3d98a..a699b7a465e4c 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: @@ -2473,8 +2473,6 @@ static bool CheckConstexprFunctionBody(Sema &SemaRef, const FunctionDecl *Dcl, } } else if (!Constructor->isDependentContext() && !Constructor->isDelegatingConstructor()) { - assert(RD->getNumVBases() == 0 && "constexpr ctor with virtual bases"); - // Skip detailed checking if we have enough initializers, and we would // allow at most one initializer per member. bool AnyAnonStructUnionMembers = false; @@ -7702,12 +7700,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 +8065,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/virtual-bases.cpp b/clang/test/AST/ByteCode/virtual-bases.cpp new file mode 100644 index 0000000000000..41874e026b2ea --- /dev/null +++ b/clang/test/AST/ByteCode/virtual-bases.cpp @@ -0,0 +1,133 @@ +// RUN: %clang_cc1 -std=c++26 -fexperimental-new-constant-interpreter %s + + +namespace PaperSample { + struct Superbase { + int a = 10; + }; + + struct Common: Superbase { + unsigned counter = 1337; + }; + + 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 = 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); +} + +namespace ZeroInit1 { + struct A { + int a; + }; + + struct B : public virtual A { + int b; + }; + + constexpr B b{}; + static_assert(b.b == 0); + static_assert(b.a == 0); + static_assert((void*)(A*)&b == (void*)(A*)&b); +} + +namespace Destruction { + struct A { + int &a; + constexpr A(int &a) :a(a) {} + constexpr ~A() { ++a; } + }; + + struct B : public virtual A { + constexpr B(int &a) : A(a) {} + }; + + constexpr int foo() { + int m = 0; + { + B b(m); + } + return m; + } + static_assert(foo() == 1); +} + + +namespace VirtualBaseWithVirtualFunctions { + struct VBase { + int x = 5; + constexpr virtual int compute() const { return x * 2; } + constexpr virtual ~VBase() = default; + }; + + struct Derived : virtual VBase { + int y = 3; + constexpr int compute() const override { return x + y; } + }; + + constexpr bool test_virtual_function() { + Derived d; + VBase *ptr = &d; + return ptr->compute() == 8; + } + + static_assert(test_virtual_function()); +} + +namespace DynamicCast { + struct A { + virtual constexpr int f() const {return 10;} + }; + struct B { + virtual constexpr int f() const {return 20;} + }; + struct C : virtual A, virtual B { + constexpr int f() const override { return 30; } + }; + + struct D: C {}; + struct E : D{ + constexpr ~E() {} + }; + + constexpr E e{}; + static_assert(e.f() == 30); + + static_assert((void*)(A*)&e == (void*)(A*)&e); + static_assert((void*)(A*)&e != (void*)(B*)&e); + + static_assert(dynamic_cast<const B*>(&e) != nullptr); + static_assert(dynamic_cast<const A*>(&e) != nullptr); + + constexpr const B *b= (B*)&e; + static_assert(dynamic_cast<const C*>(b) != nullptr); +} 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
