Nitpick Safety Systems and Error Handling
Nitpick is designed around the absolute rejection of
“Undefined Behavior” and uncontrolled crashes. It achieves
this through a defense-in-depth architecture consisting of
the Result<T> system, the
unknown sentinel state, and the mandatory
failsafe() trap.
1. The
Result<T> System
Rather than using exceptions (which obscure control flow)
or basic return codes (which are easily ignored), Nitpick
strictly enforces sticky error propagation using the
Result<T> type. Functions that might fail
should be typed to return Result<T>.
1.1 pass and
fail
To easily return from Result<T>
functions, Nitpick provides two semantic keywords: *
pass value; (Wraps the value in a successful
Result<T>) * fail errCode;
(Wraps an error code into an errored
Result<T>)
func:read_file = int32() {
if (error_occurred) { fail 404i32; }
pass 100i32;
};
2. Unwrapping Operators
When a function returns a Result<T>,
Nitpick forces the caller to explicitly handle it using one
of the following operators:
2.1 The Safe Fallback:
?
The ? operator safely unwraps a
Result. If the Result is an error,
it swallows the error and assigns a user-provided default
value instead.
// If read_file() fails, `val` becomes 0
int32:val = read_file() ? 0i32;
2.2 The Emphatic Unwrap:
?!
The ?! operator unwraps a
Result. If the Result is an error,
it immediately invokes the global failsafe()
function, passing it a custom error code provided by the
developer.
// If read_file() fails, immediately triggers failsafe(99)
int32:val = read_file() ?! 99i32;
2.3 The Explicit Cast:
raw
The raw keyword forcibly extracts the value
from a Result without checking the error flag.
This is highly unsafe but allows developers to intentionally
bypass safety checks (a “TOS keyword” that auditors can
easily grep for).
int32:val = raw read_file();
2.4 The Discard Operator:
drop
Evaluates a function but deliberately throws away the
returned Result<T> without extracting it
or checking for errors.
drop read_file();
3. The unknown
Sentinel
To handle traditional panic/UB scenarios (like division
by zero or out-of-bounds access), Nitpick defines the
unknown state. This acts similarly to
NaN in floating-point math, allowing the system
to continue operating in a degraded state rather than
immediately crashing (a requirement for fail-operational
aerospace systems).
int32:val = unknown;
3.1 The ok()
Function
You cannot pass a potentially unknown
variable into critical control paths. You must verify it
using the is unknown postfix operator, and
explicitly clear the taint using ok() before
proceeding.
if (!(val is unknown)) {
val = ok(val);
// safe to use val
}
4. The Mandatory
failsafe
Every Nitpick program must define a
failsafe function. This is the ultimate,
un-bypassable safety net (Layer 1 of the safety
architecture).
- Signature:
func:failsafe = int32(tbb32:err) { ... } - It is the only function (alongside
main) that uses theexitkeyword instead ofpass/fail. - It receives the error code that triggered the failure.
4.1 The
!!! Operator (Immediate Abort)
You can trigger the failsafe at any point in
your code using the !!! operator followed by an
error code.
if (critical_hardware_failure) {
!!! 1i32; // Immediately transfers control to failsafe(1)
}
5. Picky
Pedantry Rules (--extra-picky)
Nitpick offers an optional but highly recommended
--extra-picky flag that turns on strict
pedantic rules which default to hard compilation errors.
These rules can be parameterized: *
--extra-picky=warn-<rule> downgrades the
rule to a warning. *
--extra-picky=no-<rule> completely
disables the rule.
5.1 Literal
Suffixes (literal-suffixes)
Enforces that all integer literals provide explicit
bit-size suffixes (e.g., 42_i32 instead of
42), eliminating sizing ambiguities.
5.2 Explicit
Widening (explicit-widening)
Bans implicit widening of integer types (e.g., passing
int32 to an int64 parameter). All
widenings must be cast explicitly using as.
5.3 Ban Shadowing
(shadow)
Shadowing variables (declaring a variable with the same name as one in an outer scope) is a common source of bugs. This rule strictly bans inner scopes from redefining variables matching their parent scopes (while ignoring internal macro-generated hygiene names).
5.4 Ban Wild Memory
(wild)
Nitpick’s wild and wildx memory
qualifiers denote unchecked, unbounded pointers. While
occasionally necessary for low-level systems programming,
they bypass Nitpick’s memory safety guarantees. This rule
ensures high-level application code strictly avoids wild
pointers by rejecting wild/wildx
modifiers on variable declarations, function parameters, and
return types.