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).

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.