diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index d755626f795d5..3d9774d5c7ff2 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -472,6 +472,8 @@ Improvements to Clang's diagnostics - Clang now emits ``-Wconstant-logical-operand`` warning even when constant logical operand is on left side. (`#37919 `_) +- Clang contexpr evaluator now displays notes as well as an error when a constructor + of a base class is not called in the constructor of its derived class. Bug Fixes in This Version ------------------------- diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td index 566cdc3406058..0794ed7ba6837 100644 --- a/clang/include/clang/Basic/DiagnosticASTKinds.td +++ b/clang/include/clang/Basic/DiagnosticASTKinds.td @@ -70,6 +70,8 @@ def note_consteval_address_accessible : Note< "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< diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 2f2f4eef852fd..f1bad0c7f7f22 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -2418,9 +2418,16 @@ static bool CheckEvaluationResult(CheckEvaluationResultKind CERK, 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()) { + SourceLocation TypeBeginLoc = BS.getBaseTypeLoc(); + Info.FFDiag(TypeBeginLoc, diag::note_constexpr_uninitialized_base) + << BS.getType() << SourceRange(TypeBeginLoc, BS.getEndLoc()); + return false; + } + if (!CheckEvaluationResult(CERK, Info, DiagLoc, BS.getType(), BaseValue, + Kind, /*SubobjectDecl=*/nullptr, + CheckedTemps)) return false; ++BaseIndex; } diff --git a/clang/test/Misc/constexpr-subobj-init-source-ranges.cpp b/clang/test/Misc/constexpr-subobj-init-source-ranges.cpp new file mode 100644 index 0000000000000..342da2d886668 --- /dev/null +++ b/clang/test/Misc/constexpr-subobj-init-source-ranges.cpp @@ -0,0 +1,11 @@ +// RUN: not %clang_cc1 -fsyntax-only -fdiagnostics-print-source-range-info %s 2>&1 | FileCheck %s --strict-whitespace + +struct DelBase { + constexpr DelBase() = delete; +}; + +// CHECK: :{[[@LINE+1]]:21-[[@LINE+1]]:28} +struct Foo : public DelBase { + constexpr Foo() {}; +}; +constexpr Foo f; diff --git a/clang/test/SemaCXX/constexpr-subobj-initialization.cpp b/clang/test/SemaCXX/constexpr-subobj-initialization.cpp new file mode 100644 index 0000000000000..cd096a9270937 --- /dev/null +++ b/clang/test/SemaCXX/constexpr-subobj-initialization.cpp @@ -0,0 +1,58 @@ +// 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{{constructor of base class 'DelBase' is not called}} + constexpr Foo() {}; // expected-error {{call to deleted constructor of 'DelBase'}} +}; +constexpr Foo f; // expected-error {{must be initialized by a constant expression}} +struct Bar : Foo { + constexpr Bar() {}; +}; +constexpr Bar bar; // expected-error {{must be initialized by a constant expression}} + +struct Base {}; +struct A : Base { // expected-note {{constructor of base class 'Base' is not called}} + 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}} + +struct B : Base { // expected-note {{constructor of base class 'Base' is not called}} + constexpr B() : {} // expected-error {{expected class member or base class name}} +}; + +constexpr B b; // expected-error {{must be initialized by a constant expression}} +} // namespace baseclass_uninit + + +struct Foo { + constexpr Foo(); // expected-note 2{{declared here}} +}; + +constexpr Foo ff; // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{undefined constructor 'Foo' cannot be used in a constant expression}} + +struct Bar : protected Foo { + int i; + constexpr Bar() : i(12) {} // expected-note {{undefined constructor 'Foo' cannot be used in a constant expression}} +}; + +constexpr Bar bb; // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{in call to 'Bar()'}} + +template +struct Baz { + constexpr Baz(); // expected-note {{declared here}} +}; + +struct Quux : Baz, private Bar { + int i; + constexpr Quux() : i(12) {} // expected-note {{undefined constructor 'Baz' cannot be used in a constant expression}} +}; + +constexpr Quux qx; // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{in call to 'Quux()'}}