rename none to null

This commit is contained in:
2026-07-21 22:59:51 +02:00
parent 1619ea98a3
commit 402871ef7a
44 changed files with 3771 additions and 3767 deletions
+20 -18
View File
@@ -93,11 +93,11 @@
- if statements (implemented). example: `if condition { ... } else if { ... } else { ... }`
- conditions must be `bool`; block-scoped locals do not escape their blocks
- lowered through new `Label` / `Br` / `Cond_Br` IR opcodes (alloca-backed locals, no phi nodes)
- conditional unwrapping for optionals (`?T`) (implemented): `if val |v| { ... } else { ... }` - unwrap `val` into `v` if it is not `none`
- conditional unwrapping for optionals (`?T`) (implemented): `if val |v| { ... } else { ... }` - unwrap `val` into `v` if it is not `null`
- single immutable binding scoped to the then-block; `v` not visible in `else` or after the `if`
- `|` lexes as a new `Pipe` token; the `.If` reuses AST `name` / HIR `local` to carry the binding (no new statement kind)
- new `Optional_Is_Some` / `Optional_Value` IR opcodes (the `Unwrap` presence-test + extract, minus the trap)
- conditional unwrapping with guard clause (implemented): `if val |v : v >= 10| { ... } else { ... }` - enter the then-block when `val` is not `none` and the guard is true
- conditional unwrapping with guard clause (implemented): `if val |v : v >= 10| { ... } else { ... }` - enter the then-block when `val` is not `null` and the guard is true
- multi-unwrap (implemented; see section below)
- while loops (implemented; operates on boolean conditions). examples:
- `while condition { ... }` - iterate while the condition is true
@@ -189,7 +189,7 @@
}
```
- disallow: `b :: undefined` since assigning undefined to something that can't change defeats the purpose
- disallow assigning `undefined` after declaration; use optionals and `none` for values that intentionally move back to an empty state
- disallow assigning `undefined` after declaration; use optionals and `null` for values that intentionally move back to an empty state
13. introduce `float` and `range` type constraints (the `int` family generalized) (implemented)
- `float` resolves a local binding to any float scalar (`f32`/`f64`) via static analysis;
@@ -351,9 +351,9 @@
`target = if …` are supported too
- value-loop: a labeled body `for/while … blk: { … }` whose early exits are
`yield :blk x` and whose body ends in an unlabeled fall-through `yield` (the value
when the loop completes). The `{T, none}` yields resolve the result to `?T`
(a pure-AST `none`-scan picks optionality; the first concrete yield fixes the element
type). E.g. `active_ent_idx :: for 0..10 |i| blk: { if (cond) yield :blk i; yield none }`
when the loop completes). The `{T, null}` yields resolve the result to `?T`
(a pure-AST `null`-scan picks optionality; the first concrete yield fixes the element
type). E.g. `active_ent_idx :: for 0..10 |i| blk: { if (cond) yield :blk i; yield null }`
resolves to `?usize`
- new `blk:` / `yield :blk` label surface adds one `label` field to the AST `Stmt`; no new
token (`blk:` is `Identifier Colon`, `:blk` is `Colon Identifier`). The parser carries a
@@ -370,7 +370,7 @@
loop. The TODO "BAD" loops (unlabeled yield from inside an `if`, an unbound labeled loop)
fall out of these naturally
- follow-ups: a branch that early-`return`s instead of yielding, unwrap-`if` as a value
source, and `none`-before-concrete typing in untyped loops are done in 20.6; labeled value
source, and `null`-before-concrete typing in untyped loops are done in 20.6; labeled value
blocks and `yield`/`break` to an outer loop are done in 20.7
20.6 value if/loop follow-ups (implemented; checker-only)
@@ -383,16 +383,16 @@
guard), each branch assigning the slot; the HIR `.If` carries the unwraps, which the existing
lowering already handles. (The simple "unwrap or fallback" case is just `orelse` —
`name :: opt orelse d` — already a plain expression.)
- untyped value loops pre-type their element from the first concrete (non-`none`) yield
- untyped value loops pre-type their element from the first concrete (non-`null`) yield
regardless of source order (a capture-scoped probe build, `value_loop_element_type`), so a
`none` yielded before any concrete value still resolves the result to `?T`
`null` yielded before any concrete value still resolves the result to `?T`
- still checker-only; no HIR/lowering change
20.7 labels — value blocks + yield/break to an outer loop (implemented; first lowering change)
- `x :: blk: { …; yield :blk v }` — a labeled value *block* (the disambiguated form of "an
if/loop at the end of a block"; an unlabeled trailing if/loop stays ambiguous and is not a
value source). `yield :blk v` exits the block with a value; every path must yield. Carries
the same `{T, none}` → `?T` typing, defer-capture, and reassignment forms as value loops
the same `{T, null}` → `?T` typing, defer-capture, and reassignment forms as value loops
- `yield :outer v` to an enclosing (non-innermost) value loop/block, plus plain `break :L` /
`continue :L` to an enclosing labeled loop
- a label now names a first-class exit target: `label` added to the HIR `Stmt` (on
@@ -406,10 +406,10 @@
`.Block` break target; not a loop, so unlabeled `break`/`continue` and `continue :blk` skip
it). The checker tracks a parallel `loop_is_loop` stack so labeled `break` reaches a loop or
block while `continue` and unlabeled `break`/`continue` reach only the innermost loop
- untyped block `none`-before-concrete typing now builds the block's leading (yield-free)
- untyped block `null`-before-concrete typing now builds the block's leading (yield-free)
statements first (a throwaway probe), so a first concrete `yield :blk` that references a
block local still resolves the result to `?T`
- deferred (`// ponytail:`): the same `none`-before-concrete typing in an untyped block (or
- deferred (`// ponytail:`): the same `null`-before-concrete typing in an untyped block (or
loop) whose concrete yield references a local declared *past* the first yield (annotate)
21. unions and tagged unions (implemented; first pass — native untagged unions only; see below)
@@ -947,6 +947,8 @@
- comptime local declarations resolve type syntax in the active interpreter state, preserving match captures
- `EnumFieldStruct` sizes its working arrays directly from the comptime `.enum |info|` capture
47. rename optional `none` to `null` (implemented)
## A word on unchecked casts
For casts that bypass safety checks, Honey provides builtin functions:
@@ -978,13 +980,13 @@ ptr := ptrcast!(addr, @u8) # integer to pointer
## A word on multi-unwrap
Unwrap multiple optionals with `and`. This **short-circuits**: if the first optional is none, subsequent expressions are not evaluated.
Unwrap multiple optionals with `and`. This **short-circuits**: if the first optional is null, subsequent expressions are not evaluated.
```
name: ?[]u8 = get_name()
age: ?u8 = get_age()
if name and age |n, a| {
# both n and a are guaranteed non-none here
# both n and a are guaranteed non-null here
print("{s} is {d} years old", n, a)
}
```
@@ -1013,7 +1015,7 @@ The `and` in multi-unwrap short-circuits left-to-right:
```
if get_name() and get_hat() |n, h| {
# get_hat() is only called if get_name() returned non-none
# get_hat() is only called if get_name() returned non-null
}
```
@@ -1242,7 +1244,7 @@ Yielding is also possible from loops with the same constraint.
# get active entity
active_ent_idx :: for 0..10 |i| blk: {
if is_active(some_entity, i) yield :blk i
yield none # fall-through: no active ent was found (this should imply a return type matching both the index value and `none`, meaning it should resolve to an optional in this case)
yield null # fall-through: no active ent was found (this should imply a return type matching both the index value and `null`, meaning it should resolve to an optional in this case)
# note that in this case, we have to use the `blk` label to yield from the correct scope.
# otherwise, the yield should return directly from the if-statement's scope (which would be incorrect in this case).
@@ -1251,13 +1253,13 @@ active_ent_idx :: for 0..10 |i| blk: {
# BAD: yield returned from if-statement, but no name binds it: should miscompile similar to unused return values from functions.
active_ent_idx :: for 0..10 |i| {
if is_active(some_entity, i) yield i # bad
yield none
yield null
}
# BAD: likewise for loops
for 0..10 |i| blk: { # bad, no name binds returned value
if is_active(some_entity, i) yield :blk i
yield none
yield null
}
```