Skip to content

Commit f779ebc

Browse files
authored
[clang][ExprConst] Add "declared here" notes to uninitialized read diagnostics (#192206)
E.g. for ```c++ constexpr int unInitLocal() { int a; return a; // both-note {{read of uninitialized object}} } static_assert(unInitLocal() == 0, ""); // both-error {{not an integral constant expression}} \ // both-note {{in call to 'unInitLocal()'}} ``` we now diagnose: ```console array.cpp:896:15: error: static assertion expression is not an integral constant expression 896 | static_assert(unInitLocal() == 0, ""); // both-error {{not an integral constant expression}} \ | ^~~~~~~~~~~~~~~~~~ array.cpp:894:10: note: read of uninitialized object is not allowed in a constant expression 894 | return a; // both-note {{read of uninitialized object}} | ^ array.cpp:896:15: note: in call to 'unInitLocal()' 896 | static_assert(unInitLocal() == 0, ""); // both-error {{not an integral constant expression}} \ | ^~~~~~~~~~~~~ array.cpp:893:7: note: declared here 893 | int a; | ^ 1 warning and 1 error generated. ``` and point at the object that we were trying to read from. This adds an `NoteLValueLocation()` call to the emission of `note_constexpr_access_uninit`, which is already done in other places: https://github.com/llvm/llvm-project/blob/7ae5fe63dd979eae13ea04e166f94056ec1306ca/clang/lib/AST/ExprConstant.cpp#L4565-L4570
1 parent d6a970d commit f779ebc

34 files changed

Lines changed: 189 additions & 122 deletions

clang/lib/AST/ByteCode/Interp.cpp

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,17 @@ static void diagnoseMissingInitializer(InterpState &S, CodePtr OpPC,
8383
S.Note(VD->getLocation(), diag::note_declared_at) << VD->getSourceRange();
8484
}
8585

86+
static void noteValueLocation(InterpState &S, const Block *B) {
87+
const Descriptor *Desc = B->getDescriptor();
88+
89+
if (B->isDynamic())
90+
S.Note(Desc->getLocation(), diag::note_constexpr_dynamic_alloc_here);
91+
else if (B->isTemporary())
92+
S.Note(Desc->getLocation(), diag::note_constexpr_temporary_here);
93+
else
94+
S.Note(Desc->getLocation(), diag::note_declared_at);
95+
}
96+
8697
static void diagnoseNonConstVariable(InterpState &S, CodePtr OpPC,
8798
const ValueDecl *VD);
8899
static bool diagnoseUnknownDecl(InterpState &S, CodePtr OpPC,
@@ -185,8 +196,7 @@ static bool CheckTemporary(InterpState &S, CodePtr OpPC, const Block *B,
185196
!MTE->isUsableInConstantExpressions(S.getASTContext())) {
186197
const SourceInfo &E = S.Current->getSource(OpPC);
187198
S.FFDiag(E, diag::note_constexpr_access_static_temporary, 1) << AK;
188-
S.Note(B->getDescriptor()->getLocation(),
189-
diag::note_constexpr_temporary_here);
199+
noteValueLocation(S, B);
190200
return false;
191201
}
192202
}
@@ -418,14 +428,9 @@ bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
418428
if (Ptr.isDynamic()) {
419429
S.FFDiag(Src, diag::note_constexpr_access_deleted_object) << AK;
420430
} else if (!S.checkingPotentialConstantExpression()) {
421-
bool IsTemp = Ptr.isTemporary();
422431
S.FFDiag(Src, diag::note_constexpr_access_uninit)
423432
<< AK << /*uninitialized=*/false << S.Current->getRange(OpPC);
424-
425-
if (IsTemp)
426-
S.Note(Ptr.getDeclLoc(), diag::note_constexpr_temporary_here);
427-
else
428-
S.Note(Ptr.getDeclLoc(), diag::note_declared_at);
433+
noteValueLocation(S, Ptr.block());
429434
}
430435

431436
return false;
@@ -648,14 +653,16 @@ bool DiagnoseUninitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
648653
AccessKinds AK) {
649654
assert(Ptr.isLive());
650655
assert(!Ptr.isInitialized());
651-
return DiagnoseUninitialized(S, OpPC, Ptr.isExtern(), Ptr.getDeclDesc(), AK);
656+
return DiagnoseUninitialized(S, OpPC, Ptr.isExtern(), Ptr.block(), AK);
652657
}
653658

654659
bool DiagnoseUninitialized(InterpState &S, CodePtr OpPC, bool Extern,
655-
const Descriptor *Desc, AccessKinds AK) {
660+
const Block *B, AccessKinds AK) {
656661
if (Extern && S.checkingPotentialConstantExpression())
657662
return false;
658663

664+
const Descriptor *Desc = B->getDescriptor();
665+
659666
if (const auto *VD = Desc->asVarDecl();
660667
VD && (VD->isConstexpr() || VD->hasGlobalStorage())) {
661668

@@ -670,6 +677,7 @@ bool DiagnoseUninitialized(InterpState &S, CodePtr OpPC, bool Extern,
670677
// Diagnose as "read of object outside its lifetime".
671678
S.FFDiag(Loc, diag::note_constexpr_access_uninit)
672679
<< AK << /*IsIndeterminate=*/false;
680+
S.Note(VD->getLocation(), diag::note_declared_at);
673681
}
674682
return false;
675683
}
@@ -687,21 +695,27 @@ bool DiagnoseUninitialized(InterpState &S, CodePtr OpPC, bool Extern,
687695
if (!S.checkingPotentialConstantExpression()) {
688696
S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_access_uninit)
689697
<< AK << /*uninitialized=*/true << S.Current->getRange(OpPC);
698+
noteValueLocation(S, B);
690699
}
691700
return false;
692701
}
693702

694703
static bool CheckLifetime(InterpState &S, CodePtr OpPC, Lifetime LT,
695-
AccessKinds AK) {
704+
const Block *B, AccessKinds AK) {
696705
if (LT == Lifetime::Started)
697706
return true;
698707

699708
if (!S.checkingPotentialConstantExpression()) {
700709
S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_access_uninit)
701710
<< AK << /*uninitialized=*/false << S.Current->getRange(OpPC);
711+
noteValueLocation(S, B);
702712
}
703713
return false;
704714
}
715+
static bool CheckLifetime(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
716+
AccessKinds AK) {
717+
return CheckLifetime(S, OpPC, Ptr.getLifetime(), Ptr.block(), AK);
718+
}
705719

706720
static bool CheckWeak(InterpState &S, CodePtr OpPC, const Block *B) {
707721
if (!B->isWeak())
@@ -733,8 +747,7 @@ bool CheckGlobalLoad(InterpState &S, CodePtr OpPC, const Block *B) {
733747
if (!CheckConstant(S, OpPC, B->getDescriptor()))
734748
return false;
735749
if (Desc.InitState != GlobalInitState::Initialized)
736-
return DiagnoseUninitialized(S, OpPC, B->isExtern(), B->getDescriptor(),
737-
AK_Read);
750+
return DiagnoseUninitialized(S, OpPC, B->isExtern(), B, AK_Read);
738751
if (!CheckTemporary(S, OpPC, B, AK_Read))
739752
return false;
740753
if (B->getDescriptor()->IsVolatile) {
@@ -755,11 +768,10 @@ bool CheckGlobalLoad(InterpState &S, CodePtr OpPC, const Block *B) {
755768
bool CheckLocalLoad(InterpState &S, CodePtr OpPC, const Block *B) {
756769
assert(!B->isExtern());
757770
const auto &Desc = *reinterpret_cast<const InlineDescriptor *>(B->rawData());
758-
if (!CheckLifetime(S, OpPC, Desc.LifeState, AK_Read))
771+
if (!CheckLifetime(S, OpPC, Desc.LifeState, B, AK_Read))
759772
return false;
760773
if (!Desc.IsInitialized)
761-
return DiagnoseUninitialized(S, OpPC, /*Extern=*/false, B->getDescriptor(),
762-
AK_Read);
774+
return DiagnoseUninitialized(S, OpPC, /*Extern=*/false, B, AK_Read);
763775
if (B->getDescriptor()->IsVolatile) {
764776
if (!S.getLangOpts().CPlusPlus)
765777
return Invalid(S, OpPC);
@@ -805,7 +817,7 @@ bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
805817
return false;
806818
if (!CheckActive(S, OpPC, Ptr, AK))
807819
return false;
808-
if (!CheckLifetime(S, OpPC, Ptr.getLifetime(), AK))
820+
if (!CheckLifetime(S, OpPC, Ptr, AK))
809821
return false;
810822
if (!Ptr.isInitialized())
811823
return DiagnoseUninitialized(S, OpPC, Ptr, AK);
@@ -843,7 +855,7 @@ bool CheckFinalLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
843855

844856
if (!CheckActive(S, OpPC, Ptr, AK_Read))
845857
return false;
846-
if (!CheckLifetime(S, OpPC, Ptr.getLifetime(), AK_Read))
858+
if (!CheckLifetime(S, OpPC, Ptr, AK_Read))
847859
return false;
848860
if (!Ptr.isInitialized())
849861
return DiagnoseUninitialized(S, OpPC, Ptr, AK_Read);
@@ -868,7 +880,7 @@ bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
868880
return false;
869881
return CheckDummy(S, OpPC, Ptr.block(), AK_Assign);
870882
}
871-
if (!CheckLifetime(S, OpPC, Ptr.getLifetime(), AK_Assign))
883+
if (!CheckLifetime(S, OpPC, Ptr, AK_Assign))
872884
return false;
873885
if (!CheckRange(S, OpPC, Ptr, AK_Assign))
874886
return false;
@@ -1138,11 +1150,7 @@ bool CheckDeleteSource(InterpState &S, CodePtr OpPC, const Expr *Source,
11381150
const SourceInfo &Loc = S.Current->getSource(OpPC);
11391151
S.FFDiag(Loc, diag::note_constexpr_delete_not_heap_alloc)
11401152
<< Ptr.toDiagnosticString(S.getASTContext());
1141-
1142-
if (Ptr.isTemporary())
1143-
S.Note(Ptr.getDeclLoc(), diag::note_constexpr_temporary_here);
1144-
else
1145-
S.Note(Ptr.getDeclLoc(), diag::note_declared_at);
1153+
noteValueLocation(S, Ptr.block());
11461154
return false;
11471155
}
11481156

@@ -1505,7 +1513,7 @@ bool CheckDestructor(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
15051513
return false;
15061514
if (!CheckRange(S, OpPC, Ptr, AK_Destroy))
15071515
return false;
1508-
if (!CheckLifetime(S, OpPC, Ptr.getLifetime(), AK_Destroy))
1516+
if (!CheckLifetime(S, OpPC, Ptr, AK_Destroy))
15091517
return false;
15101518

15111519
// Can't call a dtor on a global variable.
@@ -2028,7 +2036,7 @@ bool CheckNewTypeMismatch(InterpState &S, CodePtr OpPC, const Expr *E,
20282036

20292037
// CheckLifetime for this and all base pointers.
20302038
for (Pointer P = Ptr;;) {
2031-
if (!CheckLifetime(S, OpPC, P.getLifetime(), AK_Construct))
2039+
if (!CheckLifetime(S, OpPC, P, AK_Construct))
20322040
return false;
20332041

20342042
if (P.isRoot())

clang/lib/AST/ByteCode/Interp.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ bool CheckFinalLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
8282
bool DiagnoseUninitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
8383
AccessKinds AK);
8484
bool DiagnoseUninitialized(InterpState &S, CodePtr OpPC, bool Extern,
85-
const Descriptor *Desc, AccessKinds AK);
85+
const Block *B, AccessKinds AK);
8686

8787
/// Checks a direct load of a primitive value from a global or local variable.
8888
bool CheckGlobalLoad(InterpState &S, CodePtr OpPC, const Block *B);
@@ -1663,8 +1663,7 @@ bool GetGlobalUnchecked(InterpState &S, CodePtr OpPC, uint32_t I) {
16631663
const Block *B = S.P.getGlobal(I);
16641664
const auto &Desc = B->getBlockDesc<GlobalInlineDescriptor>();
16651665
if (Desc.InitState != GlobalInitState::Initialized)
1666-
return DiagnoseUninitialized(S, OpPC, B->isExtern(), B->getDescriptor(),
1667-
AK_Read);
1666+
return DiagnoseUninitialized(S, OpPC, B->isExtern(), B, AK_Read);
16681667

16691668
S.Stk.push<T>(B->deref<T>());
16701669
return true;

clang/lib/AST/ExprConstant.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4180,10 +4180,12 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
41804180
// IsWithinLifetime, resulting in false.
41814181
if (I != 0 && handler.AccessKind == AK_IsWithinLifetime)
41824182
return false;
4183-
if (!Info.checkingPotentialConstantExpression())
4183+
if (!Info.checkingPotentialConstantExpression()) {
41844184
Info.FFDiag(E, diag::note_constexpr_access_uninit)
41854185
<< handler.AccessKind << O->isIndeterminate()
41864186
<< E->getSourceRange();
4187+
NoteLValueLocation(Info, Obj.Base);
4188+
}
41874189
return handler.failed();
41884190
}
41894191

clang/test/AST/ByteCode/builtin-bit-cast-bitfields.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,9 @@ namespace BitFields {
156156

157157
static_assert(f()[0] + f()[1] + f()[2] == 0xc0 + 0xff + 0xee);
158158
{
159-
// expected-error@+2 {{initialized by a constant expression}}
160-
// expected-note@+1 {{in call to}}
159+
// expected-error@+3 {{initialized by a constant expression}}
160+
// expected-note@+2 {{in call to}}
161+
// expected-note@+1 {{temporary created here}}
161162
constexpr auto _bad = f()[3];
162163
}
163164

@@ -173,8 +174,9 @@ namespace BitFields {
173174
};
174175
static_assert(g().s0 + g().s1 + g().b0 + g().b1 == 0xc0 + 0xff + 0xe + 0xe);
175176
{
176-
// expected-error@+2 {{initialized by a constant expression}}
177-
// expected-note@+1 {{read of uninitialized object is not allowed in a constant expression}}
177+
// expected-error@+3 {{initialized by a constant expression}}
178+
// expected-note@+2 {{read of uninitialized object is not allowed in a constant expression}}
179+
// expected-note@+1 {{temporary created here}}
178180
constexpr auto _bad = g().b2;
179181
}
180182
}

clang/test/AST/ByteCode/builtin-bit-cast-long-double.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ constexpr long double foo() {
6161
static_assert(foo() == ld);
6262

6363
constexpr bool f(bool read_uninit) {
64-
bytes b = bit_cast<bytes>(ld);
64+
bytes b = bit_cast<bytes>(ld); // both-note {{declared here}}
6565
unsigned char ld_bytes[10] = {
6666
0x0, 0x48, 0x9f, 0x49, 0xf0,
6767
0x3c, 0x20, 0xc9, 0x0, 0x40,

clang/test/AST/ByteCode/builtin-functions.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1216,7 +1216,7 @@ namespace shufflevector {
12161216
#if __cplusplus >= 202002L
12171217
constexpr int discarded3() {
12181218
int i = 0;
1219-
vector4char a;
1219+
vector4char a; // both-note {{declared here}}
12201220
__builtin_shufflevector((++i, a), a, 0); // both-note {{read of uninitialized object}} \
12211221
// both-warning {{expression result unused}}
12221222
return i;

clang/test/AST/ByteCode/constexpr-nqueens.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,9 @@ constexpr Board buildBoardRecurse(int N, int Col, const Board &B) {
6060
}
6161
constexpr Board buildBoard(int N) {
6262
return buildBoardRecurse(N, 0, Board()); // ref-note {{in call to 'buildBoardRecurse(8, 0, Board())'}} \
63-
// expected-note {{in call to 'buildBoardRecurse(8, 0, Board())'}}
63+
// expected-note {{in call to 'buildBoardRecurse(8, 0, Board())'}} \
64+
// ref-note {{temporary created here}} \
65+
// expected-note {{temporary created here}}
6466
}
6567

6668
constexpr Board q8 = buildBoard(8); // ref-error {{must be initialized by a constant expression}} \

clang/test/AST/ByteCode/cxx11.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ int array2[recurse2]; // both-warning {{variable length arrays in C++}} \
2424
// ref-warning {{variable length array folded to constant array as an extension}}
2525

2626
constexpr int b = b; // both-error {{must be initialized by a constant expression}} \
27-
// both-note {{read of object outside its lifetime is not allowed in a constant expression}}
27+
// both-note {{read of object outside its lifetime is not allowed in a constant expression}} \
28+
// both-note {{declared here}}
2829

2930

3031
[[clang::require_constant_initialization]] int c = c; // both-error {{variable does not have a constant initializer}} \

clang/test/AST/ByteCode/cxx17.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33

44
[[clang::require_constant_initialization]] int cc = cc; // both-error {{variable does not have a constant initializer}} \
55
// both-note {{attribute here}} \
6-
// both-note {{ead of object outside its lifetime}}
6+
// both-note {{ead of object outside its lifetime}} \
7+
// both-note {{declared here}}
78

89

910
struct F { int a; int b;};

clang/test/AST/ByteCode/cxx20.cpp

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ constexpr int pointerAssign2() {
5757
static_assert(pointerAssign2() == 12, "");
5858

5959
constexpr int unInitLocal() {
60-
int a;
60+
int a; // both-note {{declared here}}
6161
return a; // both-note {{read of uninitialized object}}
6262
}
6363
static_assert(unInitLocal() == 0, ""); // both-error {{not an integral constant expression}} \
@@ -71,7 +71,7 @@ constexpr int initializedLocal() {
7171
static_assert(initializedLocal() == 20);
7272

7373
constexpr int initializedLocal2() {
74-
int a[2];
74+
int a[2]; // both-note {{declared here}}
7575
return *a; // both-note {{read of uninitialized object is not allowed in a constant expression}}
7676
}
7777
static_assert(initializedLocal2() == 20); // both-error {{not an integral constant expression}} \
@@ -80,7 +80,7 @@ static_assert(initializedLocal2() == 20); // both-error {{not an integral consta
8080

8181
struct Int { int a; };
8282
constexpr int initializedLocal3() {
83-
Int i;
83+
Int i; // both-note {{declared here}}
8484
return i.a; // both-note {{read of uninitialized object is not allowed in a constant expression}}
8585
}
8686
static_assert(initializedLocal3() == 20); // both-error {{not an integral constant expression}} \
@@ -274,7 +274,8 @@ namespace BaseInit {
274274

275275
static_assert(Final{1, 2, 3}.c == 3, ""); // OK
276276
static_assert(Final{1, 2, 3}.a == 0, ""); // both-error {{not an integral constant expression}} \
277-
// both-note {{read of uninitialized object}}
277+
// both-note {{read of uninitialized object}} \
278+
// both-note {{temporary created here}}
278279

279280

280281
struct Mixin {
@@ -294,7 +295,8 @@ namespace BaseInit {
294295
static_assert(Final2{1, 2, 3}.c == 3, ""); // OK
295296
static_assert(Final2{1, 2, 3}.b == 2, ""); // OK
296297
static_assert(Final2{1, 2, 3}.a == 0, ""); // both-error {{not an integral constant expression}} \
297-
// both-note {{read of uninitialized object}}
298+
// both-note {{read of uninitialized object}} \
299+
// both-note {{temporary created here}}
298300

299301

300302
struct Mixin3 {
@@ -311,7 +313,8 @@ namespace BaseInit {
311313
static_assert(Final3{1, 2, 3}.c == 3, ""); // OK
312314
static_assert(Final3{1, 2, 3}.b == 2, ""); // OK
313315
static_assert(Final3{1, 2, 3}.a == 0, ""); // both-error {{not an integral constant expression}} \
314-
// both-note {{read of uninitialized object}}
316+
// both-note {{read of uninitialized object}} \
317+
// both-note {{temporary created here}}
315318
};
316319

317320
namespace Destructors {
@@ -584,7 +587,7 @@ namespace ImplicitFunction {
584587
};
585588

586589
constexpr int callMe() {
587-
A a;
590+
A a; // expected-note {{declared here}}
588591
A b{12};
589592

590593
/// The operator= call here will fail and the diagnostics should be fine.
@@ -969,7 +972,7 @@ namespace LocalDestroy {
969972
namespace PseudoDtor {
970973
constexpr int f1() {
971974
using T = int;
972-
int a = 0;
975+
int a = 0; // both-note {{declared here}}
973976
a.~T();
974977
return a; // both-note {{read of object outside its lifetime}}
975978
}
@@ -978,7 +981,7 @@ namespace PseudoDtor {
978981

979982
constexpr int f2() {
980983
using T = int;
981-
int a = 0;
984+
int a = 0; // both-note {{declared here}}
982985
a.~T();
983986
a = 0; // both-note {{assignment to object outside its lifetime}}
984987
return a;

0 commit comments

Comments
 (0)