Timm =?utf-8?q?Bäder?= <[email protected]>, Timm =?utf-8?q?Bäder?= <[email protected]>, 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 1ddb95a2bf522f26ca1c2735ca0f9b5e66b58f15 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/5] [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 e8572afe8f69c..ade92b52ab8fc 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -4850,7 +4850,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; } @@ -5506,11 +5513,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)) @@ -5527,13 +5548,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; @@ -7094,6 +7115,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 d1836b6b739b2..d46f8f7ba8017 100644 --- a/clang/lib/AST/ByteCode/Interp.h +++ b/clang/lib/AST/ByteCode/Interp.h @@ -2184,6 +2184,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 de2b3421f404b..eff3920d5dbd5 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); @@ -876,7 +876,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 6ac16c2b831d2..9b93bce359a34 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -2474,6 +2474,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(); @@ -5445,7 +5447,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. @@ -5461,22 +5464,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; } @@ -5484,8 +5509,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 f0064362abbc6..528e61f929c4f 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 >From 56a2f208fe809e2c8a9aa0756260c8a9eb710d43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= <[email protected]> Date: Fri, 19 Jun 2026 10:35:13 +0200 Subject: [PATCH 2/5] Toplevel confusion --- clang/lib/AST/ExprConstant.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 9b93bce359a34..ceef1aba900c2 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -5486,7 +5486,7 @@ static bool handleDefaultInitValue(QualType T, APValue &Result, if (I->isUnnamedBitField()) continue; Success &= handleDefaultInitValue( - I->getType(), Result.getStructField(I->getFieldIndex()), false); + I->getType(), Result.getStructField(I->getFieldIndex())); } if (Toplevel) { @@ -5510,8 +5510,7 @@ static bool handleDefaultInitValue(QualType T, APValue &Result, Result = APValue(APValue::UninitArray(), 0, AT->getZExtSize()); if (Result.hasArrayFiller()) Success &= handleDefaultInitValue(AT->getElementType(), - Result.getArrayFiller(), false); - + Result.getArrayFiller()); return Success; } >From 10c03165b05e164f5c6aed835cd733b6bdc330bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= <[email protected]> Date: Fri, 19 Jun 2026 10:48:02 +0200 Subject: [PATCH 3/5] todo --- clang/lib/AST/ByteCode/EvaluationResult.cpp | 19 ++++++++++++++++++- clang/test/AST/ByteCode/virtual-bases.cpp | 12 ++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/clang/lib/AST/ByteCode/EvaluationResult.cpp b/clang/lib/AST/ByteCode/EvaluationResult.cpp index 0873b870fc8b2..1b987668e9c15 100644 --- a/clang/lib/AST/ByteCode/EvaluationResult.cpp +++ b/clang/lib/AST/ByteCode/EvaluationResult.cpp @@ -113,7 +113,24 @@ static bool CheckFieldsInitialized(InterpState &S, SourceLocation Loc, Result &= CheckFieldsInitialized(S, Loc, P, B.R); } - // TODO: Virtual bases + for (auto [I, B] : llvm::enumerate(R->virtual_bases())) { + PtrView P = BasePtr.atField(B.Offset); + if (!P.isInitialized()) { + const Descriptor *Desc = BasePtr.getDeclDesc(); + if (const auto *CD = dyn_cast_if_present<CXXRecordDecl>(R->getDecl())) { + const auto &BS = *std::next(CD->bases_begin(), I); + SourceLocation TypeBeginLoc = BS.getBaseTypeLoc(); + S.FFDiag(TypeBeginLoc, diag::note_constexpr_uninitialized_base) + << B.Desc->getType() << SourceRange(TypeBeginLoc, BS.getEndLoc()); + } else { + S.FFDiag(Desc->getLocation(), diag::note_constexpr_uninitialized_base) + << B.Desc->getType(); + } + return false; + } + Result &= CheckFieldsInitialized(S, Loc, P, B.R); + } + return Result; } diff --git a/clang/test/AST/ByteCode/virtual-bases.cpp b/clang/test/AST/ByteCode/virtual-bases.cpp index 41874e026b2ea..9caeb323e1c68 100644 --- a/clang/test/AST/ByteCode/virtual-bases.cpp +++ b/clang/test/AST/ByteCode/virtual-bases.cpp @@ -131,3 +131,15 @@ namespace DynamicCast { constexpr const B *b= (B*)&e; static_assert(dynamic_cast<const C*>(b) != nullptr); } + +namespace UninitializedFields { + + struct A { + int a; // expected-note {{declared here}} + constexpr A() {} + }; + struct B : public A { + }; + constexpr B b{}; // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{ subobject 'a' is not initialized}}} +} >From 06edb8bbdbfe84ae01b33e40fa08d7c55c7b2002 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= <[email protected]> Date: Fri, 19 Jun 2026 11:08:25 +0200 Subject: [PATCH 4/5] Properly check virtual bases for uninitialized fields --- clang/lib/AST/ByteCode/EvaluationResult.cpp | 39 ++++++++++++--------- clang/test/AST/ByteCode/virtual-bases.cpp | 5 ++- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/clang/lib/AST/ByteCode/EvaluationResult.cpp b/clang/lib/AST/ByteCode/EvaluationResult.cpp index 1b987668e9c15..fda77d21d0c17 100644 --- a/clang/lib/AST/ByteCode/EvaluationResult.cpp +++ b/clang/lib/AST/ByteCode/EvaluationResult.cpp @@ -27,7 +27,8 @@ static void DiagnoseUninitializedSubobject(InterpState &S, SourceLocation Loc, } static bool CheckFieldsInitialized(InterpState &S, SourceLocation Loc, - PtrView BasePtr, const Record *R); + PtrView BasePtr, const Record *R, + bool Toplevel = true); static bool CheckArrayInitialized(InterpState &S, SourceLocation Loc, PtrView BasePtr) { @@ -66,7 +67,8 @@ static bool CheckArrayInitialized(InterpState &S, SourceLocation Loc, } static bool CheckFieldsInitialized(InterpState &S, SourceLocation Loc, - PtrView BasePtr, const Record *R) { + PtrView BasePtr, const Record *R, + bool Toplevel) { assert(R); bool Result = true; // Check all fields of this record are initialized. @@ -110,25 +112,28 @@ static bool CheckFieldsInitialized(InterpState &S, SourceLocation Loc, } return false; } - Result &= CheckFieldsInitialized(S, Loc, P, B.R); + Result &= CheckFieldsInitialized(S, Loc, P, B.R, false); } - for (auto [I, B] : llvm::enumerate(R->virtual_bases())) { - PtrView P = BasePtr.atField(B.Offset); - if (!P.isInitialized()) { - const Descriptor *Desc = BasePtr.getDeclDesc(); - if (const auto *CD = dyn_cast_if_present<CXXRecordDecl>(R->getDecl())) { - const auto &BS = *std::next(CD->bases_begin(), I); - SourceLocation TypeBeginLoc = BS.getBaseTypeLoc(); - S.FFDiag(TypeBeginLoc, diag::note_constexpr_uninitialized_base) - << B.Desc->getType() << SourceRange(TypeBeginLoc, BS.getEndLoc()); - } else { - S.FFDiag(Desc->getLocation(), diag::note_constexpr_uninitialized_base) - << B.Desc->getType(); + if (Toplevel) { + for (auto [I, B] : llvm::enumerate(R->virtual_bases())) { + PtrView P = BasePtr.atField(B.Offset); + if (!P.isInitialized()) { + const Descriptor *Desc = BasePtr.getDeclDesc(); + if (const auto *CD = dyn_cast_if_present<CXXRecordDecl>(R->getDecl())) { + const auto &BS = *std::next(CD->bases_begin(), I); + SourceLocation TypeBeginLoc = BS.getBaseTypeLoc(); + S.FFDiag(TypeBeginLoc, diag::note_constexpr_uninitialized_base) + << B.Desc->getType() << SourceRange(TypeBeginLoc, BS.getEndLoc()); + } else { + S.FFDiag(Desc->getLocation(), diag::note_constexpr_uninitialized_base) + << B.Desc->getType(); + } + return false; } - return false; + + Result &= CheckFieldsInitialized(S, Loc, P, B.R, false); } - Result &= CheckFieldsInitialized(S, Loc, P, B.R); } return Result; diff --git a/clang/test/AST/ByteCode/virtual-bases.cpp b/clang/test/AST/ByteCode/virtual-bases.cpp index 9caeb323e1c68..18d9c003cff42 100644 --- a/clang/test/AST/ByteCode/virtual-bases.cpp +++ b/clang/test/AST/ByteCode/virtual-bases.cpp @@ -1,5 +1,4 @@ -// RUN: %clang_cc1 -std=c++26 -fexperimental-new-constant-interpreter %s - +// RUN: %clang_cc1 -std=c++26 -fexperimental-new-constant-interpreter -verify %s namespace PaperSample { struct Superbase { @@ -141,5 +140,5 @@ namespace UninitializedFields { struct B : public A { }; constexpr B b{}; // expected-error {{must be initialized by a constant expression}} \ - // expected-note {{ subobject 'a' is not initialized}}} + // expected-note {{subobject 'a' is not initialized}}} } >From 608aefba0f666a09711e0462a1b8ebc48686abcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= <[email protected]> Date: Fri, 19 Jun 2026 12:08:02 +0200 Subject: [PATCH 5/5] Check base class state in dtors --- clang/lib/AST/ByteCode/Compiler.cpp | 50 ++++++---- clang/lib/AST/ByteCode/Compiler.h | 6 +- clang/lib/AST/ByteCode/EvaluationResult.cpp | 15 ++- clang/lib/AST/ByteCode/Interp.h | 5 + clang/lib/AST/ByteCode/Opcodes.td | 2 + clang/lib/AST/ExprConstant.cpp | 29 +++++- clang/test/AST/ByteCode/virtual-bases.cpp | 103 ++++++++++++++++++++ 7 files changed, 185 insertions(+), 25 deletions(-) diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp index ade92b52ab8fc..f2994e939c24c 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -1945,7 +1945,6 @@ bool Compiler<Emitter>::VisitImplicitValueInitExpr( return false; assert(Initializing); - return this->visitZeroRecordInitializer(R, E); } if (QT->isIncompleteArrayType()) @@ -4784,7 +4783,8 @@ bool Compiler<Emitter>::visitZeroInitializer(PrimType T, QualType QT, template <class Emitter> bool Compiler<Emitter>::visitZeroRecordInitializer(const Record *R, - const Expr *E) { + const Expr *E, + bool Toplevel) { assert(E); assert(R); // Fields @@ -4844,19 +4844,21 @@ bool Compiler<Emitter>::visitZeroRecordInitializer(const Record *R, for (const Record::Base &B : R->bases()) { if (!this->emitGetPtrBase(B.Offset, E)) return false; - if (!this->visitZeroRecordInitializer(B.R, E)) + if (!this->visitZeroRecordInitializer(B.R, E, false)) return false; if (!this->emitFinishInitPop(E)) return false; } - 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; + if (Toplevel) { + 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, false)) + return false; + if (!this->emitFinishInitPop(E)) + return false; + } } return true; @@ -7115,20 +7117,31 @@ 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{})) + if (R->getNumVirtualBases() > 0) { + LabelTy EndLabel = this->getLabel(); + // If this is a base class, skip the virtual bases. + if (!this->emitIsBaseClass({})) return false; - if (!this->emitRecordDestructionPop(Base.R, {})) + if (!this->jumpTrue(EndLabel, {})) 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; + } + + this->fallthrough(EndLabel); + this->emitLabel(EndLabel); } if (!this->emitMarkDestroyed(Dtor)) return false; - // FIXME: Virtual bases. return this->emitPopPtr(Dtor) && this->emitRetVoid(Dtor); } @@ -8124,7 +8137,8 @@ bool Compiler<Emitter>::emitComplexComparison(const Expr *LHS, const Expr *RHS, /// Emit destruction of record types (or arrays of record types). template <class Emitter> bool Compiler<Emitter>::emitRecordDestructionPop(const Record *R, - SourceInfo Loc) { + SourceInfo Loc, + bool Toplevel) { assert(R); assert(!R->hasTrivialDtor()); const CXXDestructorDecl *Dtor = R->getDestructor(); diff --git a/clang/lib/AST/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h index e0008e4eeebc4..de95a274945aa 100644 --- a/clang/lib/AST/ByteCode/Compiler.h +++ b/clang/lib/AST/ByteCode/Compiler.h @@ -361,7 +361,8 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>, /// Emits a zero initializer. bool visitZeroInitializer(PrimType T, QualType QT, const Expr *E); - bool visitZeroRecordInitializer(const Record *R, const Expr *E); + bool visitZeroRecordInitializer(const Record *R, const Expr *E, + bool Toplevel = true); bool visitZeroArrayInitializer(QualType T, const Expr *E); bool visitAssignment(const Expr *LHS, const Expr *RHS, const Expr *E); @@ -418,7 +419,8 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>, bool emitComplexBoolCast(const Expr *E); bool emitComplexComparison(const Expr *LHS, const Expr *RHS, const BinaryOperator *E); - bool emitRecordDestructionPop(const Record *R, SourceInfo Loc); + bool emitRecordDestructionPop(const Record *R, SourceInfo Loc, + bool Toplevel = true); bool emitDestructionPop(const Descriptor *Desc, SourceInfo Loc); bool emitDummyPtr(const DeclTy &D, const Expr *E, bool CU = false); bool emitFloat(const APFloat &F, const Expr *E); diff --git a/clang/lib/AST/ByteCode/EvaluationResult.cpp b/clang/lib/AST/ByteCode/EvaluationResult.cpp index fda77d21d0c17..d95c1e19889e8 100644 --- a/clang/lib/AST/ByteCode/EvaluationResult.cpp +++ b/clang/lib/AST/ByteCode/EvaluationResult.cpp @@ -178,7 +178,8 @@ static bool isOrHasPtr(const Descriptor *D) { return false; } -static void collectBlocks(PtrView Ptr, llvm::SetVector<const Block *> &Blocks) { +static void collectBlocks(PtrView Ptr, llvm::SetVector<const Block *> &Blocks, + bool Toplevel = true) { auto isUsefulPtr = [](const Pointer &P) -> bool { return P.isLive() && P.isBlockPointer() && !P.isZero() && !P.isDummy() && P.isDereferencable() && !P.isUnknownSizeArray() && !P.isOnePastEnd(); @@ -202,7 +203,7 @@ static void collectBlocks(PtrView Ptr, llvm::SetVector<const Block *> &Blocks) { if (!B.R->hasPtrField()) continue; PtrView BasePtr = Ptr.atField(B.Offset); - collectBlocks(BasePtr, Blocks); + collectBlocks(BasePtr, Blocks, false); } for (const Record::Field &F : R->fields()) { @@ -211,6 +212,16 @@ static void collectBlocks(PtrView Ptr, llvm::SetVector<const Block *> &Blocks) { PtrView FieldPtr = Ptr.atField(F.Offset); collectBlocks(FieldPtr, Blocks); } + + if (Toplevel) { + for (const Record::Base &B : R->virtual_bases()) { + if (!B.R->hasPtrField()) + continue; + PtrView BasePtr = Ptr.atField(B.Offset); + collectBlocks(BasePtr, Blocks, false); + } + } + return; } diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h index d46f8f7ba8017..b5e147b51c6ac 100644 --- a/clang/lib/AST/ByteCode/Interp.h +++ b/clang/lib/AST/ByteCode/Interp.h @@ -4103,6 +4103,11 @@ inline bool CheckDestruction(InterpState &S, CodePtr OpPC) { return CheckDestructor(S, OpPC, Ptr); } +inline bool IsBaseClass(InterpState &S, CodePtr OpPC) { + S.Stk.push<bool>(S.Stk.peek<Pointer>().isBaseClass()); + return true; +} + //===----------------------------------------------------------------------===// // Read opcode arguments //===----------------------------------------------------------------------===// diff --git a/clang/lib/AST/ByteCode/Opcodes.td b/clang/lib/AST/ByteCode/Opcodes.td index 39a164dd52718..ebc9a664b8857 100644 --- a/clang/lib/AST/ByteCode/Opcodes.td +++ b/clang/lib/AST/ByteCode/Opcodes.td @@ -388,6 +388,8 @@ def GetPtrVirtBase : Opcode { let Args = [ArgRecordDecl]; } +def IsBaseClass : SuccessOpcode; + // [] -> [Pointer] def GetPtrThisBase : Opcode { diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index ceef1aba900c2..189f0e3f095fa 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -2183,7 +2183,8 @@ static bool CheckEvaluationResult(CheckEvaluationResultKind CERK, QualType Type, const APValue &Value, ConstantExprKind Kind, const FieldDecl *SubobjectDecl, - CheckedTemporaries &CheckedTemps); + CheckedTemporaries &CheckedTemps, + bool Toplevel = true); /// Check that this reference or pointer core constant expression is a valid /// value for an address or reference constant expression. Return true if we @@ -2428,7 +2429,8 @@ static bool CheckEvaluationResult(CheckEvaluationResultKind CERK, QualType Type, const APValue &Value, ConstantExprKind Kind, const FieldDecl *SubobjectDecl, - CheckedTemporaries &CheckedTemps) { + CheckedTemporaries &CheckedTemps, + bool Toplevel) { if (!Value.hasValue()) { if (SubobjectDecl) { Info.FFDiag(DiagLoc, diag::note_constexpr_uninitialized) @@ -2485,7 +2487,7 @@ static bool CheckEvaluationResult(CheckEvaluationResultKind CERK, } if (!CheckEvaluationResult(CERK, Info, DiagLoc, BS.getType(), BaseValue, Kind, /*SubobjectDecl=*/nullptr, - CheckedTemps)) + CheckedTemps, false)) return false; ++BaseIndex; } @@ -2499,6 +2501,27 @@ static bool CheckEvaluationResult(CheckEvaluationResultKind CERK, I, CheckedTemps)) return false; } + + if (Toplevel) { + if (const CXXRecordDecl *CD = dyn_cast<CXXRecordDecl>(RD)) { + unsigned BaseIndex = 0; + for (const CXXBaseSpecifier &BS : CD->vbases()) { + assert(BS.isVirtual()); + 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, false)) + return false; + ++BaseIndex; + } + } + } } if (Value.isLValue() && diff --git a/clang/test/AST/ByteCode/virtual-bases.cpp b/clang/test/AST/ByteCode/virtual-bases.cpp index 18d9c003cff42..7edc6ccc395a5 100644 --- a/clang/test/AST/ByteCode/virtual-bases.cpp +++ b/clang/test/AST/ByteCode/virtual-bases.cpp @@ -141,4 +141,107 @@ namespace UninitializedFields { }; constexpr B b{}; // expected-error {{must be initialized by a constant expression}} \ // expected-note {{subobject 'a' is not initialized}}} + + + struct X { + int *p; + constexpr X() { + p = new int; // expected-note {{heap allocation performed here}} + } + }; + struct Y: public virtual X { + }; + constexpr Y y; // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{pointer to heap-allocated object is not a constant expression}} +} + + +namespace DtorOrder { + enum { + R_A = 1, + R_B = 2, + R_C = 3, + R_F = 4, + R_G = 5, + }; + + struct A { + int a; int b; + int *results; + int &i; + + constexpr A(int *results, int &i) : results(results), i(i) {} + constexpr ~A() { + *(results + i) = R_A; + ++i; + } + + }; + struct B : public virtual A { + int c; int d; + int *results; + int &i; + + constexpr B(int *results, int &i) : A(results, i), results(results), i(i) {} + constexpr ~B() { + *(results + i) = R_B; + ++i; + } + }; + + + struct G { + int *results; + int &i; + constexpr G(int *results, int &i) : results(results), i(i) {} + + constexpr ~G() { + *(results + i) = R_G; + ++i; + } + }; + + + struct F : virtual G{ + int *results; + int &i; + constexpr F(int *results, int &i) : G(results, i), results(results), i(i) {} + constexpr ~F() { + *(results + i) = R_F; + ++i; + } + }; + + struct C : public virtual A, public virtual B { + int *results; + int &i; + int m = 10; + + F f; + + constexpr C(int *results, int &i) : A(results, i), B(results, i), results(results), i(i), f(results,i) {} + + constexpr ~C() { + *(results + i) = R_C; + ++i; + } + }; + + constexpr int foo() { + int results[] = {0, 0, 0, 0, 0, 0, 0}; + + int i = 0; + { + C c = C(results, i); + } + return i == 5 && + results[0] == R_C && + results[1] == R_F && + results[2] == R_G && + results[3] == R_B && + results[4] == R_A; + } + static_assert(foo() == 1); + + } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
