Skip to content

Commit ba11d31

Browse files
authored
[clang][Interp] Only diagnose null field access in constant contexts (#69223)
Looks like this should work as long as we don't dereference the value.
1 parent f118d47 commit ba11d31

File tree

5 files changed

+72
-7
lines changed

5 files changed

+72
-7
lines changed

clang/lib/AST/Interp/Interp.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
216216
}
217217

218218
bool CheckDummy(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
219-
return !Ptr.isDummy();
219+
return !Ptr.isZero() && !Ptr.isDummy();
220220
}
221221

222222
bool CheckNull(InterpState &S, CodePtr OpPC, const Pointer &Ptr,

clang/lib/AST/Interp/Interp.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1155,7 +1155,7 @@ inline bool GetPtrGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
11551155
/// 2) Pushes Pointer.atField(Off) on the stack
11561156
inline bool GetPtrField(InterpState &S, CodePtr OpPC, uint32_t Off) {
11571157
const Pointer &Ptr = S.Stk.pop<Pointer>();
1158-
if (!CheckNull(S, OpPC, Ptr, CSK_Field))
1158+
if (S.inConstantContext() && !CheckNull(S, OpPC, Ptr, CSK_Field))
11591159
return false;
11601160
if (!CheckExtern(S, OpPC, Ptr))
11611161
return false;

clang/lib/AST/Interp/Pointer.h

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,10 @@ class Pointer {
199199
bool isField() const { return Base != 0 && Base != RootPtrMark; }
200200

201201
/// Accessor for information about the declaration site.
202-
const Descriptor *getDeclDesc() const { return Pointee->Desc; }
202+
const Descriptor *getDeclDesc() const {
203+
assert(Pointee);
204+
return Pointee->Desc;
205+
}
203206
SourceLocation getDeclLoc() const { return getDeclDesc()->getLocation(); }
204207

205208
/// Returns a pointer to the object of which this pointer is a field.
@@ -296,11 +299,17 @@ class Pointer {
296299
bool isUnion() const;
297300

298301
/// Checks if the storage is extern.
299-
bool isExtern() const { return Pointee->isExtern(); }
302+
bool isExtern() const { return Pointee && Pointee->isExtern(); }
300303
/// Checks if the storage is static.
301-
bool isStatic() const { return Pointee->isStatic(); }
304+
bool isStatic() const {
305+
assert(Pointee);
306+
return Pointee->isStatic();
307+
}
302308
/// Checks if the storage is temporary.
303-
bool isTemporary() const { return Pointee->isTemporary(); }
309+
bool isTemporary() const {
310+
assert(Pointee);
311+
return Pointee->isTemporary();
312+
}
304313
/// Checks if the storage is a static temporary.
305314
bool isStaticTemporary() const { return isStatic() && isTemporary(); }
306315

@@ -323,7 +332,10 @@ class Pointer {
323332
}
324333

325334
/// Returns the declaration ID.
326-
std::optional<unsigned> getDeclID() const { return Pointee->getDeclID(); }
335+
std::optional<unsigned> getDeclID() const {
336+
assert(Pointee);
337+
return Pointee->getDeclID();
338+
}
327339

328340
/// Returns the byte offset from the start.
329341
unsigned getByteOffset() const {
@@ -351,6 +363,8 @@ class Pointer {
351363

352364
/// Checks if the index is one past end.
353365
bool isOnePastEnd() const {
366+
if (!Pointee)
367+
return false;
354368
return isElementPastEnd() || getSize() == getOffset();
355369
}
356370

@@ -360,6 +374,7 @@ class Pointer {
360374
/// Dereferences the pointer, if it's live.
361375
template <typename T> T &deref() const {
362376
assert(isLive() && "Invalid pointer");
377+
assert(Pointee);
363378
if (isArrayRoot())
364379
return *reinterpret_cast<T *>(Pointee->rawData() + Base +
365380
sizeof(InitMapPtr));
@@ -370,6 +385,7 @@ class Pointer {
370385
/// Dereferences a primitive element.
371386
template <typename T> T &elem(unsigned I) const {
372387
assert(I < getNumElems());
388+
assert(Pointee);
373389
return reinterpret_cast<T *>(Pointee->data() + sizeof(InitMapPtr))[I];
374390
}
375391

@@ -431,12 +447,14 @@ class Pointer {
431447
/// Returns a descriptor at a given offset.
432448
InlineDescriptor *getDescriptor(unsigned Offset) const {
433449
assert(Offset != 0 && "Not a nested pointer");
450+
assert(Pointee);
434451
return reinterpret_cast<InlineDescriptor *>(Pointee->rawData() + Offset) -
435452
1;
436453
}
437454

438455
/// Returns a reference to the InitMapPtr which stores the initialization map.
439456
InitMapPtr &getInitMap() const {
457+
assert(Pointee);
440458
return *reinterpret_cast<InitMapPtr *>(Pointee->rawData() + Base);
441459
}
442460

clang/test/AST/Interp/c.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
// RUN: %clang_cc1 -verify=ref -std=c11 %s
44
// RUN: %clang_cc1 -pedantic -verify=pedantic-ref -std=c11 %s
55

6+
typedef __INTPTR_TYPE__ intptr_t;
7+
68
_Static_assert(1, "");
79
_Static_assert(0 != 1, "");
810
_Static_assert(1.0 == 1.0, ""); // pedantic-ref-warning {{not an integer constant expression}} \
@@ -67,3 +69,15 @@ _Static_assert(&Test50 != (void*)0, ""); // ref-warning {{always true}} \
6769
// expected-warning {{always true}} \
6870
// pedantic-expected-warning {{always true}} \
6971
// pedantic-expected-warning {{is a GNU extension}}
72+
73+
struct y {int x,y;};
74+
int a2[(intptr_t)&((struct y*)0)->y]; // expected-warning {{folded to constant array}} \
75+
// pedantic-expected-warning {{folded to constant array}} \
76+
// ref-warning {{folded to constant array}} \
77+
// pedantic-ref-warning {{folded to constant array}}
78+
79+
const struct y *yy = (struct y*)0;
80+
const intptr_t L = (intptr_t)(&(yy->y)); // expected-error {{not a compile-time constant}} \
81+
// pedantic-expected-error {{not a compile-time constant}} \
82+
// ref-error {{not a compile-time constant}} \
83+
// pedantic-ref-error {{not a compile-time constant}}

clang/test/AST/Interp/records.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1102,3 +1102,36 @@ namespace DelegatingConstructors {
11021102
static_assert(d4.a == 10, "");
11031103
static_assert(d4.b == 12, "");
11041104
}
1105+
1106+
namespace AccessOnNullptr {
1107+
struct F {
1108+
int a;
1109+
};
1110+
1111+
constexpr int a() { // expected-error {{never produces a constant expression}} \
1112+
// ref-error {{never produces a constant expression}}
1113+
F *f = nullptr;
1114+
1115+
f->a = 0; // expected-note 2{{cannot access field of null pointer}} \
1116+
// ref-note 2{{cannot access field of null pointer}}
1117+
return f->a;
1118+
}
1119+
static_assert(a() == 0, ""); // expected-error {{not an integral constant expression}} \
1120+
// expected-note {{in call to 'a()'}} \
1121+
// ref-error {{not an integral constant expression}} \
1122+
// ref-note {{in call to 'a()'}}
1123+
1124+
constexpr int a2() { // expected-error {{never produces a constant expression}} \
1125+
// ref-error {{never produces a constant expression}}
1126+
F *f = nullptr;
1127+
1128+
1129+
const int *a = &(f->a); // expected-note 2{{cannot access field of null pointer}} \
1130+
// ref-note 2{{cannot access field of null pointer}}
1131+
return f->a;
1132+
}
1133+
static_assert(a2() == 0, ""); // expected-error {{not an integral constant expression}} \
1134+
// expected-note {{in call to 'a2()'}} \
1135+
// ref-error {{not an integral constant expression}} \
1136+
// ref-note {{in call to 'a2()'}}
1137+
}

0 commit comments

Comments
 (0)