Index: clang/docs/ReleaseNotes.rst =================================================================== --- clang/docs/ReleaseNotes.rst +++ clang/docs/ReleaseNotes.rst @@ -375,6 +375,8 @@ by making use of the syntactical structure of function calls. This avoids display of syntactically invalid codes in diagnostics. (`#57081: `_) +- Clang contexpr evaluator now displays notes as well as an error when a constructor + of base class is not called in the constructor of its derived class. Bug Fixes in This Version ------------------------- Index: clang/include/clang/Basic/DiagnosticASTKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticASTKinds.td +++ clang/include/clang/Basic/DiagnosticASTKinds.td @@ -70,10 +70,14 @@ "is not a constant expression">; def note_constexpr_uninitialized : Note< "subobject %0 is not initialized">; +def note_constexpr_uninitialized_base : Note< + "constructor of base class %0 is not called">; def note_constexpr_static_local : Note< "control flows through the definition of a %select{static|thread_local}0 variable">; def note_constexpr_subobject_declared_here : Note< "subobject declared here">; +def note_constexpr_base_inherited_here : Note< + "base class inherited here">; def note_constexpr_array_index : Note<"cannot refer to element %0 of " "%select{array of %2 element%plural{1:|:s}2|non-array object}1 " "in a constant expression">; Index: clang/lib/AST/ExprConstant.cpp =================================================================== --- clang/lib/AST/ExprConstant.cpp +++ clang/lib/AST/ExprConstant.cpp @@ -2415,9 +2415,16 @@ if (const CXXRecordDecl *CD = dyn_cast(RD)) { unsigned BaseIndex = 0; for (const CXXBaseSpecifier &BS : CD->bases()) { - if (!CheckEvaluationResult(CERK, Info, DiagLoc, BS.getType(), - Value.getStructBase(BaseIndex), Kind, - /*SubobjectDecl=*/nullptr, CheckedTemps)) + const APValue &BaseValue = Value.getStructBase(BaseIndex); + if (!BaseValue.hasValue()) { + Info.FFDiag(DiagLoc, diag::note_constexpr_uninitialized_base) + << BS.getType(); + Info.Note(BS.getBeginLoc(), diag::note_constexpr_base_inherited_here); + return false; + } + if (!CheckEvaluationResult(CERK, Info, DiagLoc, BS.getType(), BaseValue, + Kind, /*SubobjectDecl=*/nullptr, + CheckedTemps)) return false; ++BaseIndex; } Index: clang/test/SemaCXX/constexpr-subobj-initialization.cpp =================================================================== --- /dev/null +++ clang/test/SemaCXX/constexpr-subobj-initialization.cpp @@ -0,0 +1,34 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +namespace baseclass_uninit { +struct DelBase { + constexpr DelBase() = delete; // expected-note {{'DelBase' has been explicitly marked deleted here}} +}; + +struct Foo : DelBase { // expected-note 2{{base class inherited here}} + constexpr Foo() {}; // expected-error {{call to deleted constructor of 'DelBase'}} +}; +constexpr Foo f; // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{constructor of base class 'DelBase' is not called}} +struct Bar : Foo { + constexpr Bar() {}; +}; +constexpr Bar bar; // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{constructor of base class 'DelBase' is not called}} + +struct Base {}; +struct A : Base { // expected-note {{base class inherited here}} + constexpr A() : value() {} // expected-error {{member initializer 'value' does not name a non-static data member or base class}} +}; + +constexpr A a; // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{constructor of base class 'Base' is not called}} + +struct B : Base { // expected-note {{base class inherited here}} + constexpr B() : {} // expected-error {{expected class member or base class name}} +}; + +constexpr B b; // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{constructor of base class 'Base' is not called}} +} // namespace baseclass_uninit +