diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 0b7433b0fcb55..9420a9e687ca7 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -282,9 +282,7 @@ Improvements to Clang's diagnostics Clang ABI >= 15. (`#62353: `_, fallout from the non-POD packing ABI fix in LLVM 15). -- Clang constexpr evaluator now prints subobject's name instead of its type in notes - when a constexpr variable has uninitialized subobjects after its constructor call. - (`#58601 `_) + Bug Fixes in This Version ------------------------- diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td index eb13467317963..c283ee842e730 100644 --- a/clang/include/clang/Basic/DiagnosticASTKinds.td +++ b/clang/include/clang/Basic/DiagnosticASTKinds.td @@ -65,7 +65,7 @@ def note_consteval_address_accessible : Note< "%select{pointer|reference}0 to a consteval declaration " "is not a constant expression">; def note_constexpr_uninitialized : Note< - "subobject %0 is not initialized">; + "%select{|sub}0object of type %1 is not initialized">; 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 84c8623a0b8ae..ce3c5257e7114 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -2119,7 +2119,7 @@ static bool CheckEvaluationResult(CheckEvaluationResultKind CERK, EvalInfo &Info, SourceLocation DiagLoc, QualType Type, const APValue &Value, ConstantExprKind Kind, - const FieldDecl *SubobjectDecl, + SourceLocation SubobjectLoc, CheckedTemporaries &CheckedTemps); /// Check that this reference or pointer core constant expression is a valid @@ -2266,8 +2266,8 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc, APValue *V = MTE->getOrCreateValue(false); assert(V && "evasluation result refers to uninitialised temporary"); if (!CheckEvaluationResult(CheckEvaluationResultKind::ConstantExpression, - Info, MTE->getExprLoc(), TempType, *V, Kind, - /*SubobjectDecl=*/nullptr, CheckedTemps)) + Info, MTE->getExprLoc(), TempType, *V, + Kind, SourceLocation(), CheckedTemps)) return false; } } @@ -2350,13 +2350,13 @@ static bool CheckEvaluationResult(CheckEvaluationResultKind CERK, EvalInfo &Info, SourceLocation DiagLoc, QualType Type, const APValue &Value, ConstantExprKind Kind, - const FieldDecl *SubobjectDecl, + SourceLocation SubobjectLoc, CheckedTemporaries &CheckedTemps) { if (!Value.hasValue()) { - assert(SubobjectDecl && "SubobjectDecl shall be non-null"); - Info.FFDiag(DiagLoc, diag::note_constexpr_uninitialized) << SubobjectDecl; - Info.Note(SubobjectDecl->getLocation(), - diag::note_constexpr_subobject_declared_here); + Info.FFDiag(DiagLoc, diag::note_constexpr_uninitialized) + << true << Type; + if (SubobjectLoc.isValid()) + Info.Note(SubobjectLoc, diag::note_constexpr_subobject_declared_here); return false; } @@ -2373,19 +2373,20 @@ static bool CheckEvaluationResult(CheckEvaluationResultKind CERK, for (unsigned I = 0, N = Value.getArrayInitializedElts(); I != N; ++I) { if (!CheckEvaluationResult(CERK, Info, DiagLoc, EltTy, Value.getArrayInitializedElt(I), Kind, - SubobjectDecl, CheckedTemps)) + SubobjectLoc, CheckedTemps)) return false; } if (!Value.hasArrayFiller()) return true; return CheckEvaluationResult(CERK, Info, DiagLoc, EltTy, - Value.getArrayFiller(), Kind, SubobjectDecl, + Value.getArrayFiller(), Kind, SubobjectLoc, CheckedTemps); } if (Value.isUnion() && Value.getUnionField()) { return CheckEvaluationResult( CERK, Info, DiagLoc, Value.getUnionField()->getType(), - Value.getUnionValue(), Kind, Value.getUnionField(), CheckedTemps); + Value.getUnionValue(), Kind, Value.getUnionField()->getLocation(), + CheckedTemps); } if (Value.isStruct()) { RecordDecl *RD = Type->castAs()->getDecl(); @@ -2394,7 +2395,7 @@ static bool CheckEvaluationResult(CheckEvaluationResultKind CERK, for (const CXXBaseSpecifier &BS : CD->bases()) { if (!CheckEvaluationResult(CERK, Info, DiagLoc, BS.getType(), Value.getStructBase(BaseIndex), Kind, - /*SubobjectDecl=*/nullptr, CheckedTemps)) + BS.getBeginLoc(), CheckedTemps)) return false; ++BaseIndex; } @@ -2404,8 +2405,8 @@ static bool CheckEvaluationResult(CheckEvaluationResultKind CERK, continue; if (!CheckEvaluationResult(CERK, Info, DiagLoc, I->getType(), - Value.getStructField(I->getFieldIndex()), Kind, - I, CheckedTemps)) + Value.getStructField(I->getFieldIndex()), + Kind, I->getLocation(), CheckedTemps)) return false; } } @@ -2439,7 +2440,7 @@ static bool CheckConstantExpression(EvalInfo &Info, SourceLocation DiagLoc, CheckedTemporaries CheckedTemps; return CheckEvaluationResult(CheckEvaluationResultKind::ConstantExpression, Info, DiagLoc, Type, Value, Kind, - /*SubobjectDecl=*/nullptr, CheckedTemps); + SourceLocation(), CheckedTemps); } /// Check that this evaluated value is fully-initialized and can be loaded by @@ -2449,7 +2450,7 @@ static bool CheckFullyInitialized(EvalInfo &Info, SourceLocation DiagLoc, CheckedTemporaries CheckedTemps; return CheckEvaluationResult( CheckEvaluationResultKind::FullyInitialized, Info, DiagLoc, Type, Value, - ConstantExprKind::Normal, /*SubobjectDecl=*/nullptr, CheckedTemps); + ConstantExprKind::Normal, SourceLocation(), CheckedTemps); } /// Enforce C++2a [expr.const]/4.17, which disallows new-expressions unless diff --git a/clang/lib/AST/Interp/Interp.cpp b/clang/lib/AST/Interp/Interp.cpp index 4d331467f8f2e..3af8027ddeb53 100644 --- a/clang/lib/AST/Interp/Interp.cpp +++ b/clang/lib/AST/Interp/Interp.cpp @@ -369,11 +369,11 @@ bool CheckPure(InterpState &S, CodePtr OpPC, const CXXMethodDecl *MD) { } static void DiagnoseUninitializedSubobject(InterpState &S, const SourceInfo &SI, - const FieldDecl *SubObjDecl) { - assert(SubObjDecl && "Subobject declaration does not exist"); - S.FFDiag(SI, diag::note_constexpr_uninitialized) << SubObjDecl; - S.Note(SubObjDecl->getLocation(), - diag::note_constexpr_subobject_declared_here); + QualType SubObjType, + SourceLocation SubObjLoc) { + S.FFDiag(SI, diag::note_constexpr_uninitialized) << true << SubObjType; + if (SubObjLoc.isValid()) + S.Note(SubObjLoc, diag::note_constexpr_subobject_declared_here); } static bool CheckFieldsInitialized(InterpState &S, CodePtr OpPC, @@ -400,8 +400,8 @@ static bool CheckArrayInitialized(InterpState &S, CodePtr OpPC, } else { for (size_t I = 0; I != NumElems; ++I) { if (!BasePtr.atIndex(I).isInitialized()) { - DiagnoseUninitializedSubobject(S, S.Current->getSource(OpPC), - BasePtr.getField()); + DiagnoseUninitializedSubobject(S, S.Current->getSource(OpPC), ElemType, + BasePtr.getFieldDesc()->getLocation()); Result = false; } } @@ -426,7 +426,8 @@ static bool CheckFieldsInitialized(InterpState &S, CodePtr OpPC, cast(FieldType->getAsArrayTypeUnsafe()); Result &= CheckArrayInitialized(S, OpPC, FieldPtr, CAT); } else if (!FieldPtr.isInitialized()) { - DiagnoseUninitializedSubobject(S, S.Current->getSource(OpPC), F.Decl); + DiagnoseUninitializedSubobject(S, S.Current->getSource(OpPC), + F.Decl->getType(), F.Decl->getLocation()); Result = false; } } diff --git a/clang/test/AST/Interp/cxx20.cpp b/clang/test/AST/Interp/cxx20.cpp index 2bf935ef2375b..37be7a41bf754 100644 --- a/clang/test/AST/Interp/cxx20.cpp +++ b/clang/test/AST/Interp/cxx20.cpp @@ -143,9 +143,9 @@ namespace UninitializedFields { constexpr A() {} }; constexpr A a; // expected-error {{must be initialized by a constant expression}} \ - // expected-note {{subobject 'a' is not initialized}} \ + // expected-note {{subobject of type 'int' is not initialized}} \ // ref-error {{must be initialized by a constant expression}} \ - // ref-note {{subobject 'a' is not initialized}} + // ref-note {{subobject of type 'int' is not initialized}} class Base { @@ -161,18 +161,18 @@ namespace UninitializedFields { constexpr Derived() : Base() {} }; constexpr Derived D; // expected-error {{must be initialized by a constant expression}} \ - // expected-note {{subobject 'a' is not initialized}} \ + // expected-note {{subobject of type 'int' is not initialized}} \ // ref-error {{must be initialized by a constant expression}} \ - // ref-note {{subobject 'a' is not initialized}} + // ref-note {{subobject of type 'int' is not initialized}} class C2 { public: A a; constexpr C2() {} }; constexpr C2 c2; // expected-error {{must be initialized by a constant expression}} \ - // expected-note {{subobject 'a' is not initialized}} \ + // expected-note {{subobject of type 'int' is not initialized}} \ // ref-error {{must be initialized by a constant expression}} \ - // ref-note {{subobject 'a' is not initialized}} + // ref-note {{subobject of type 'int' is not initialized}} // FIXME: These two are currently disabled because the array fields diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp index f1f677ebfcd34..b6f3e2e721f38 100644 --- a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp +++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp @@ -360,7 +360,7 @@ namespace PR14503 { // The constructor is still 'constexpr' here, but the result is not intended // to be a constant expression. The standard is not clear on how this should // work. - constexpr V v; // expected-error {{constant expression}} expected-note {{subobject 'y' is not initialized}} + constexpr V v; // expected-error {{constant expression}} expected-note {{subobject of type 'int' is not initialized}} constexpr int k = V().x; // FIXME: ok? } diff --git a/clang/test/SemaCXX/constant-expression-cxx2a.cpp b/clang/test/SemaCXX/constant-expression-cxx2a.cpp index 09f17d5b38949..34881d7446e58 100644 --- a/clang/test/SemaCXX/constant-expression-cxx2a.cpp +++ b/clang/test/SemaCXX/constant-expression-cxx2a.cpp @@ -409,12 +409,12 @@ namespace Union { b.a.x = 2; return b; } - constexpr B uninit = return_uninit(); // expected-error {{constant expression}} expected-note {{subobject 'y' is not initialized}} + constexpr B uninit = return_uninit(); // expected-error {{constant expression}} expected-note {{subobject of type 'int' is not initialized}} static_assert(return_uninit().a.x == 2); constexpr A return_uninit_struct() { B b = {.b = 1}; b.a.x = 2; - return b.a; // expected-note {{in call to 'A(b.a)'}} expected-note {{subobject 'y' is not initialized}} + return b.a; // expected-note {{in call to 'A(b.a)'}} expected-note {{subobject of type 'int' is not initialized}} } // Note that this is rejected even though return_uninit() is accepted, and // return_uninit() copies the same stuff wrapped in a union. @@ -558,7 +558,7 @@ namespace Uninit { } }; constinit X x1(true); - constinit X x2(false); // expected-error {{constant initializer}} expected-note {{constinit}} expected-note {{subobject 'n' is not initialized}} + constinit X x2(false); // expected-error {{constant initializer}} expected-note {{constinit}} expected-note {{subobject of type 'int' is not initialized}} struct Y { struct Z { int n; }; // expected-note {{here}} @@ -577,7 +577,7 @@ namespace Uninit { }; // FIXME: This is working around clang not implementing DR2026. With that // fixed, we should be able to test this without the injected copy. - constexpr Y copy(Y y) { return y; } // expected-note {{in call to 'Y(y)'}} expected-note {{subobject 'n' is not initialized}} + constexpr Y copy(Y y) { return y; } // expected-note {{in call to 'Y(y)'}} expected-note {{subobject of type 'int' is not initialized}} constexpr Y y1 = copy(Y()); static_assert(y1.z1.n == 1 && y1.z2.n == 2 && y1.z3.n == 3); diff --git a/clang/test/SemaCXX/cxx2a-consteval.cpp b/clang/test/SemaCXX/cxx2a-consteval.cpp index 7946107ae54f3..811c048342d7e 100644 --- a/clang/test/SemaCXX/cxx2a-consteval.cpp +++ b/clang/test/SemaCXX/cxx2a-consteval.cpp @@ -761,7 +761,7 @@ struct S { }; S s1; // expected-error {{call to consteval function 'NamespaceScopeConsteval::S::S' is not a constant expression}} \ - expected-note {{subobject 'Val' is not initialized}} + expected-note {{subobject of type 'int' is not initialized}} template struct T { @@ -770,7 +770,7 @@ struct T { }; T t; // expected-error {{call to consteval function 'NamespaceScopeConsteval::T::T' is not a constant expression}} \ - expected-note {{subobject 'Val' is not initialized}} + expected-note {{subobject of type 'int' is not initialized}} } // namespace NamespaceScopeConsteval