In void*-to-ptr casts, the type of the pointed-to object in the source
operand needs to be compared to the target pointee type.
If a block was created for a `new`/`new[]`/`std::allocator` expression,
then a pointer needs to be stripped from the type of the expression
(which points to the single-object allocation or first element of the
allocation) to get the former.
`Descriptor::getType` did not do this and `Descriptor::getDataType`
returns an array type for array allocations. Therefore this introduces a
new function `Descriptor::getDataElemType` with the same behavior as
`Descriptor::getDataType`, except that it always produces the element
type in the array case and avoids the need for an `ASTContext`
reference. Make `Pointer::getType` use this function instead.
Fixes#174131
Our internal representation for records does not include things like
alignment of the fields or padding bits, so we can't only rely on those
values, not even for just comparing two pointers.
Try to look up the offsets from the `ASTRecordLayout` instead.
Fixes https://github.com/llvm/llvm-project/issues/172165
---------
Co-authored-by: Shafik Yaghmour <shafik.yaghmour@intel.com>
Save them as a pointer intead of using a shared_ptr. This we we can use
the pointer integer value to differentiate the "no initmap yet" and "all
values initialzed" cases.
This regresses one test case in const-eval.c, but as it turns out, that
only worked coincidentally before.
For large primitive arrays, avoid creating a new `Pointer` for every
element (via `Pointer::isElementInitialized()`) or avoid iterating over
the array altogether (via `Pointer::allElementsInitialized()`).
Now that we don't have the PointeeStorage pointer anymore, it's simpler
to access the members of the anonymous union directly instead of using
asBlockPointer(), etc.
This reverts commit 9642aadf7064192164d1687378d28d6bda1978c9.
Since elem() only works on primitive arrays anyway, we don't have to do
the isArrayRoot() check at all.
This has been a long-standing problem, but we didn't use to call the
destructors of items on the stack unless we explicitly `pop()` or
`discard()` them.
When interpretation was interrupted midway-through (because something
failed), we left `Pointer`s on the stack. Since all `Block`s track what
`Pointer`s point to them (via a doubly-linked list in the `Pointer`),
that meant we potentially leave deallocated pointers in that list. We
used to work around this by removing the `Pointer` from the list before
deallocating the block.
However, we now want to track pointers to global blocks as well, which
poses a problem since the blocks are never deallocated and thus those
pointers are always left dangling.
I've tried a few different approaches to fixing this but in the end I
just gave up on the idea of never knowing what items are in the stack.
We already have an `ItemTypes` vector that we use for debugging
assertions. This patch simply enables this vector unconditionally and
uses it in the abort case to properly `discard()` all elements from the
stack. That's a little sad IMO but I don't know of another way of
solving this problem.
As expected, this is a slight hit to compile times:
https://llvm-compile-time-tracker.com/compare.php?from=574d0a92060bf4808776b7a0239ffe91a092b15d&to=0317105f559093cfb909bfb01857a6b837991940&stat=instructions:u
This way, we can check a single uint8_t for != 0 to know whether this
block is accessible or not. If not, we still need to figure out why not
and diagnose appropriately of course.
To initialize all elements of a primitive array at once. This saves us
from creating the InitMap just to destroy it again after all elements
have been initialized.
As a way of writing atIndex(I).deref<T>(), which creates an intermediate
Pointer, which in turn adds (and removes) that pointer from the pointer
list of the Block. This way we can avoid that.
For mutable and const fields, we have two bits in InlineDescriptor,
which both get inherited down the hierarchy. When a field is both const
and mutable, we CAN read from it if it is a mutable-in-const field, but
we _can't_ read from it if it is a const-in-mutable field. We need
another bit to distinguish the two cases.
In the attached test case, one pointer points to the `Derived` class and
one to `Base`, but they should compare equal. They didn't because those
two bases are saved at different offsets in the block. Use
`computeOffsetForComparison` not just for unions and fix it to work in
the more general cases.
For
```c++
struct S {
constexpr S(int=0) : i(1) {}
int i;
};
constexpr volatile S vs;
```
reading from `vs.i` is not allowed, even though `i` is not volatile
qualified. Propagate the IsVolatile bit down the hierarchy, so we know
reading from `vs.i` is a volatile read.
Fix comparing type id pointers, add mor info when print()ing them, use
the most derived type in GetTypeidPtr() and the canonically unqualified
type when we know the type statically.
The Pointer class already has the capability to be a function pointer,
but we still classifed function pointers as PT_FnPtr/FunctionPointer.
This means when converting from a Pointer to a FunctionPointer, we lost
the information of what the original Pointer pointed to.
This issue is very convoluted, but in essence, in the new version:
For a Pointer P that points to the root of a multidimensional, primitive
array:
`P.narrow()` does nothing.
`P.atIndex(0)` points `P[0]`
`P.atIndex(0).atIndex(0)` is the same as `P.atIndex(0)` (as before)
`P.atIndex(0).narrow().atIndex(0)` points to `P[0][0]`
`P.atIndex(0).narrow().narrow()` is the same as `P.atIndex(0).narrow()`.
Make lifetime management more explicit. We're only using this for
CXXPseudoDestructorExprs for now but we need this to handle
std::construct_at/placement-new after destructor calls later anyway.
Add it as another kind of pointer, saving both a `Type*` for the result
of the typeid() expression as well as one for the type of the typeid
expression.