[clang] [clang][bytecode] Support virtual bases in C++26 (PR #204289)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Jun 17 23:04:35 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-modules
Author: Timm Baeder (tbaederr)
<details>
<summary>Changes</summary>
---
Patch is 27.14 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/204289.diff
18 Files Affected:
- (modified) clang/include/clang/AST/APValue.h (+23-5)
- (modified) clang/include/clang/AST/PropertiesBase.td (+10-1)
- (modified) clang/lib/AST/APValue.cpp (+26-4)
- (modified) clang/lib/AST/ASTImporter.cpp (+4-2)
- (modified) clang/lib/AST/ByteCode/Compiler.cpp (+38-7)
- (modified) clang/lib/AST/ByteCode/Interp.cpp (+3)
- (modified) clang/lib/AST/ByteCode/Interp.h (+8)
- (modified) clang/lib/AST/ByteCode/Opcodes.td (+6)
- (modified) clang/lib/AST/ByteCode/Pointer.cpp (+2-2)
- (modified) clang/lib/AST/DeclCXX.cpp (+3-2)
- (modified) clang/lib/AST/ExprConstant.cpp (+35-10)
- (modified) clang/lib/AST/TextNodeDumper.cpp (+6)
- (modified) clang/lib/Sema/SemaDeclCXX.cpp (+3-6)
- (modified) clang/lib/Sema/SemaType.cpp (+1-1)
- (modified) clang/test/AST/ByteCode/cxx23.cpp (-1)
- (added) clang/test/AST/ByteCode/virtual-bases.cpp (+133)
- (modified) clang/test/CXX/drs/cwg16xx.cpp (+14-2)
- (modified) clang/test/CXX/drs/cwg6xx.cpp (+12)
``````````diff
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..1d4b463ddd10a 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 directlys 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...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/204289
More information about the cfe-commits
mailing list