Skip to content

Directives and markers

PPy adds two kinds of annotation: directives, which are decorators on a function, and markers, which are type aliases. Both come from the ppy package.

Directives

Each directive works bare (@ppy.pure) and called (@ppy.pure()). A directive is a contract the compiler verifies. The compiler does not trust it as a hint.

directive meaning
@ppy.pure no observable effects: no I/O, no global or nonlocal writes, no mutation of arguments. Local allocation and mutation of locally created values are fine. Violations are E1601/E1602.
@ppy.opt(n) per-function optimization level 0–3, overriding the project default.
@ppy.native lower to LLVM. require=True makes any fallback to the Python body an error (E1702).
@ppy.parallel parallelize the eligible loop. require=True makes failure an error (E1701).
@ppy.jit specialize at runtime on the argument classes actually seen.
@ppy.specialize ahead-of-time specialization on declared value classes.
@ppy.inline / @ppy.noinline force or forbid inlining into callers.
@ppy.fastmath permit floating-point reassociation in this function; without it, reduction order is preserved bit-for-bit.
@ppy.jax stage this function for build-time StableHLO export (a plain @jax.jit decorator marks it too).
@ppy.dynamic / with ppy.dynamic(): an explicit boundary inside which dynamic features are allowed. Each value it produces is Dynamic, and stays Dynamic through attribute hops and arithmetic until a ppy.check[T] clears it.
@ppy.reflective the function's annotations are runtime-visible state, as written: ppy convert/ppy migrate will not add to them or rely on rewriting them.

A typo in a directive name is E1205, reported with a suggestion.

Markers

Markers are ordinary Annotated aliases from ppy:

marker meaning
i8 i16 i32 i64 u8 u16 u32 u64 fixed-width integer contract. A value provably outside the range is E1401; a check the contract mode forbids is E1402.
f16 f32 f64 floating-point width.
bf16 bfloat16 element format, distinct from IEEE float16.
Tensor[dtype, shape] a common tensor contract, including dtype and dimensions; storage belongs to the selected backend. See Common tensors.
Buffer[T] a borrowed writable buffer (memoryview over array.array), zero-copy in and out of native code. T may be int, float, or ppy.i8/ppy.u8 for one byte per element.
Array[T], Vector[T] contiguous numeric containers with a known element type.
Range(lo, hi) an integer refinement the checker propagates.
Dynamic an explicit Python-dynamic boundary value. Entering is free; leaving is not: Dynamic -> Dynamic flows freely, but Dynamic -> int is E1508 until a ppy.check[T] validates it. Any at runtime.
Length(n), Shape(...), DType("f32"), Contiguous, NoAlias container refinements; Shape/DType are what makes a @ppy.jax function exportable.
Owned[T], Borrowed[T], Mut[T] how a parameter holds its value. Borrowed is read for the call and no longer: not returned (E1611), not stored where it outlives the call (E1612), not written (E1613). Mut is a borrow the function may write through. Owned hands the value over. A Buffer[T] is Borrowed unless the program says otherwise.

Examples: Basics, Effects and contracts, Buffers and JIT.