Skip to main content

GcHeader

Struct GcHeader 

Source
pub struct GcHeader { /* private fields */ }
Expand description

Header prepended to every GC allocation (§12.2).

Layout is #[repr(C)] and the payload follows this header in the same allocation, at GcHeader::payload_offset_for bytes from the header’s address — not necessarily at size_of::<GcHeader>(), because an over-aligned payload is padded forward. The header is addressable as *mut GcHeader and the payload is reached via GcHeader::payload.

The fields are private: the allocator (Heap::alloc_raw) is the only constructor, so an initialized header is the only kind that exists, and payload_offset cannot disagree with the address the allocator handed to the payload initializer.

Sixteen bytes, and every field in them has a reader on a hot path (ADR-109). This prefixes every allocation in the language, so a field here is a tax on every object a program makes — and once #[repr(C)] padding is counted, a four-byte field costs eight. Adding a field here is not a local decision — it moves page::MIN_BLOCK, the whole size-class ladder, and the immediate generated code folds to reach a payload, so it owes an ABI bump and an ADR.

Implementations§

Source§

impl GcHeader

Source

pub const DESCRIPTOR_OFFSET: usize

Where the descriptor pointer sits, relative to the header’s address.

Generated code reads it: an Inst::ExtractScalar proves the object’s type inline (ADR-102) — one load from here, one compare against the scalar descriptor’s address — rather than calling praxis_int_load and letting the wrapper prove it. The check is what makes the folded payload offset below the offset the allocator actually used, and it is what keeps a praxis check-clean program extracting an Int from a Unit a refusal rather than an out-of-bounds read.

Exported from here, derived with offset_of!, because ADR-039 decision 1 made the fields private to this module: the backend cannot reach for the offset itself, and the alternative — writing 0 in the backend — is exactly the re-derived literal that decision exists to prevent. payload_offset_for is the same idea one step further along.

Source

pub const PAYLOAD_OFFSET_FIELD_OFFSET: usize

Where the recorded payload displacement sits, relative to the header’s address (ADR-119).

Read by nothing in generated code and written by one thing: the inline claim sequence, which lays out a header itself. It is a u16, and what it must be handed is what GcHeader::payload_offset_for answered for the descriptor being stored beside it — the same value Heap::occupy writes, from the same call. ADR-039 decision 1 is still the authority; this is a second transcription of its answer, which is why InlineClaimSite carries the offset and the value together rather than letting a caller pair them.

Source

pub const HEAP_ID_OFFSET: usize

Where the owning HeapId sits, relative to the header’s address (ADR-119).

The provenance word ADR-039 decision 2 made the mark phase’s first read. Generated code writes it — with the id it loaded out of the live Heap it claimed the block from, never a compile-time constant: there is no heap at compile time, and a debugger session replaces its Jit while keeping its Runtime ([crate::GcConst]’s reason, one field along).

Source

pub const fn payload_offset_for(payload_align: usize) -> usize

Where the payload begins, relative to the header’s address, for a payload with the given alignment.

This is the object-layout calculation: Heap::alloc_raw uses it to place the payload, payload_offset records what it returned, and generated code calls it to reach a payload directly. const so codegen can fold it into an immediate.

§Panics

Panics if payload_align is not a power of two.

Source

pub fn descriptor(&self) -> &'static TypeDescriptor

The descriptor describing this object’s payload (§11.4).

Descriptors are always 'static (built-in constants or compiler-emitted statics), so the returned lifetime is unconstrained.

§Panics

Panics if the header has been poisoned by the sweep. Callers that may hold a stale reference must check GcHeader::is_poisoned first; the collector does this via GcHeader::heap_id.

Source

pub fn payload<T>(&self) -> *mut T

Pointer to this header’s payload bytes.

The caller is responsible for knowing the payload type (via the descriptor); this is the low-level escape hatch used by descriptor callbacks and typed accessors.

Source

pub fn heap_id(&self) -> Option<HeapId>

The heap that owns this allocation, or None if the header is poisoned.

Source

pub fn is_poisoned(&self) -> bool

Whether this header’s storage has been swept.

A poisoned header is not an object: its payload has been finalized and its bytes may be reused. Reading anything but this predicate off it is a bug.

“May be reused” is why this predicate has a shelf life. It answers “has this block been reclaimed” only until the allocator reissues the block and writes a fresh header over the poison. The collector’s weak arm (crate::debug::DebugFrameStackHeader::clear_reclaimed, ADR-106) is the one caller that depends on that, and it runs inside the collection — after the sweep and before any allocation — for exactly this reason.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.