Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[clang][Interp] Add explicit dummy descriptors #68888

Merged
merged 2 commits into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions clang/lib/AST/Interp/Descriptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,13 @@ Descriptor::Descriptor(const DeclTy &D, Record *R, MetadataSize MD,
assert(Source && "Missing source");
}

Descriptor::Descriptor(const DeclTy &D, MetadataSize MD)
: Source(D), ElemSize(1), Size(ElemSize), MDSize(MD.value_or(0)),
AllocSize(Size + MDSize), ElemRecord(nullptr), IsConst(true),
IsMutable(false), IsTemporary(false), IsDummy(true) {
assert(Source && "Missing source");
}

QualType Descriptor::getType() const {
if (auto *E = asExpr())
return E->getType();
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/AST/Interp/Descriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ struct Descriptor final {
const bool IsTemporary = false;
/// Flag indicating if the block is an array.
const bool IsArray = false;
/// Flag indicating if this is a dummy descriptor.
const bool IsDummy = false;
tbaederr marked this conversation as resolved.
Show resolved Hide resolved

/// Storage management methods.
const BlockCtorFn CtorFn = nullptr;
Expand Down Expand Up @@ -137,6 +139,8 @@ struct Descriptor final {
Descriptor(const DeclTy &D, Record *R, MetadataSize MD, bool IsConst,
bool IsTemporary, bool IsMutable);

Descriptor(const DeclTy &D, MetadataSize MD);

QualType getType() const;
QualType getElemQualType() const;
SourceLocation getLocation() const;
Expand Down Expand Up @@ -190,6 +194,8 @@ struct Descriptor final {
bool isArray() const { return IsArray; }
/// Checks if the descriptor is of a record.
bool isRecord() const { return !IsArray && ElemRecord; }
/// Checks if this is a dummy descriptor.
bool isDummy() const { return IsDummy; }
};

/// Bitfield tracking the initialisation status of elements of primitive arrays.
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/AST/Interp/Interp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,10 @@ bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
return true;
}

bool CheckDummy(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
return !Ptr.isDummy();
}

bool CheckNull(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
CheckSubobjectKind CSK) {
if (!Ptr.isZero())
Expand Down Expand Up @@ -268,6 +272,8 @@ bool CheckInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
}

bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
if (!CheckDummy(S, OpPC, Ptr))
return false;
if (!CheckLive(S, OpPC, Ptr, AK_Read))
return false;
if (!CheckExtern(S, OpPC, Ptr))
Expand Down
20 changes: 14 additions & 6 deletions clang/lib/AST/Interp/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ bool CheckArray(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
/// Checks if a pointer is live and accessible.
bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
AccessKinds AK);

/// Checks if a pointer is a dummy pointer.
bool CheckDummy(InterpState &S, CodePtr OpPC, const Pointer &Ptr);

/// Checks if a pointer is null.
bool CheckNull(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
CheckSubobjectKind CSK);
Expand Down Expand Up @@ -1423,8 +1427,9 @@ bool OffsetHelper(InterpState &S, CodePtr OpPC, const T &Offset,
// Compute the largest index into the array.
T MaxIndex = T::from(Ptr.getNumElems(), Offset.bitWidth());

bool Invalid = false;
// Helper to report an invalid offset, computed as APSInt.
auto InvalidOffset = [&]() {
auto DiagInvalidOffset = [&]() -> void {
const unsigned Bits = Offset.bitWidth();
APSInt APOffset(Offset.toAPSInt().extend(Bits + 2), false);
APSInt APIndex(Index.toAPSInt().extend(Bits + 2), false);
Expand All @@ -1434,28 +1439,31 @@ bool OffsetHelper(InterpState &S, CodePtr OpPC, const T &Offset,
<< NewIndex
<< /*array*/ static_cast<int>(!Ptr.inArray())
<< static_cast<unsigned>(MaxIndex);
return false;
Invalid = true;
};

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

// If the new offset would be out of bounds, bail out.
if (Offset.isPositive() && Offset > MaxOffset)
return InvalidOffset();
DiagInvalidOffset();
} else {
// If the new offset would be negative, bail out.
if (Offset.isPositive() && Index < Offset)
return InvalidOffset();
DiagInvalidOffset();

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

if (Invalid && !Ptr.isDummy())
return false;

// Offset is valid - compute it on unsigned.
int64_t WideIndex = static_cast<int64_t>(Index);
int64_t WideOffset = static_cast<int64_t>(Offset);
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/AST/Interp/InterpBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,9 @@ static bool interp__builtin_strlen(InterpState &S, CodePtr OpPC,
if (!CheckLive(S, OpPC, StrPtr, AK_Read))
return false;

if (!CheckDummy(S, OpPC, StrPtr))
return false;

assert(StrPtr.getFieldDesc()->isPrimitiveArray());

size_t Len = 0;
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/AST/Interp/Pointer.h
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,8 @@ class Pointer {
bool isActive() const { return Base == 0 || getInlineDesc()->IsActive; }
/// Checks if a structure is a base class.
bool isBaseClass() const { return isField() && getInlineDesc()->IsBase; }
/// Checks if the pointer pointers to a dummy value.
bool isDummy() const { return getDeclDesc()->isDummy(); }

/// Checks if an object or a subfield is mutable.
bool isConst() const {
Expand Down
20 changes: 11 additions & 9 deletions clang/lib/AST/Interp/Program.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,16 +144,18 @@ std::optional<unsigned> Program::getOrCreateDummy(const ValueDecl *PD) {
It != DummyParams.end())
return It->second;

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

if (auto Idx = createGlobal(PD, Ty, /*isStatic=*/true, /*isExtern=*/true)) {
DummyParams[PD] = *Idx;
return Idx;
}
return std::nullopt;
auto *G = new (Allocator, Desc->getAllocSize())
Global(getCurrentDecl(), Desc, /*IsStatic=*/true, /*IsExtern=*/false);
G->block()->invokeCtor();

Globals.push_back(G);
DummyParams[PD] = I;
return I;
}

std::optional<unsigned> Program::createGlobal(const ValueDecl *VD,
Expand Down
20 changes: 20 additions & 0 deletions clang/test/AST/Interp/c.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,23 @@ _Static_assert(&a != 0, ""); // ref-warning {{always true}} \
// expected-warning {{always true}} \
// pedantic-expected-warning {{always true}} \
// pedantic-expected-warning {{is a GNU extension}}
_Static_assert((&c + 1) != 0, ""); // pedantic-ref-warning {{is a GNU extension}} \
// pedantic-expected-warning {{is a GNU extension}}
_Static_assert((&a + 100) != 0, ""); // pedantic-ref-warning {{is a GNU extension}} \
// pedantic-ref-note {{100 of non-array}} \
// pedantic-expected-note {{100 of non-array}} \
// pedantic-expected-warning {{is a GNU extension}}
_Static_assert((&a - 100) != 0, ""); // pedantic-ref-warning {{is a GNU extension}} \
// pedantic-expected-warning {{is a GNU extension}} \
// pedantic-ref-note {{-100 of non-array}} \
// pedantic-expected-note {{-100 of non-array}}
/// extern variable of a composite type.
/// FIXME: The 'cast from void*' note is missing in the new interpreter.
extern struct Test50S Test50;
_Static_assert(&Test50 != (void*)0, ""); // ref-warning {{always true}} \
// pedantic-ref-warning {{always true}} \
// pedantic-ref-warning {{is a GNU extension}} \
// pedantic-ref-note {{cast from 'void *' is not allowed}} \
// expected-warning {{always true}} \
// pedantic-expected-warning {{always true}} \
// pedantic-expected-warning {{is a GNU extension}}