Regions
Regions provide a fixed-size, statically allocated block of memory that can
be used as an allocation context for new.
Regions are represented at runtime as a first-class Region handle value. A
Region value may be passed to functions, stored in structs, and exported.
Notes#
Supported forms:
- Parsing and type-checking of:
const region <name>: u8[N];with <name> { ... }with <bytes> { ... }/with(<bytes>) { ... }(anonymous region for the block)with <bytes> from <region> { ... }with <bytes> from <region>[<start>..] { ... }with <bytes> from <region>[<start>..<end>] { ... }Regionis a primitive handle type:const region name: u8[N];bindsnameas aRegionvalue,Regionvalues may be passed and stored (including in struct fields),with <name> { ... }accepts anyRegion-typed binding (including function parameters and locals).- Inside a
with <region> { ... }block,newallocations for non-opaquestructvalues allocate from the active region instead of the heap. - Within the dynamic extent of a
with <region> { ... }block (including calls performed while the block is active), raw allocations viastd::runtime::mem::allocallocate from the active region (8-byte aligned). - Region allocation overflow traps at runtime.
Limitations (Supported forms):
- The region backing store is currently restricted to
u8[N](a fixed-size byte array type annotation). - Hosted native lowering currently keeps the active-region selector
process-global, and the backing bytes/cursor generated for each anonymous
with <bytes>site are static. Overlappingwithdynamic extents on multiple OS threads are therefore unsupported, even when the source sites or named regions differ. Programs must not enterwithconcurrently from task workers; use thread-safe heap/runtime operations or serialize the whole region extent. A future backend change must make both the selector and anonymous storage invocation-local or thread-local before this restriction can be removed. - Only the existing
newsubset is affected (non-opaquestructallocations that produce&Struct). - Region-backed
newallocations are still reference-counted: - last-release runs
drop(when defined), - but the backing bytes are not freed (region memory is reclaimed only by reusing the region cursor, as described below).
Syntax#
Region handle type#
Region is a primitive value type representing a region allocation context.
Conceptually, a Region value contains:
- a base pointer to the backing bytes,
- a pointer to a mutable cursor cell (shared by copies of the handle), and
- a byte limit used for overflow checking.
Copying a Region value copies the handle; copies refer to the same backing
store and cursor.
Declaring a region#
A region declaration has the surface form:
const region region_buf: u8[1024];
The backing-size expression may also use literal arithmetic that folds at parse time:
const region region_buf: u8[1024 * 1024];
Rules:
const regionis a declaration form (it is not a type).- A region declaration has no initializer.
- The type annotation specifies the region backing size and must be a fixed
byte array type:
u8[N]. - In the current parser subset,
Nmay be an integer literal or a literal-only integer arithmetic expression using+,-,*,/,%, and parentheses. - In the current region subset, that expression must fold to a concrete byte count at parse/type-check time.
- The declared name is bound as a
Regionvalue.
Using a region: with#
with establishes a region allocation context for the enclosed block.
1) Bind an existing region#
with <region> { ... } activates a named region binding:
struct Frame { x: int }
fn main () -> int {
const region region_buf: u8[1024];
with region_buf {
let p: &Frame = new Frame{ x: 1 };
// ...
}
return 0;
}
The <region> name may refer to any Region-typed binding, including a region
parameter passed to a function:
struct Frame { x: int }
fn alloc_in (r: Region) -> int {
with r {
let p: &Frame = new Frame{ x: 1 };
return p.x;
}
}
2) Use an anonymous region with an explicit byte budget#
with <bytes> { ... } (or with(<bytes>) { ... }) creates an anonymous region
backed by <bytes> writable bytes and activates it for the block:
struct Frame { x: int }
fn main () -> int {
with 1024 {
let p: &Frame = new Frame{ x: 1 };
// ...
}
return 0;
}
Rules (Supported forms):
<bytes>must be a positive integer literal.
3) Use a slice of an existing region (from)#
with <bytes> from <region> { ... } creates an anonymous region backed by the
first <bytes> bytes of <region>:
struct Frame { x: int }
fn main () -> int {
const region region_buf: u8[2048];
with 1024 from region_buf {
let p: &Frame = new Frame{ x: 1 };
// ...
}
return 0;
}
You may also specify a byte slice of the source region:
with 1024 from region_buf[64..] {
// uses bytes 64..(64 + 1024) of `region_buf`
}
with 1024 from region_buf[64..1088] {
// uses bytes 64..1088 of `region_buf`
}
Rules (Supported forms):
<bytes>must be a positive integer literal.<region>must name aRegionvalue that has a compile-time-known backing size in the Supported forms (for example aconst regiondeclaration).- Slice bounds use byte offsets (the region backing store is
u8[N]). <start>/<end>must be non-negative integer literals.- When an explicit
<end>is present, it is exclusive ([start..end]). - The
fromslice must contain at least<bytes>writable bytes: with <bytes> from r { ... }requires<bytes> <= sizeof(r).with <bytes> from r[start..end] { ... }requires<bytes> <= end - start.with <bytes> from r[start..] { ... }requires<bytes> <= sizeof(r) - start.
Semantics#
Region-backed new#
Within a with <region> { ... } block:
- any
newallocation performed by the compiler’snewlowering uses the active region as its backing store, - allocations are 8-byte aligned in the Supported forms,
- if the region does not have enough remaining space, the program traps.
Outside of a with block, new uses the current heap model described in
memory model.
Region-backed raw allocation (std::runtime::mem::alloc)#
Within the dynamic extent of a with <region> { ... } block (including calls
performed while the block is active):
std::runtime::mem::alloc(n)allocates ann-byte payload from the active region (8-byte aligned) and reserves an additional 8-byte header immediately before the returned pointer (used by the runtime to distinguish region-backed and heap-backed pointers and to record the allocation size),- if the region does not have enough remaining space, the program traps.
Implication for with <bytes> limits: each alloc(n) consumes at least
n + 8 bytes of region capacity (plus any alignment padding from 8-byte
alignment).
Region-backed raw allocations are bump-allocated. In the current runtime model:
std::runtime::mem::freeis a no-op for region-backed pointers,std::runtime::mem::reallocreallocates by allocating a new region block and copying bytes (it never calls libcreallocon a region-backed pointer).
Nested with#
Nested with blocks use the innermost active region:
with a {
with b {
// `new` uses region `b` here.
}
}
Reclaiming Region Memory#
Regions are bump allocators: each allocation advances a cursor within the backing byte buffer.
Because region-backed new allocations are still RC-managed in the current
subset and do not free backing bytes on last-release, reclaiming region memory
requires resetting the region cursor so the backing bytes can be reused.
Current behavior:
with <region> { ... }activates the region but does not reset its cursor.- allocations across multiple
with <region>blocks accumulate and can eventually overflow and trap. with <bytes> { ... }creates an anonymous region and resets its cursor to0on entry so repeated execution of the block starts from an empty region.with <bytes> from <region>[...] { ... }creates an anonymous region backed by a subrange of<region>and resets its cursor to the slice start on entry.
Important limitation:
- The compiler does not yet enforce “region allocations must not escape the
withblock”. Because anonymous-region cursors are reset on entry, code must treat pointers/&Structvalues allocated insidewith <bytes> { ... }andwith <bytes> from ... { ... }as block-scoped.
Exports#
Region declarations may be exported and imported like other top-level bindings:
export const region global_region_buf: u8[4096];
Exporting a region exports a Region handle that refers to the same backing
bytes and cursor cell. Importing a region binds a Region value that may be
used with with like a locally declared region.
Source repository · Edit this page · View Markdown