blob: f0860589b6b0cc9606a978d9aad8e7a1c88f7104 [file] [log] [blame] [view]
# Bounds Checking
For a slice or array `x`, an expression like `x[i .. j]` is invalid unless the
compiler can prove that `((0 <= i) and (i <= j) and (j <= x.length()))`. For
example, the expression `x[..]` is always valid because the implicit `i` and
`j` values are `0` and `x.length()`, and a slice or array cannot have negative
length.
Similarly, an expression like `x[i]` is invalid unless there is a compile-time
proof that `i` is in-bounds: that `((0 <= i) and (i < x.length()))`. Proofs can
involve natural bounds (e.g. if `i` has type `base.u8` then `(0 <= i)` is
trivially true since a `base.u8` is unsigned),
[refinements](/doc/glossary.md#refinement-type) (e.g. if `i` has type
`base.u32[..= 100]` then 100 is an inclusive upper bound for `i`),
[facts](/doc/note/facts.md) (e.g. an explicit prior check `if i < x.length()`)
and [interval arithmetic](/doc/note/interval-arithmetic.md) (e.g. for an
expression like `x[m + n]`, the upper bound for `m + n` is the sum of the upper
bounds of `m` and `n`).
For example, if `a` is an `array[1024] base.u8`, and `expr` is some expression
of type `base.u32`, then `a[expr & 1023]` is valid (in-bounds). The bitwise-and
means that the overall index expression is in the range `[0 ..= 1023]`,
regardless of `expr`'s range.
## Overflow Checking
Arithmetic overflow checking uses the same mechanisms as bounds checking. For
example, if `m` and `n` both have type `base.u8` (or a refinement thereof) then
the expression `m + n` also has type `base.u8`. Proving that `m + n` evaluates
to something inside the `base.u8` range, `[0 ..= 255]`, is equivalent to
proving that the expression `h[m + n]` is valid (in-bounds) for a hypothetical
`h` array of length `256`.
## `nullptr` Checking
Guarding against `nullptr` dereferences is another case of bounds checking.
Like other programming languages, `nptr T` and `ptr T` are types: nullable and
non-null pointers to some type `T`. In Wuffs, the latter is effectively a
refinement. Assuming that `nullptr` is equivalent to `0`, `ptr T` is
conceptually just `(nptr T)[1 ..= UINTPTR_MAX]`, although that's not actually
valid syntax. Similarly, if `expr` is a pointer-typed expression then checking
that `expr` is not `nullptr` is equivalent to checking that `expr` is in the
range `[1 ..= UINTPTR_MAX]`.