Skip to content

Commit 7f677fe

Browse files
authored
[clang][Interp] Add explicit dummy descriptors (#68888)
Instead of (ab)using incomplete array types for this, add a 'Dummy' bit to Descriptor. We need to be able to differentiate between the two when adding an offset.
1 parent 1b6b4d6 commit 7f677fe

File tree

8 files changed

+69
-15
lines changed

8 files changed

+69
-15
lines changed

clang/lib/AST/Interp/Descriptor.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,13 @@ Descriptor::Descriptor(const DeclTy &D, Record *R, MetadataSize MD,
296296
assert(Source && "Missing source");
297297
}
298298

299+
Descriptor::Descriptor(const DeclTy &D, MetadataSize MD)
300+
: Source(D), ElemSize(1), Size(ElemSize), MDSize(MD.value_or(0)),
301+
AllocSize(Size + MDSize), ElemRecord(nullptr), IsConst(true),
302+
IsMutable(false), IsTemporary(false), IsDummy(true) {
303+
assert(Source && "Missing source");
304+
}
305+
299306
QualType Descriptor::getType() const {
300307
if (auto *E = asExpr())
301308
return E->getType();

clang/lib/AST/Interp/Descriptor.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ struct Descriptor final {
111111
const bool IsTemporary = false;
112112
/// Flag indicating if the block is an array.
113113
const bool IsArray = false;
114+
/// Flag indicating if this is a dummy descriptor.
115+
const bool IsDummy = false;
114116

115117
/// Storage management methods.
116118
const BlockCtorFn CtorFn = nullptr;
@@ -139,6 +141,8 @@ struct Descriptor final {
139141
Descriptor(const DeclTy &D, Record *R, MetadataSize MD, bool IsConst,
140142
bool IsTemporary, bool IsMutable);
141143

144+
Descriptor(const DeclTy &D, MetadataSize MD);
145+
142146
QualType getType() const;
143147
QualType getElemQualType() const;
144148
SourceLocation getLocation() const;
@@ -192,6 +196,8 @@ struct Descriptor final {
192196
bool isArray() const { return IsArray; }
193197
/// Checks if the descriptor is of a record.
194198
bool isRecord() const { return !IsArray && ElemRecord; }
199+
/// Checks if this is a dummy descriptor.
200+
bool isDummy() const { return IsDummy; }
195201
};
196202

197203
/// Bitfield tracking the initialisation status of elements of primitive arrays.

clang/lib/AST/Interp/Interp.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,10 @@ bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
215215
return true;
216216
}
217217

218+
bool CheckDummy(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
219+
return !Ptr.isDummy();
220+
}
221+
218222
bool CheckNull(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
219223
CheckSubobjectKind CSK) {
220224
if (!Ptr.isZero())
@@ -297,6 +301,8 @@ bool CheckInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
297301
}
298302

299303
bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
304+
if (!CheckDummy(S, OpPC, Ptr))
305+
return false;
300306
if (!CheckLive(S, OpPC, Ptr, AK_Read))
301307
return false;
302308
if (!CheckExtern(S, OpPC, Ptr))

clang/lib/AST/Interp/Interp.h

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ bool CheckArray(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
5555
/// Checks if a pointer is live and accessible.
5656
bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
5757
AccessKinds AK);
58+
59+
/// Checks if a pointer is a dummy pointer.
60+
bool CheckDummy(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
61+
5862
/// Checks if a pointer is null.
5963
bool CheckNull(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
6064
CheckSubobjectKind CSK);
@@ -1415,8 +1419,9 @@ bool OffsetHelper(InterpState &S, CodePtr OpPC, const T &Offset,
14151419
// Compute the largest index into the array.
14161420
T MaxIndex = T::from(Ptr.getNumElems(), Offset.bitWidth());
14171421

1422+
bool Invalid = false;
14181423
// Helper to report an invalid offset, computed as APSInt.
1419-
auto InvalidOffset = [&]() {
1424+
auto DiagInvalidOffset = [&]() -> void {
14201425
const unsigned Bits = Offset.bitWidth();
14211426
APSInt APOffset(Offset.toAPSInt().extend(Bits + 2), false);
14221427
APSInt APIndex(Index.toAPSInt().extend(Bits + 2), false);
@@ -1426,28 +1431,31 @@ bool OffsetHelper(InterpState &S, CodePtr OpPC, const T &Offset,
14261431
<< NewIndex
14271432
<< /*array*/ static_cast<int>(!Ptr.inArray())
14281433
<< static_cast<unsigned>(MaxIndex);
1429-
return false;
1434+
Invalid = true;
14301435
};
14311436

14321437
T MaxOffset = T::from(MaxIndex - Index, Offset.bitWidth());
14331438
if constexpr (Op == ArithOp::Add) {
14341439
// If the new offset would be negative, bail out.
14351440
if (Offset.isNegative() && (Offset.isMin() || -Offset > Index))
1436-
return InvalidOffset();
1441+
DiagInvalidOffset();
14371442

14381443
// If the new offset would be out of bounds, bail out.
14391444
if (Offset.isPositive() && Offset > MaxOffset)
1440-
return InvalidOffset();
1445+
DiagInvalidOffset();
14411446
} else {
14421447
// If the new offset would be negative, bail out.
14431448
if (Offset.isPositive() && Index < Offset)
1444-
return InvalidOffset();
1449+
DiagInvalidOffset();
14451450

14461451
// If the new offset would be out of bounds, bail out.
14471452
if (Offset.isNegative() && (Offset.isMin() || -Offset > MaxOffset))
1448-
return InvalidOffset();
1453+
DiagInvalidOffset();
14491454
}
14501455

1456+
if (Invalid && !Ptr.isDummy())
1457+
return false;
1458+
14511459
// Offset is valid - compute it on unsigned.
14521460
int64_t WideIndex = static_cast<int64_t>(Index);
14531461
int64_t WideOffset = static_cast<int64_t>(Offset);

clang/lib/AST/Interp/InterpBuiltin.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,9 @@ static bool interp__builtin_strlen(InterpState &S, CodePtr OpPC,
152152
if (!CheckLive(S, OpPC, StrPtr, AK_Read))
153153
return false;
154154

155+
if (!CheckDummy(S, OpPC, StrPtr))
156+
return false;
157+
155158
assert(StrPtr.getFieldDesc()->isPrimitiveArray());
156159

157160
size_t Len = 0;

clang/lib/AST/Interp/Pointer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,8 @@ class Pointer {
314314
bool isActive() const { return Base == 0 || getInlineDesc()->IsActive; }
315315
/// Checks if a structure is a base class.
316316
bool isBaseClass() const { return isField() && getInlineDesc()->IsBase; }
317+
/// Checks if the pointer pointers to a dummy value.
318+
bool isDummy() const { return getDeclDesc()->isDummy(); }
317319

318320
/// Checks if an object or a subfield is mutable.
319321
bool isConst() const {

clang/lib/AST/Interp/Program.cpp

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -144,16 +144,18 @@ std::optional<unsigned> Program::getOrCreateDummy(const ValueDecl *PD) {
144144
It != DummyParams.end())
145145
return It->second;
146146

147-
// Create a pointer to an incomplete array of the specified elements.
148-
QualType ElemTy = PD->getType();
149-
QualType Ty =
150-
Ctx.getASTContext().getIncompleteArrayType(ElemTy, ArrayType::Normal, 0);
147+
// Create dummy descriptor.
148+
Descriptor *Desc = allocateDescriptor(PD, std::nullopt);
149+
// Allocate a block for storage.
150+
unsigned I = Globals.size();
151151

152-
if (auto Idx = createGlobal(PD, Ty, /*isStatic=*/true, /*isExtern=*/true)) {
153-
DummyParams[PD] = *Idx;
154-
return Idx;
155-
}
156-
return std::nullopt;
152+
auto *G = new (Allocator, Desc->getAllocSize())
153+
Global(getCurrentDecl(), Desc, /*IsStatic=*/true, /*IsExtern=*/false);
154+
G->block()->invokeCtor();
155+
156+
Globals.push_back(G);
157+
DummyParams[PD] = I;
158+
return I;
157159
}
158160

159161
std::optional<unsigned> Program::createGlobal(const ValueDecl *VD,

clang/test/AST/Interp/c.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,23 @@ _Static_assert(&a != 0, ""); // ref-warning {{always true}} \
4747
// expected-warning {{always true}} \
4848
// pedantic-expected-warning {{always true}} \
4949
// pedantic-expected-warning {{is a GNU extension}}
50+
_Static_assert((&c + 1) != 0, ""); // pedantic-ref-warning {{is a GNU extension}} \
51+
// pedantic-expected-warning {{is a GNU extension}}
52+
_Static_assert((&a + 100) != 0, ""); // pedantic-ref-warning {{is a GNU extension}} \
53+
// pedantic-ref-note {{100 of non-array}} \
54+
// pedantic-expected-note {{100 of non-array}} \
55+
// pedantic-expected-warning {{is a GNU extension}}
56+
_Static_assert((&a - 100) != 0, ""); // pedantic-ref-warning {{is a GNU extension}} \
57+
// pedantic-expected-warning {{is a GNU extension}} \
58+
// pedantic-ref-note {{-100 of non-array}} \
59+
// pedantic-expected-note {{-100 of non-array}}
60+
/// extern variable of a composite type.
61+
/// FIXME: The 'cast from void*' note is missing in the new interpreter.
62+
extern struct Test50S Test50;
63+
_Static_assert(&Test50 != (void*)0, ""); // ref-warning {{always true}} \
64+
// pedantic-ref-warning {{always true}} \
65+
// pedantic-ref-warning {{is a GNU extension}} \
66+
// pedantic-ref-note {{cast from 'void *' is not allowed}} \
67+
// expected-warning {{always true}} \
68+
// pedantic-expected-warning {{always true}} \
69+
// pedantic-expected-warning {{is a GNU extension}}

0 commit comments

Comments
 (0)