Skip to content

Commit 0e167fc

Browse files
committed
[clang][AST] Print name instead of type when diagnosing uninitialized subobject in constexpr variables
This patch improves the diagnostic on uninitialized subobjects in constexpr variables by modifying the diagnostic message to display the subobject's name instead of its type. Fixes #58601 Differential Revision: https://reviews.llvm.org/D146358
1 parent b5f2bc2 commit 0e167fc

File tree

8 files changed

+41
-41
lines changed

8 files changed

+41
-41
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,9 @@ Improvements to Clang's diagnostics
280280
Clang ABI >= 15.
281281
(`#62353: <https://github.com/llvm/llvm-project/issues/62353>`_,
282282
fallout from the non-POD packing ABI fix in LLVM 15).
283-
283+
- Clang constexpr evaluator now prints subobject's name instead of its type in notes
284+
when a constexpr variable has uninitialized subobjects after its constructor call.
285+
(`#58601 <https://github.com/llvm/llvm-project/issues/58601>`_)
284286

285287
Bug Fixes in This Version
286288
-------------------------

clang/include/clang/Basic/DiagnosticASTKinds.td

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ def note_consteval_address_accessible : Note<
6565
"%select{pointer|reference}0 to a consteval declaration "
6666
"is not a constant expression">;
6767
def note_constexpr_uninitialized : Note<
68-
"%select{|sub}0object of type %1 is not initialized">;
68+
"subobject %0 is not initialized">;
6969
def note_constexpr_static_local : Note<
7070
"control flows through the definition of a %select{static|thread_local}0 variable">;
7171
def note_constexpr_subobject_declared_here : Note<

clang/lib/AST/ExprConstant.cpp

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2119,7 +2119,7 @@ static bool CheckEvaluationResult(CheckEvaluationResultKind CERK,
21192119
EvalInfo &Info, SourceLocation DiagLoc,
21202120
QualType Type, const APValue &Value,
21212121
ConstantExprKind Kind,
2122-
SourceLocation SubobjectLoc,
2122+
const FieldDecl *SubobjectDecl,
21232123
CheckedTemporaries &CheckedTemps);
21242124

21252125
/// Check that this reference or pointer core constant expression is a valid
@@ -2266,8 +2266,8 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc,
22662266
APValue *V = MTE->getOrCreateValue(false);
22672267
assert(V && "evasluation result refers to uninitialised temporary");
22682268
if (!CheckEvaluationResult(CheckEvaluationResultKind::ConstantExpression,
2269-
Info, MTE->getExprLoc(), TempType, *V,
2270-
Kind, SourceLocation(), CheckedTemps))
2269+
Info, MTE->getExprLoc(), TempType, *V, Kind,
2270+
/*SubobjectDecl=*/nullptr, CheckedTemps))
22712271
return false;
22722272
}
22732273
}
@@ -2350,13 +2350,13 @@ static bool CheckEvaluationResult(CheckEvaluationResultKind CERK,
23502350
EvalInfo &Info, SourceLocation DiagLoc,
23512351
QualType Type, const APValue &Value,
23522352
ConstantExprKind Kind,
2353-
SourceLocation SubobjectLoc,
2353+
const FieldDecl *SubobjectDecl,
23542354
CheckedTemporaries &CheckedTemps) {
23552355
if (!Value.hasValue()) {
2356-
Info.FFDiag(DiagLoc, diag::note_constexpr_uninitialized)
2357-
<< true << Type;
2358-
if (SubobjectLoc.isValid())
2359-
Info.Note(SubobjectLoc, diag::note_constexpr_subobject_declared_here);
2356+
assert(SubobjectDecl && "SubobjectDecl shall be non-null");
2357+
Info.FFDiag(DiagLoc, diag::note_constexpr_uninitialized) << SubobjectDecl;
2358+
Info.Note(SubobjectDecl->getLocation(),
2359+
diag::note_constexpr_subobject_declared_here);
23602360
return false;
23612361
}
23622362

@@ -2373,20 +2373,19 @@ static bool CheckEvaluationResult(CheckEvaluationResultKind CERK,
23732373
for (unsigned I = 0, N = Value.getArrayInitializedElts(); I != N; ++I) {
23742374
if (!CheckEvaluationResult(CERK, Info, DiagLoc, EltTy,
23752375
Value.getArrayInitializedElt(I), Kind,
2376-
SubobjectLoc, CheckedTemps))
2376+
SubobjectDecl, CheckedTemps))
23772377
return false;
23782378
}
23792379
if (!Value.hasArrayFiller())
23802380
return true;
23812381
return CheckEvaluationResult(CERK, Info, DiagLoc, EltTy,
2382-
Value.getArrayFiller(), Kind, SubobjectLoc,
2382+
Value.getArrayFiller(), Kind, SubobjectDecl,
23832383
CheckedTemps);
23842384
}
23852385
if (Value.isUnion() && Value.getUnionField()) {
23862386
return CheckEvaluationResult(
23872387
CERK, Info, DiagLoc, Value.getUnionField()->getType(),
2388-
Value.getUnionValue(), Kind, Value.getUnionField()->getLocation(),
2389-
CheckedTemps);
2388+
Value.getUnionValue(), Kind, Value.getUnionField(), CheckedTemps);
23902389
}
23912390
if (Value.isStruct()) {
23922391
RecordDecl *RD = Type->castAs<RecordType>()->getDecl();
@@ -2395,7 +2394,7 @@ static bool CheckEvaluationResult(CheckEvaluationResultKind CERK,
23952394
for (const CXXBaseSpecifier &BS : CD->bases()) {
23962395
if (!CheckEvaluationResult(CERK, Info, DiagLoc, BS.getType(),
23972396
Value.getStructBase(BaseIndex), Kind,
2398-
BS.getBeginLoc(), CheckedTemps))
2397+
/*SubobjectDecl=*/nullptr, CheckedTemps))
23992398
return false;
24002399
++BaseIndex;
24012400
}
@@ -2405,8 +2404,8 @@ static bool CheckEvaluationResult(CheckEvaluationResultKind CERK,
24052404
continue;
24062405

24072406
if (!CheckEvaluationResult(CERK, Info, DiagLoc, I->getType(),
2408-
Value.getStructField(I->getFieldIndex()),
2409-
Kind, I->getLocation(), CheckedTemps))
2407+
Value.getStructField(I->getFieldIndex()), Kind,
2408+
I, CheckedTemps))
24102409
return false;
24112410
}
24122411
}
@@ -2440,7 +2439,7 @@ static bool CheckConstantExpression(EvalInfo &Info, SourceLocation DiagLoc,
24402439
CheckedTemporaries CheckedTemps;
24412440
return CheckEvaluationResult(CheckEvaluationResultKind::ConstantExpression,
24422441
Info, DiagLoc, Type, Value, Kind,
2443-
SourceLocation(), CheckedTemps);
2442+
/*SubobjectDecl=*/nullptr, CheckedTemps);
24442443
}
24452444

24462445
/// Check that this evaluated value is fully-initialized and can be loaded by
@@ -2450,7 +2449,7 @@ static bool CheckFullyInitialized(EvalInfo &Info, SourceLocation DiagLoc,
24502449
CheckedTemporaries CheckedTemps;
24512450
return CheckEvaluationResult(
24522451
CheckEvaluationResultKind::FullyInitialized, Info, DiagLoc, Type, Value,
2453-
ConstantExprKind::Normal, SourceLocation(), CheckedTemps);
2452+
ConstantExprKind::Normal, /*SubobjectDecl=*/nullptr, CheckedTemps);
24542453
}
24552454

24562455
/// Enforce C++2a [expr.const]/4.17, which disallows new-expressions unless

clang/lib/AST/Interp/Interp.cpp

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -369,11 +369,11 @@ bool CheckPure(InterpState &S, CodePtr OpPC, const CXXMethodDecl *MD) {
369369
}
370370

371371
static void DiagnoseUninitializedSubobject(InterpState &S, const SourceInfo &SI,
372-
QualType SubObjType,
373-
SourceLocation SubObjLoc) {
374-
S.FFDiag(SI, diag::note_constexpr_uninitialized) << true << SubObjType;
375-
if (SubObjLoc.isValid())
376-
S.Note(SubObjLoc, diag::note_constexpr_subobject_declared_here);
372+
const FieldDecl *SubObjDecl) {
373+
assert(SubObjDecl && "Subobject declaration does not exist");
374+
S.FFDiag(SI, diag::note_constexpr_uninitialized) << SubObjDecl;
375+
S.Note(SubObjDecl->getLocation(),
376+
diag::note_constexpr_subobject_declared_here);
377377
}
378378

379379
static bool CheckFieldsInitialized(InterpState &S, CodePtr OpPC,
@@ -400,8 +400,8 @@ static bool CheckArrayInitialized(InterpState &S, CodePtr OpPC,
400400
} else {
401401
for (size_t I = 0; I != NumElems; ++I) {
402402
if (!BasePtr.atIndex(I).isInitialized()) {
403-
DiagnoseUninitializedSubobject(S, S.Current->getSource(OpPC), ElemType,
404-
BasePtr.getFieldDesc()->getLocation());
403+
DiagnoseUninitializedSubobject(S, S.Current->getSource(OpPC),
404+
BasePtr.getField());
405405
Result = false;
406406
}
407407
}
@@ -426,8 +426,7 @@ static bool CheckFieldsInitialized(InterpState &S, CodePtr OpPC,
426426
cast<ConstantArrayType>(FieldType->getAsArrayTypeUnsafe());
427427
Result &= CheckArrayInitialized(S, OpPC, FieldPtr, CAT);
428428
} else if (!FieldPtr.isInitialized()) {
429-
DiagnoseUninitializedSubobject(S, S.Current->getSource(OpPC),
430-
F.Decl->getType(), F.Decl->getLocation());
429+
DiagnoseUninitializedSubobject(S, S.Current->getSource(OpPC), F.Decl);
431430
Result = false;
432431
}
433432
}

clang/test/AST/Interp/cxx20.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -143,9 +143,9 @@ namespace UninitializedFields {
143143
constexpr A() {}
144144
};
145145
constexpr A a; // expected-error {{must be initialized by a constant expression}} \
146-
// expected-note {{subobject of type 'int' is not initialized}} \
146+
// expected-note {{subobject 'a' is not initialized}} \
147147
// ref-error {{must be initialized by a constant expression}} \
148-
// ref-note {{subobject of type 'int' is not initialized}}
148+
// ref-note {{subobject 'a' is not initialized}}
149149

150150

151151
class Base {
@@ -161,18 +161,18 @@ namespace UninitializedFields {
161161
constexpr Derived() : Base() {} };
162162

163163
constexpr Derived D; // expected-error {{must be initialized by a constant expression}} \
164-
// expected-note {{subobject of type 'int' is not initialized}} \
164+
// expected-note {{subobject 'a' is not initialized}} \
165165
// ref-error {{must be initialized by a constant expression}} \
166-
// ref-note {{subobject of type 'int' is not initialized}}
166+
// ref-note {{subobject 'a' is not initialized}}
167167

168168
class C2 {
169169
public:
170170
A a;
171171
constexpr C2() {} };
172172
constexpr C2 c2; // expected-error {{must be initialized by a constant expression}} \
173-
// expected-note {{subobject of type 'int' is not initialized}} \
173+
// expected-note {{subobject 'a' is not initialized}} \
174174
// ref-error {{must be initialized by a constant expression}} \
175-
// ref-note {{subobject of type 'int' is not initialized}}
175+
// ref-note {{subobject 'a' is not initialized}}
176176

177177

178178
// FIXME: These two are currently disabled because the array fields

clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ namespace PR14503 {
360360
// The constructor is still 'constexpr' here, but the result is not intended
361361
// to be a constant expression. The standard is not clear on how this should
362362
// work.
363-
constexpr V<int> v; // expected-error {{constant expression}} expected-note {{subobject of type 'int' is not initialized}}
363+
constexpr V<int> v; // expected-error {{constant expression}} expected-note {{subobject 'y' is not initialized}}
364364

365365
constexpr int k = V<int>().x; // FIXME: ok?
366366
}

clang/test/SemaCXX/constant-expression-cxx2a.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -409,12 +409,12 @@ namespace Union {
409409
b.a.x = 2;
410410
return b;
411411
}
412-
constexpr B uninit = return_uninit(); // expected-error {{constant expression}} expected-note {{subobject of type 'int' is not initialized}}
412+
constexpr B uninit = return_uninit(); // expected-error {{constant expression}} expected-note {{subobject 'y' is not initialized}}
413413
static_assert(return_uninit().a.x == 2);
414414
constexpr A return_uninit_struct() {
415415
B b = {.b = 1};
416416
b.a.x = 2;
417-
return b.a; // expected-note {{in call to 'A(b.a)'}} expected-note {{subobject of type 'int' is not initialized}}
417+
return b.a; // expected-note {{in call to 'A(b.a)'}} expected-note {{subobject 'y' is not initialized}}
418418
}
419419
// Note that this is rejected even though return_uninit() is accepted, and
420420
// return_uninit() copies the same stuff wrapped in a union.
@@ -558,7 +558,7 @@ namespace Uninit {
558558
}
559559
};
560560
constinit X x1(true);
561-
constinit X x2(false); // expected-error {{constant initializer}} expected-note {{constinit}} expected-note {{subobject of type 'int' is not initialized}}
561+
constinit X x2(false); // expected-error {{constant initializer}} expected-note {{constinit}} expected-note {{subobject 'n' is not initialized}}
562562

563563
struct Y {
564564
struct Z { int n; }; // expected-note {{here}}
@@ -577,7 +577,7 @@ namespace Uninit {
577577
};
578578
// FIXME: This is working around clang not implementing DR2026. With that
579579
// fixed, we should be able to test this without the injected copy.
580-
constexpr Y copy(Y y) { return y; } // expected-note {{in call to 'Y(y)'}} expected-note {{subobject of type 'int' is not initialized}}
580+
constexpr Y copy(Y y) { return y; } // expected-note {{in call to 'Y(y)'}} expected-note {{subobject 'n' is not initialized}}
581581
constexpr Y y1 = copy(Y());
582582
static_assert(y1.z1.n == 1 && y1.z2.n == 2 && y1.z3.n == 3);
583583

clang/test/SemaCXX/cxx2a-consteval.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -761,7 +761,7 @@ struct S {
761761
};
762762

763763
S s1; // expected-error {{call to consteval function 'NamespaceScopeConsteval::S::S' is not a constant expression}} \
764-
expected-note {{subobject of type 'int' is not initialized}}
764+
expected-note {{subobject 'Val' is not initialized}}
765765

766766
template <typename Ty>
767767
struct T {
@@ -770,7 +770,7 @@ struct T {
770770
};
771771

772772
T<int> t; // expected-error {{call to consteval function 'NamespaceScopeConsteval::T<int>::T' is not a constant expression}} \
773-
expected-note {{subobject of type 'int' is not initialized}}
773+
expected-note {{subobject 'Val' is not initialized}}
774774

775775
} // namespace NamespaceScopeConsteval
776776

0 commit comments

Comments
 (0)