Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Allocation and traps

Source-level heap construction and allocating operations use checked paths. AllocError has two allocation-free nullary variants: AllocError.OutOfMemory for allocator rejection and AllocError.CapacityOverflow for impossible sizes or layouts. Constructing, copying, comparing, matching, or propagating it inside Result allocates nothing. AllocError itself is not a family on which ? operates.

Class instances, tuples, and enum values are inline and construct directly without allocation. Allocations in their component expressions keep their own Results. A fallible class initializer can make construction return Result[Class, AllocError]; class initialization defines cleanup of a partially initialized instance. String literals, scalar values, and ranges require no heap allocation.

Collections, boxes, and newly allocated dynamic text expose allocation errors. Collection displays, comprehensions, constructors, growing updates, and copy(value) return Result. Their APIs have no separate try_ naming family. Successful reservation permits subsequent insertions within that capacity without storage allocation. Failed growth preserves logical contents and order, although capacity may have increased. Failed deep copy preserves the source and destroys partial output. Exact APIs and input-transfer behavior are in collections and text.

Failure does not generally roll back argument effects, consumed generator steps, external input, or partial writes. Operations specify what is preserved and what has already been consumed. Allocation-free wrapper transport, borrowing, inline environments, generator frames, and cleanup bookkeeping do not make allocations performed by user expressions or destructors disappear. Cleanup on an allocation-error path must still handle its own fallible work. No public allocator switching or custom-memory-manager API is exposed.

value.unwrap() extracts a success payload without allocation, moving it when owned. Err or Nothing deliberately traps. Invalid indexing, absent dictionary keys in indexed access, a numeric cast to an integer type that cannot represent the value, invalid range parameters, and an empty split separator are also fatal runtime errors. Optional lookups provide recoverable absence where supported. Integer overflow and division by zero return Err from the function’s declared Result by default, or from a local checked: block. Only an explicit unchecked: suite reaches arithmetic traps from source operators, where they currently enforce its no-failure precondition. Explicitly trapping primitives such as atomic fetch operations retain their contracts. Traps terminate the process without unwinding language scopes. They are not caught by Result propagation or resource scopes.

Trap reports

A trap flushes standard output, writes its report to standard error, and exits with status one. The line gives the position of the failing operation in the form of a compile-time diagnostic, error: file:line:column: message:

error: main.plenty:7:11: index out of bounds: index 5, length 3

The position is the start of the expression that trapped: the left operand of an arithmetic operator, the type name of a cast, the indexed collection, the receiver of unwrap or split, or the range call. A trap in an imported function or a generic instance reports the position in the function’s body, in the file that defines it, not the call or instantiation. The file is relative to the source root, tools/geometry.plenty for the module tools.geometry, so an executable does not depend on the directory it was built in. Compile-time diagnostics print the full path. Reporting a trap allocates nothing.

When the failing body belongs to the embedded standard library, an additional line names the nearest non-std caller:

error: std/collections.plenty:20:10: index out of bounds: index 0, length 0
note: called from main.plenty:7:12

This caller is the actual invocation, including generic methods, indirect calls, tail calls, and nested standard-library helpers. A user callback that calls std becomes the nearest caller; a trap in the callback itself keeps its own ordinary report. A suspended std generator uses its current resume site, not its creation site. The note is omitted when there is no source caller on the current thread, such as a std function started directly as a worker. Caller context does not cross thread creation or executor submission. This is one caller note, not a stack trace; it does not change the trap message into an API-specific error.

Caller locations

The same nearest non-std caller is available to standard-library code that reports a failure without trapping. std.test assertions use it to begin their report with file:line:column: . The compiler records it as described above, so a std function needs no extra parameter and user code no annotation. Plenty code outside the standard library cannot read it: the hook, __caller_location, is rejected with `__caller_location` is reserved for the standard library. Without a source caller on the current thread the report has no position.

TrapMessage
Integer overflow in +, -, *, //, pow, or negation, inside unchecked:integer overflow
// or % by zero, inside unchecked:division by zero
Integer pow with a negative exponentnegative integer exponent
Shift count negative or not below the integer widthshift count outside integer width
Numeric cast to an integer type that cannot represent the valueinteger cast out of range
unwrap() on Nothing or Errunwrap on Nothing, unwrap on Err
List, text, range, or Bytes index out of rangeindex out of bounds: index I, length N
Absent dictionary key in d[k]dictionary key not found
A range longer than i64range length exceeds i64
Empty split separator held in a variable; a literal "" is a compile errorstring split requires a nonempty separator
A standard-library argument outside its domain, such as a Bytes range past the lengthThe operation and argument, such as Bytes.get: index is outside 0..len()

A bounds report prints the index as written, before a negative index counts from the end. A missing dictionary key is not printed. Slices never trap on their bounds: out-of-range bounds clamp as in Python.

Standard-library code raises the last kind with the statement __trap("message"), whose argument is a string literal. It reports the position of the statement in the std file, with the caller note, and never returns, so it can end a branch of a function that returns a value. Code outside the standard library is rejected with `__trap` is reserved for the standard library.

Stack overflow is reported separately and has no position. Trap sites describes how positions reach the runtime.