Skip to content

Commit

Permalink
[clang][Interp] Fix lifetime diagnostics for dead records
Browse files Browse the repository at this point in the history
This used to crash the interpreter, either because we ran into the
assertion in CheckMutable() or because we accessed a Descriptor* pointer
preceding the field of a record. Those are preceded by an
InlineDescriptor though.

Differential Revision: https://reviews.llvm.org/D152132
  • Loading branch information
tbaederr committed Aug 20, 2023
1 parent 8a58f0d commit 39236e9
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 4 deletions.
3 changes: 1 addition & 2 deletions clang/lib/AST/Interp/Descriptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,9 +170,8 @@ static void moveRecord(Block *B, const std::byte *Src, std::byte *Dst,
const Descriptor *D) {
for (const auto &F : D->ElemRecord->fields()) {
auto FieldOff = F.Offset;
auto FieldDesc = F.Desc;
auto *FieldDesc = F.Desc;

*(reinterpret_cast<Descriptor **>(Dst + FieldOff) - 1) = FieldDesc;
if (auto Fn = FieldDesc->MoveFn)
Fn(B, Src + FieldOff, Dst + FieldOff, FieldDesc);
}
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Interp/InterpBlock.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ class DeadBlock final {

/// Returns a pointer to the stored data.
std::byte *data() { return B.data(); }
std::byte *rawData() { return B.rawData(); }

private:
friend class Block;
Expand Down
8 changes: 6 additions & 2 deletions clang/lib/AST/Interp/InterpState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,13 @@ void InterpState::deallocate(Block *B) {
reinterpret_cast<char *>(std::malloc(sizeof(DeadBlock) + Size));
auto *D = new (Memory) DeadBlock(DeadBlocks, B);

// Move data from one block to another.
if (Desc->MoveFn)
// Move data and metadata from the old block to the new (dead)block.
if (Desc->MoveFn) {
Desc->MoveFn(B, B->data(), D->data(), Desc);
if (Desc->getMetadataSize() > 0)
std::memcpy(D->rawData(), B->rawData(), Desc->getMetadataSize());
}

} else {
// Free storage, if necessary.
if (Desc->DtorFn)
Expand Down
24 changes: 24 additions & 0 deletions clang/test/AST/Interp/lifetimes.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify %s
// RUN: %clang_cc1 -verify=ref %s

struct Foo {
int a;
};

constexpr int dead1() { // expected-error {{never produces a constant expression}}

Foo *F2 = nullptr;
{
Foo F{12}; // expected-note 2{{declared here}}
F2 = &F;
} // Ends lifetime of F.

return F2->a; // expected-note 2{{read of variable whose lifetime has ended}} \
// ref-note {{read of object outside its lifetime is not allowed in a constant expression}}
}
static_assert(dead1() == 1, ""); // expected-error {{not an integral constant expression}} \
// expected-note {{in call to}} \
// ref-error {{not an integral constant expression}} \
// ref-note {{in call to}} \

0 comments on commit 39236e9

Please sign in to comment.