Skip to content

v0.8.0

Choose a tag to compare

@Hindurable Hindurable released this 20 May 20:10
· 575 commits to main since this release

[0.8.0] — 2026-05-20

Added

  • Native ^^ XOR operator. Two adjacent carets lex as a single
    ^^ token (the lexer pairs them only when adjacent — ^ ^ with a
    space is still two return tokens). ^^ is a strictly-binary
    operator lowered to LLVM xor: bitwise XOR on integer operands,
    logical XOR on b operands. Float operands are a compile error
    (LLVM has no float xor). Replaces the old (a | b) - (a & b)
    identity workaround. ^ alone remains the return operator.
    Grammar (spec/grammar.ebnf) and nurlfmt updated; regression
    tests xor_op.nu + should_fail_xor_float.nu.

  • inout and sink parameter conventions (BORROW.md Phase 4,
    Option B — mutable value semantics).
    in / inout / sink are
    contextual keywords recognised only as a parameter's leading token
    (no lexer change); in is the default.
    A parameter marked inout is an exclusive mutable borrow: the
    callee mutates the caller's binding in place. inout T lowers to a
    by-address <T>* parameter — the body reads/writes the caller's
    storage with no local copy — replacing the *T-parameter and
    return-the-struct mutation idioms. The argument must be a mutable
    (: ~) binding; an inout function must be defined before it is
    called. Exclusive-access check (BORROW.md Phase 5): a binding
    passed inout must be the only argument path to its value at that
    call — passing it again, as a second inout or a plain by-value
    argument, is a warning:.
    A parameter marked sink consumes (takes ownership of) its
    argument: it lowers to an ordinary by-value parameter, and the
    borrow checker records the argument binding as moved so a later
    use is a use-after-move. sink v1 applies to Vec and other
    manually-managed handles; passing a compiler-auto-dropped value
    (owned string / slice / Drop value / struct with owned fields)
    to a sink parameter is rejected pending drop-ownership transfer.

  • Static borrow checker, on by default (BORROW.md Phases 0-3 + 6 +
    8-partial).
    A diagnostic analysis pass (disable with
    --no-borrowck) that never changes generated code — a
    borrow-clean program compiles to byte-identical IR. Closes four
    bug classes with warning: diagnostics: use-after-move (a binding
    read after its ownership moved), alias double-free (: T b a of an
    owned heap value moves a), stack-reference escape (a closure
    capturing a : ~-mutable struct by pointer that is returned,
    pushed into a container, spawned onto a thread, or assigned into a
    longer-lived binding — a region-based check), and iterator
    invalidation (mutating a container — vec_push/vec_free/… — from
    inside a ~-foreach that iterates it). Ownership + borrow rules
    documented in the new docs/MEMORY.md.

  • Tail-call optimisation in the @-fn dispatch path. gen_ret
    now flags the upcoming return-value expression as
    tail-position; gen_call snapshots + clears the flag on entry,
    so only the outermost call in the return expression is treated
    as tail (argument-evaluation recursions stay non-tail). In the
    regular @-fn dispatch path the LLVM call becomes tail call
    when (a) the flag was set, (b) rlt == fn_ret_ty so LLVM
    accepts the marker, (c) the callee is not variadic, and (d)
    gen_ret saw no pending owned-string / owned-slice / owned-
    struct-field / user-drop / defer in scope at flag-set time
    (any of those would emit drop calls between the tail call and
    ret, which LLVM would silently demote).

    Deliberately chose tail over musttail: tail is a hint
    LLVM may drop when its safety analysis can't confirm the
    rewrite (alloca-escape through an arg, etc.), so a
    misclassification only costs an optimisation. musttail is
    verifier-enforced and would fail on NURL's owning ABI where
    the same source-level signature lowers to different LLVM
    types across call sites.

    Effect: tail-recursive functions no longer blow the stack —
    compiler/tests/tco_deep_recursion.nu runs a 5_000_000-deep
    countdown in O(1) stack (~7 ms wall-clock). Trait/impl,
    closure-loaded var, and fn-pointer-parameter dispatch paths
    intentionally still emit a plain call (different shapes; not
    the deep-recursion targets TCO exists for).

    Coexists with --g DWARF emission: tools/dwarf_test.sh still
    passes all five phases.

  • DWARF Phase 6 composite-type rendering. User structs and
    generic-instantiation handles (%Vec__u8, %String, %FmtTok,
    user % Point, …) now resolve under nurlc --g to a
    !DICompositeType(tag: DW_TAG_structure_type, …) carrying one
    !DIDerivedType(tag: DW_TAG_member, …) per field — instead of
    the previous i64 placeholder. gdb ptype Point lists the fields
    with their NURL names + base types; print p renders the value
    as {x = 3, y = 7}; print p.x evaluates a single field.

    Field roster lives in the existing symbol table next to the
    per-field __idx_N__type entries — gen_struct_decl and the
    generic-instantiation emitter now also record
    <sname>__field_count and <sname>__idx_N__name. New helpers
    dbg_size_bits / dbg_align_bits / dbg_align_up compute
    LLVM-natural cumulative field offsets so the emitted
    !DIDerivedType member offsets match the actual layout
    clang/LLVM uses. Self-referential structs (a cell holding a
    pointer to itself, etc.) are safe — the composite id is interned
    in g_dbg_type_syms before the per-field recursion descends
    through dbg_type_id_for, so a back-edge returns the cached id
    instead of looping.

    Regression: compiler/tests/dwarf_struct.nu exercises the
    codegen path in the standard test corpus; tools/dwarf_test.sh
    picks up a fifth phase that drives gdb in batch mode to assert
    ptype + print + field-access over the new test. Bootstrap
    fixed point holds — non-debug IR is byte-identical.

    Closes the open Phase 6 follow-up in DWARF.md. Phase 7
    (per-instantiation source-line precision for generics) remains
    deferred.