Index: clang/docs/ReleaseNotes.rst =================================================================== --- clang/docs/ReleaseNotes.rst +++ clang/docs/ReleaseNotes.rst @@ -196,6 +196,9 @@ - Clang now checks for completeness of the second and third arguments in the conditional operator. (`#59718 `_) +- 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 ------------------------- Index: clang/include/clang/Basic/DiagnosticASTKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticASTKinds.td +++ clang/include/clang/Basic/DiagnosticASTKinds.td @@ -65,7 +65,7 @@ "%select{pointer|reference}0 to a consteval declaration " "is not a constant expression">; def note_constexpr_uninitialized : Note< - "%select{|sub}0object of type %1 is not initialized">; + "subobject %0 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< Index: clang/lib/AST/ExprConstant.cpp =================================================================== --- clang/lib/AST/ExprConstant.cpp +++ clang/lib/AST/ExprConstant.cpp @@ -2119,7 +2119,7 @@ EvalInfo &Info, SourceLocation DiagLoc, QualType Type, const APValue &Value, ConstantExprKind Kind, - SourceLocation SubobjectLoc, + const FieldDecl *SubobjectDecl, CheckedTemporaries &CheckedTemps); /// Check that this reference or pointer core constant expression is a valid @@ -2266,8 +2266,8 @@ APValue *V = MTE->getOrCreateValue(false); assert(V && "evasluation result refers to uninitialised temporary"); if (!CheckEvaluationResult(CheckEvaluationResultKind::ConstantExpression, - Info, MTE->getExprLoc(), TempType, *V, - Kind, SourceLocation(), CheckedTemps)) + Info, MTE->getExprLoc(), TempType, *V, Kind, + /*SubobjectDecl=*/nullptr, CheckedTemps)) return false; } } @@ -2350,13 +2350,15 @@ EvalInfo &Info, SourceLocation DiagLoc, QualType Type, const APValue &Value, ConstantExprKind Kind, - SourceLocation SubobjectLoc, + const FieldDecl *SubobjectDecl, CheckedTemporaries &CheckedTemps) { + if (!Value.hasValue()) { - Info.FFDiag(DiagLoc, diag::note_constexpr_uninitialized) - << true << Type; - if (SubobjectLoc.isValid()) - Info.Note(SubobjectLoc, diag::note_constexpr_subobject_declared_here); + if (SubobjectDecl) { + Info.FFDiag(DiagLoc, diag::note_constexpr_uninitialized) << SubobjectDecl; + Info.Note(SubobjectDecl->getLocation(), + diag::note_constexpr_subobject_declared_here); + } return false; } @@ -2373,20 +2375,19 @@ for (unsigned I = 0, N = Value.getArrayInitializedElts(); I != N; ++I) { if (!CheckEvaluationResult(CERK, Info, DiagLoc, EltTy, Value.getArrayInitializedElt(I), Kind, - SubobjectLoc, CheckedTemps)) + SubobjectDecl, CheckedTemps)) return false; } if (!Value.hasArrayFiller()) return true; return CheckEvaluationResult(CERK, Info, DiagLoc, EltTy, - Value.getArrayFiller(), Kind, SubobjectLoc, + Value.getArrayFiller(), Kind, SubobjectDecl, CheckedTemps); } if (Value.isUnion() && Value.getUnionField()) { return CheckEvaluationResult( CERK, Info, DiagLoc, Value.getUnionField()->getType(), - Value.getUnionValue(), Kind, Value.getUnionField()->getLocation(), - CheckedTemps); + Value.getUnionValue(), Kind, Value.getUnionField(), CheckedTemps); } if (Value.isStruct()) { RecordDecl *RD = Type->castAs()->getDecl(); @@ -2395,7 +2396,7 @@ for (const CXXBaseSpecifier &BS : CD->bases()) { if (!CheckEvaluationResult(CERK, Info, DiagLoc, BS.getType(), Value.getStructBase(BaseIndex), Kind, - BS.getBeginLoc(), CheckedTemps)) + /*SubobjectDecl=*/nullptr, CheckedTemps)) return false; ++BaseIndex; } @@ -2405,8 +2406,8 @@ continue; if (!CheckEvaluationResult(CERK, Info, DiagLoc, I->getType(), - Value.getStructField(I->getFieldIndex()), - Kind, I->getLocation(), CheckedTemps)) + Value.getStructField(I->getFieldIndex()), Kind, + I, CheckedTemps)) return false; } } @@ -2440,7 +2441,7 @@ CheckedTemporaries CheckedTemps; return CheckEvaluationResult(CheckEvaluationResultKind::ConstantExpression, Info, DiagLoc, Type, Value, Kind, - SourceLocation(), CheckedTemps); + /*SubobjectDecl=*/nullptr, CheckedTemps); } /// Check that this evaluated value is fully-initialized and can be loaded by @@ -2450,7 +2451,7 @@ CheckedTemporaries CheckedTemps; return CheckEvaluationResult( CheckEvaluationResultKind::FullyInitialized, Info, DiagLoc, Type, Value, - ConstantExprKind::Normal, SourceLocation(), CheckedTemps); + ConstantExprKind::Normal, /*SubobjectDecl=*/nullptr, CheckedTemps); } /// Enforce C++2a [expr.const]/4.17, which disallows new-expressions unless Index: clang/lib/AST/Interp/Interp.cpp =================================================================== --- clang/lib/AST/Interp/Interp.cpp +++ clang/lib/AST/Interp/Interp.cpp @@ -369,11 +369,11 @@ } static void DiagnoseUninitializedSubobject(InterpState &S, const SourceInfo &SI, - 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); + 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); } static bool CheckFieldsInitialized(InterpState &S, CodePtr OpPC, @@ -400,8 +400,8 @@ } else { for (size_t I = 0; I != NumElems; ++I) { if (!BasePtr.atIndex(I).isInitialized()) { - DiagnoseUninitializedSubobject(S, S.Current->getSource(OpPC), ElemType, - BasePtr.getFieldDesc()->getLocation()); + DiagnoseUninitializedSubobject(S, S.Current->getSource(OpPC), + BasePtr.getField()); Result = false; } } @@ -426,8 +426,7 @@ cast(FieldType->getAsArrayTypeUnsafe()); Result &= CheckArrayInitialized(S, OpPC, FieldPtr, CAT); } else if (!FieldPtr.isInitialized()) { - DiagnoseUninitializedSubobject(S, S.Current->getSource(OpPC), - F.Decl->getType(), F.Decl->getLocation()); + DiagnoseUninitializedSubobject(S, S.Current->getSource(OpPC), F.Decl); Result = false; } } Index: clang/test/AST/Interp/cxx20.cpp =================================================================== --- clang/test/AST/Interp/cxx20.cpp +++ clang/test/AST/Interp/cxx20.cpp @@ -143,9 +143,9 @@ constexpr A() {} }; constexpr A a; // expected-error {{must be initialized by a constant expression}} \ - // expected-note {{subobject of type 'int' is not initialized}} \ + // expected-note {{subobject 'a' is not initialized}} \ // ref-error {{must be initialized by a constant expression}} \ - // ref-note {{subobject of type 'int' is not initialized}} + // ref-note {{subobject 'a' is not initialized}} class Base { @@ -161,18 +161,18 @@ constexpr Derived() : Base() {} }; constexpr Derived D; // expected-error {{must be initialized by a constant expression}} \ - // expected-note {{subobject of type 'int' is not initialized}} \ + // expected-note {{subobject 'a' is not initialized}} \ // ref-error {{must be initialized by a constant expression}} \ - // ref-note {{subobject of type 'int' is not initialized}} + // ref-note {{subobject 'a' 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 of type 'int' is not initialized}} \ + // expected-note {{subobject 'a' is not initialized}} \ // ref-error {{must be initialized by a constant expression}} \ - // ref-note {{subobject of type 'int' is not initialized}} + // ref-note {{subobject 'a' is not initialized}} // FIXME: These two are currently disabled because the array fields Index: clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp =================================================================== --- clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp +++ clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp @@ -359,7 +359,7 @@ // 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 of type 'int' is not initialized}} + constexpr V v; // expected-error {{constant expression}} expected-note {{subobject 'y' is not initialized}} constexpr int k = V().x; // FIXME: ok? } Index: clang/test/SemaCXX/constant-expression-cxx2a.cpp =================================================================== --- clang/test/SemaCXX/constant-expression-cxx2a.cpp +++ clang/test/SemaCXX/constant-expression-cxx2a.cpp @@ -409,12 +409,12 @@ b.a.x = 2; return b; } - constexpr B uninit = return_uninit(); // expected-error {{constant expression}} expected-note {{subobject of type 'int' is not initialized}} + constexpr B uninit = return_uninit(); // expected-error {{constant expression}} expected-note {{subobject 'y' 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 of type 'int' is not initialized}} + return b.a; // expected-note {{in call to 'A(b.a)'}} expected-note {{subobject 'y' 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 @@ } }; constinit X x1(true); - constinit X x2(false); // expected-error {{constant initializer}} expected-note {{constinit}} expected-note {{subobject of type 'int' is not initialized}} + constinit X x2(false); // expected-error {{constant initializer}} expected-note {{constinit}} expected-note {{subobject 'n' is not initialized}} struct Y { struct Z { int n; }; // expected-note {{here}} @@ -577,7 +577,7 @@ }; // 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 of type 'int' is not initialized}} + constexpr Y copy(Y y) { return y; } // expected-note {{in call to 'Y(y)'}} expected-note {{subobject 'n' is not initialized}} constexpr Y y1 = copy(Y()); static_assert(y1.z1.n == 1 && y1.z2.n == 2 && y1.z3.n == 3); Index: clang/test/SemaCXX/cxx2a-consteval.cpp =================================================================== --- clang/test/SemaCXX/cxx2a-consteval.cpp +++ clang/test/SemaCXX/cxx2a-consteval.cpp @@ -761,7 +761,7 @@ }; S s1; // expected-error {{call to consteval function 'NamespaceScopeConsteval::S::S' is not a constant expression}} \ - expected-note {{subobject of type 'int' is not initialized}} + expected-note {{subobject 'Val' is not initialized}} template struct T { @@ -770,7 +770,7 @@ }; T t; // expected-error {{call to consteval function 'NamespaceScopeConsteval::T::T' is not a constant expression}} \ - expected-note {{subobject of type 'int' is not initialized}} + expected-note {{subobject 'Val' is not initialized}} } // namespace NamespaceScopeConsteval