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
+4 -4
View File
@@ -36,7 +36,7 @@ roadmap and milestone history.
- `ptrcast!(T, ptr)` as a first-pass pointer-child retype that preserves optionality, pointer kind, mutability, and sentinel shape
- UTF-8 string literals as immutable pointers to static zero-terminated byte arrays, plus raw backtick multiline strings
- narrow immutable zero-terminated byte pointer/slice conversion to `*c_char` / `?*c_char` without general `u8`/`c_char` interchange
- optionals with `none`, `orelse`, postfix `?`, conditional unwraps, guarded unwraps, and left-to-right short-circuiting multi-unwraps
- optionals with `null`, `orelse`, postfix `?`, conditional unwraps, guarded unwraps, and left-to-right short-circuiting multi-unwraps
- nominal distinct types with exact backing construction, native enums with optional explicit integer backing and explicit backing-to-scalar casts, contextual enum literals, and imported C enums as target-backed integer aliases
- source-order native structs, opaque nominal records with `Name :: opaque`, complete `c_struct { ... }`, keyed record literals, native untagged unions, and native tagged unions `union(Enum)` / `union(enum)`
- named native struct fields may declare defaults with `field T = expression`; keyed literals use defaults for omitted fields and explicit initializers override them
@@ -210,8 +210,8 @@ exactly once. Bare functions named `memcopy` or `memset` remain ordinary user fu
- forced typed comptime expressions such as `$sum(1, 2)`, `$Point { x = 1, y = 2 }`, and comptime value blocks such as `${ yield 4 }`
- comptime execution for bodyful Brolang functions with mutable locals, loops, `defer`, `match`, `try`/`catch`, exact type `==`/`!=`, pointer/slice storage mutation, pointer captures, and calls through comptime-known function values; `undefined` storage may be initialized at comptime, but remaining poison cannot be observed
- comptime type factories such as `Box func($T type) type { return struct { value T } }`; calls like `Box(i32)` are concrete nominal types and may appear anywhere a type is expected
- `struct_type!(layout, names, types, defaults)` constructs a nominal record type from comptime fixed arrays or tuples and is valid in every type position; layout is `.auto` or `.c`, bare `none` means a required field, and `some!(none)` installs an optional `none` default
- `some!(value)` explicitly constructs the present branch of an expected optional, including nested optionals where `some!(none)` differs from outer `none`
- `struct_type!(layout, names, types, defaults)` constructs a nominal record type from comptime fixed arrays or tuples and is valid in every type position; layout is `.auto` or `.c`, bare `null` means a required field, and `some!(null)` installs an optional `null` default
- `some!(value)` explicitly constructs the present branch of an expected optional, including nested optionals where `some!(null)` differs from outer `null`
- tuple types are unnamed-field structs (`struct { i32, []u8 }`), tuple values use `{1, "bro"}` / `{1,}` / `{}`, and fields use canonical numeric names such as `.0`
- anonymous keyed records use `{x = 1, name = "bro"}`; without context their declaration-ordered names and inferred value types form a structurally interned record type, while a record context applies that type's coercions and field defaults; `{}` remains an empty tuple without context and constructs an empty contextual record when a record is expected
- `typeinfo!`, `field!`, `compile_error!`, and semantic `expand for` provide compile-time record and enum reflection and heterogeneous static expansion without runtime metadata; enum reflection exposes declaration-ordered fields, reflected aggregates remain persistent compile-time values, and expand-loop `break` / `continue` must be selected entirely at comptime
@@ -236,7 +236,7 @@ exactly once. Bare functions named `memcopy` or `memset` remain ordinary user fu
- root `std` re-exports `ArrayList(T)` while its operations remain in `std/arraylist`
- `std/mem` generic slice equality, allocator contract with raw byte operations, typed `empty` / `alloc` / `realloc` / `free`, overflow checks, zero-sized-type support, and failure-preserving reallocation
- `std/arraylist` generic `ArrayList(T)` with direct `items` slice access, explicit capacity, allocator ownership, fallible reserve/append, clear, and deinit
- `std/meta` reflection records plus `EnumFieldStruct(E, Field, default ?Field)`, implemented with `struct_type!`; it produces a record with one field per native enum member in declaration order, where outer `none` means no field default
- `std/meta` reflection records plus `EnumFieldStruct(E, Field, default ?Field)`, implemented with `struct_type!`; it produces a record with one field per native enum member in declaration order, where outer `null` means no field default
- `std/io` explicit `Io` capabilities, provider-bound `Reader`/`Writer` handles, existing-file open/close operations, allocation-free `write_all`, and comptime-expanded writer-first `print`; formatting supports natural `{}`, byte `{s}`, decimal `{d}`, integer `{b}` / `{o}` / `{x}` / `{X}`, byte-character `{c}`, scientific float `{e}`, and `{{` / `}}`, with malformed formats and incompatible tuple fields rejected at comptime
- entry points are either `main func() ...` or `main func(init process.Init) ...`; `std/process.Init` carries startup capabilities, currently only `io`, while the system provider remains hidden inside `std/io`
- `std/debug.print` is an allocation-free, failure-ignoring stderr escape hatch independent of `process.Init`
+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
}
```
+1 -1
View File
@@ -78,7 +78,7 @@ Expr_Kind :: enum u8 {
String,
Bool,
Array,
None,
Null,
Undefined,
Inference_Hole,
Type,
+43 -43
View File
@@ -81,7 +81,7 @@ Yield_Target :: struct {
label: symbol.Id,
slot: hir.Local_Id,
slot_type: types.Type,
// True when the loop also yields `none` (a `{T, none}` set `?T`); set from a
// True when the loop also yields `null` (a `{T, null}` set `?T`); set from a
// pure-AST scan, used to pick the slot's element type on the first concrete yield.
result_optional: bool,
// `len(defers)` when this target's body began; a `yield :label` flushes defers down
@@ -394,7 +394,7 @@ block_reads_name :: proc(checker: ^Checker, statements: []ast.Stmt_Id, name: sym
case .Add, .Sub, .Mul, .Div, .Bit_And, .Bit_Or, .Bit_Xor, .Shift_Left, .Shift_Right,
.Shift_Left_Saturating, .Index, .Orelse, .Eq, .Ne, .Lt, .Le, .Gt, .Ge, .And, .Or, .Range:
append(&expr_stack, expr.left, expr.right)
case .Invalid, .Integer, .Float, .String, .Bool, .None, .Undefined, .Inference_Hole,
case .Invalid, .Integer, .Float, .String, .Bool, .Null, .Undefined, .Inference_Hole,
.Type, .Name, .Function_Literal, .Anonymous_Struct_Type:
}
}
@@ -639,9 +639,9 @@ build_static_value :: proc(checker: ^Checker, value: Ct_Value, span: source.Span
diagnostic=source.INVALID_DIAGNOSTIC,
})
}
if value.kind == .None {
if value.kind == .Null {
return add_hir_expr(checker, hir.Expr{
kind=.None, span=span, type=value.type,
kind=.Null, span=span, type=value.type,
target=hir.INVALID_REF, left=hir.INVALID_EXPR, right=hir.INVALID_EXPR,
diagnostic=source.INVALID_DIAGNOSTIC,
})
@@ -2797,7 +2797,7 @@ infer_call_comptime_values :: proc(
for value_bound in bound {
all_bound = all_bound && value_bound
}
// Concrete arguments bind first. Numeric constants and `none` are contextual and
// Concrete arguments bind first. Numeric constants and `null` are contextual and
// therefore only contribute after stronger evidence has had a chance to bind the
// parameter type.
weak_passes := [2]bool{false, true}
@@ -2811,8 +2811,8 @@ infer_call_comptime_values :: proc(
continue
}
arg_expr := checker.ast_module.exprs[arg_id]
is_none := arg_expr.kind == .None
is_weak := is_numeric_constant_expr(checker, arg_id) || is_none
is_null := arg_expr.kind == .Null
is_weak := is_numeric_constant_expr(checker, arg_id) || is_null
if is_weak != weak {
continue
}
@@ -2836,7 +2836,7 @@ infer_call_comptime_values :: proc(
}
}
actual := actual_args[param_index]
if is_none {
if is_null {
previous := checker.current_comptime_values
checker.current_comptime_values = values
contextual := type_from_syntax(
@@ -3506,7 +3506,7 @@ mark_expr_imports_used :: proc(checker: ^Checker, expr_id: ast.Expr_Id, file: as
case .Add, .Sub, .Mul, .Div, .Bit_And, .Bit_Or, .Bit_Xor, .Shift_Left, .Shift_Right,
.Shift_Left_Saturating, .Index, .Orelse, .Eq, .Ne, .Lt, .Le, .Gt, .Ge, .And, .Or, .Range:
append(&stack, expr.left, expr.right)
case .Invalid, .Integer, .Float, .String, .Bool, .None, .Undefined, .Inference_Hole, .Type, .Name, .Anonymous_Struct_Type:
case .Invalid, .Integer, .Float, .String, .Bool, .Null, .Undefined, .Inference_Hole, .Type, .Name, .Anonymous_Struct_Type:
}
}
}
@@ -4585,10 +4585,10 @@ infer_compound_expr :: proc(
case .Eq, .Ne, .Lt, .Le, .Gt, .Ge, .And, .Or:
left_expr := checker.ast_module.exprs[expr.left]
right_expr := checker.ast_module.exprs[expr.right]
if left_expr.kind == .None && right_expr.kind != .None {
if left_expr.kind == .Null && right_expr.kind != .Null {
right := infer_nested_expr(checker, expr.right, locals, pkg, file, demanded, local_types)
_ = infer_nested_expr(checker, expr.left, locals, pkg, file, demanded, local_types, right)
} else if right_expr.kind == .None && left_expr.kind != .None {
} else if right_expr.kind == .Null && left_expr.kind != .Null {
left := infer_nested_expr(checker, expr.left, locals, pkg, file, demanded, local_types)
_ = infer_nested_expr(checker, expr.right, locals, pkg, file, demanded, local_types, left)
} else {
@@ -4629,7 +4629,7 @@ infer_compound_expr :: proc(
element = types.I64
}
return types.array(store, element, u64(len(expr.args)), false)
case .None:
case .Null:
return expected if types.is_optional(expected, store) else types.INVALID
case .Undefined:
return types.INVALID
@@ -4928,7 +4928,7 @@ infer_expr :: proc(
case .Float:
last = types.F64
_ = pop(&stack)
case .String, .Array, .None, .Undefined, .Address, .Deref, .Index, .Slice,
case .String, .Array, .Null, .Undefined, .Address, .Deref, .Index, .Slice,
.Field, .Unwrap, .Orelse, .Try, .Catch, .Struct_Literal, .Keyed, .Enum_Literal, .Cast,
.Comptime, .Bool, .Not, .Bit_Not, .Bit_And, .Bit_Or, .Bit_Xor, .Shift_Left,
.Shift_Right, .Shift_Left_Saturating, .Eq, .Ne, .Lt, .Le, .Gt, .Ge, .And, .Or, .Range:
@@ -7773,13 +7773,13 @@ build_compound_expr :: proc(
target=hir.INVALID_REF, left=hir.INVALID_EXPR, right=hir.INVALID_EXPR,
diagnostic=source.INVALID_DIAGNOSTIC,
})
case .None:
case .Null:
if !types.is_optional(expected, store) {
id := source.add(checker.diagnostics, expr.span, "'none' requires an optional context")
id := source.add(checker.diagnostics, expr.span, "'null' requires an optional context")
return invalid_hir_expr(checker, expr.span, id, expected)
}
return add_hir_expr(checker, hir.Expr{
kind=.None, span=expr.span, type=expected, target=hir.INVALID_REF,
kind=.Null, span=expr.span, type=expected, target=hir.INVALID_REF,
left=hir.INVALID_EXPR, right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
})
case .Undefined:
@@ -8311,7 +8311,7 @@ build_compound_expr :: proc(
case .Eq, .Ne, .Lt, .Le, .Gt, .Ge:
// Contextualize literals whose type comes from their peer. This covers
// integer and enum literals as well as optional presence tests such as
// `value == none` and `none != value`.
// `value == null` and `null != value`.
left_const := eval_constant(checker, expr.left)
right_const := eval_constant(checker, expr.right)
left, right: hir.Expr_Id
@@ -8319,10 +8319,10 @@ build_compound_expr :: proc(
right_expr := checker.ast_module.exprs[expr.right]
left_numeric_const := left_const.kind == .Value || is_float_constant_expr(checker, expr.left)
right_numeric_const := right_const.kind == .Value || is_float_constant_expr(checker, expr.right)
if right_expr.kind == .None && left_expr.kind != .None {
if right_expr.kind == .Null && left_expr.kind != .Null {
left = build_nested_expr(checker, expr.left, locals, global_reads, calls, types.INVALID, pkg, file)
right = build_nested_expr(checker, expr.right, locals, global_reads, calls, checker.module.exprs[left].type, pkg, file)
} else if left_expr.kind == .None && right_expr.kind != .None {
} else if left_expr.kind == .Null && right_expr.kind != .Null {
right = build_nested_expr(checker, expr.right, locals, global_reads, calls, types.INVALID, pkg, file)
left = build_nested_expr(checker, expr.left, locals, global_reads, calls, checker.module.exprs[right].type, pkg, file)
} else if right_expr.kind == .Enum_Literal && left_expr.kind != .Enum_Literal {
@@ -8349,10 +8349,10 @@ build_compound_expr :: proc(
left_type := checker.module.exprs[left].type
right_type := checker.module.exprs[right].type
operand_type := types.INVALID
if left_expr.kind == .None || right_expr.kind == .None {
if left_expr.kind == .Null || right_expr.kind == .Null {
if expr.kind != .Eq && expr.kind != .Ne ||
!types.is_optional(left_type, store) || !types.equal(left_type, right_type) {
id := source.add(checker.diagnostics, expr.span, "'none' only supports '==' and '!=' with an optional value")
id := source.add(checker.diagnostics, expr.span, "'null' only supports '==' and '!=' with an optional value")
return invalid_hir_expr(checker, expr.span, id, types.BOOL)
}
operand_type = left_type
@@ -8703,7 +8703,7 @@ build_expr :: proc(
continue
}
switch expr.kind {
case .String, .Array, .None, .Undefined, .Address, .Deref, .Index, .Slice,
case .String, .Array, .Null, .Undefined, .Address, .Deref, .Index, .Slice,
.Field, .Unwrap, .Orelse, .Try, .Catch, .Struct_Literal, .Keyed,
.Bool, .Cast, .Comptime, .Not, .Bit_Not, .Bit_And, .Bit_Or, .Bit_Xor, .Shift_Left,
.Shift_Right, .Shift_Left_Saturating, .Eq, .Ne, .Lt, .Le, .Gt, .Ge, .And, .Or, .Range,
@@ -12567,24 +12567,24 @@ build_value_match :: proc(
return slot_read(checker, slot, slot_type, span), slot_type
}
// loop_yields_none reports whether any `yield` that targets this loop (a labeled
// loop_yields_null reports whether any `yield` that targets this loop (a labeled
// `yield :blk` inside `if`/block branches, or the trailing fall-through) yields the
// literal `none` making the loop's result optional. Pure AST walk; does not descend
// literal `null` making the loop's result optional. Pure AST walk; does not descend
// into nested loops or value sources, whose yields belong to them.
loop_yields_none :: proc(checker: ^Checker, stmts: []ast.Stmt_Id) -> bool {
loop_yields_null :: proc(checker: ^Checker, stmts: []ast.Stmt_Id) -> bool {
for id in stmts {
s := checker.ast_module.statements[id]
#partial switch s.kind {
case .Yield:
if s.expr != ast.INVALID_EXPR && checker.ast_module.exprs[s.expr].kind == .None {
if s.expr != ast.INVALID_EXPR && checker.ast_module.exprs[s.expr].kind == .Null {
return true
}
case .If:
if loop_yields_none(checker, s.body) || loop_yields_none(checker, s.else_body) {
if loop_yields_null(checker, s.body) || loop_yields_null(checker, s.else_body) {
return true
}
case .Block:
if loop_yields_none(checker, s.body) {
if loop_yields_null(checker, s.body) {
return true
}
}
@@ -12593,8 +12593,8 @@ loop_yields_none :: proc(checker: ^Checker, stmts: []ast.Stmt_Id) -> bool {
}
// resolve_loop_slot fixes a value-loop's result slot from its first concrete yield (an
// optional element type when the loop also yields `none`) and coerces `value` into it.
// Returns INVALID when the type can't be fixed yet (a `none`/invalid first yield).
// optional element type when the loop also yields `null`) and coerces `value` into it.
// Returns INVALID when the type can't be fixed yet (a `null`/invalid first yield).
resolve_loop_slot :: proc(ctx: ^Build_Ctx, target: ^Yield_Target, value: hir.Expr_Id, vtype: types.Type, span: source.Span) -> hir.Expr_Id {
checker := ctx.checker
if target.slot == hir.INVALID_LOCAL {
@@ -12608,14 +12608,14 @@ resolve_loop_slot :: proc(ctx: ^Build_Ctx, target: ^Yield_Target, value: hir.Exp
}
// first_concrete_yield_expr returns the AST expr of the first yield (source order) that is
// not the literal `none`, descending into `if`/block branches but not nested loops or value
// sources (whose yields belong to them). INVALID when the loop yields only `none`.
// not the literal `null`, descending into `if`/block branches but not nested loops or value
// sources (whose yields belong to them). INVALID when the loop yields only `null`.
first_concrete_yield_expr :: proc(checker: ^Checker, stmts: []ast.Stmt_Id) -> ast.Expr_Id {
for id in stmts {
s := checker.ast_module.statements[id]
#partial switch s.kind {
case .Yield:
if s.expr != ast.INVALID_EXPR && checker.ast_module.exprs[s.expr].kind != .None {
if s.expr != ast.INVALID_EXPR && checker.ast_module.exprs[s.expr].kind != .Null {
return s.expr
}
case .If:
@@ -12635,9 +12635,9 @@ first_concrete_yield_expr :: proc(checker: ^Checker, stmts: []ast.Stmt_Id) -> as
}
// value_loop_element_type pre-types the element of an untyped value loop from its first
// concrete (non-`none`) yield, so a `none` yielded before any concrete value still resolves
// concrete (non-`null`) yield, so a `null` yielded before any concrete value still resolves
// the result to `?T`. The loop's captures are bound temporarily for the probe and the probe
// expr is discarded; returns INVALID when the loop yields only `none`.
// expr is discarded; returns INVALID when the loop yields only `null`.
value_loop_element_type :: proc(ctx: ^Build_Ctx, loop_stmt: ast.Stmt) -> types.Type {
checker := ctx.checker
yield_expr := first_concrete_yield_expr(checker, loop_stmt.body)
@@ -12736,7 +12736,7 @@ block_element_type :: proc(ctx: ^Build_Ctx, block_stmts: []ast.Stmt_Id) -> types
// reads it after the block. Every path must yield (or otherwise exit); HIR holds a `.Block`
// that emits the body and the exit label the labeled breaks branch to. No iteration / no
// fall-through (unlike a value loop). The type is the annotation when typed, else the first
// concrete `yield :blk`'s type (optional when any yield is `none`).
// concrete `yield :blk`'s type (optional when any yield is `null`).
build_value_labeled_block :: proc(
ctx: ^Build_Ctx,
body: ^[dynamic]hir.Stmt_Id,
@@ -12746,7 +12746,7 @@ build_value_labeled_block :: proc(
span: source.Span,
) -> (value: hir.Expr_Id, value_type: types.Type) {
checker := ctx.checker
result_optional := loop_yields_none(checker, block_stmts)
result_optional := loop_yields_null(checker, block_stmts)
slot := hir.INVALID_LOCAL
slot_type := types.INVALID
if is_runtime_type(checker, expected) {
@@ -12754,8 +12754,8 @@ build_value_labeled_block :: proc(
slot = new_value_slot(ctx, slot_type)
result_optional = types.is_optional(slot_type, &checker.module.types)
} else if result_optional {
// Untyped block that also yields `none`: pre-type the element from the first
// concrete yield (regardless of source order) so a `none` yielded first still
// Untyped block that also yields `null`: pre-type the element from the first
// concrete yield (regardless of source order) so a `null` yielded first still
// resolves the result to `?T`.
elem := block_element_type(ctx, block_stmts)
if is_runtime_type(checker, elem) {
@@ -12816,7 +12816,7 @@ build_value_labeled_block :: proc(
// desugars (in build_block) to `slot = x; break`, and the construct's value is a read
// of the slot after the loop. Reuses the ordinary `.For`/`.While` build via a peeled
// copy; no new HIR. The yielded type is the annotation when typed, else the first
// concrete yield's type (optional when any yield is `none`).
// concrete yield's type (optional when any yield is `null`).
build_value_loop :: proc(
ctx: ^Build_Ctx,
body: ^[dynamic]hir.Stmt_Id,
@@ -12844,7 +12844,7 @@ build_value_loop :: proc(
}
fall_stmt := checker.ast_module.statements[loop_stmt.body[n - 1]]
result_optional := loop_yields_none(checker, loop_stmt.body)
result_optional := loop_yields_null(checker, loop_stmt.body)
slot := hir.INVALID_LOCAL
slot_type := types.INVALID
if is_runtime_type(checker, expected) {
@@ -12852,8 +12852,8 @@ build_value_loop :: proc(
slot = new_value_slot(ctx, slot_type)
result_optional = types.is_optional(slot_type, &checker.module.types)
} else if result_optional {
// Untyped loop that also yields `none`: pre-type the element from the first
// concrete yield (regardless of source order) so a `none` built before any
// Untyped loop that also yields `null`: pre-type the element from the first
// concrete yield (regardless of source order) so a `null` built before any
// concrete yield still resolves the result to `?T`.
elem := value_loop_element_type(ctx, loop_stmt)
if is_runtime_type(checker, elem) {
+19 -19
View File
@@ -228,7 +228,7 @@ Ct_Value_Kind :: enum u8 {
Slice,
Function,
Type,
None,
Null,
Optional_Some,
Fallible,
}
@@ -651,12 +651,12 @@ ct_coerce_value :: proc(state: ^Ct_State, id: Ct_Value_Id, expected: types.Type,
return ct_add_value(state, value), true
}
}
if value.kind == .None {
if value.kind == .Null {
if types.is_optional(expected, store) {
value.type = expected
return ct_add_value(state, value), true
}
return INVALID_CT_VALUE, ct_fail(state, .Not_Comptime, span, "'none' requires an optional context")
return INVALID_CT_VALUE, ct_fail(state, .Not_Comptime, span, "'null' requires an optional context")
}
if types.is_optional(expected, store) {
child := types.child_type(expected, store)
@@ -941,9 +941,9 @@ ct_materialize_value :: proc(
case .Function:
value_expected := expected if types.is_valid(expected) else value.type
return build_function_value(checker, ast.Function_Id(u32(value.index)), span, value_expected)
case .None:
case .Null:
return add_hir_expr(checker, hir.Expr{
kind=.None, span=span, type=value.type,
kind=.Null, span=span, type=value.type,
target=hir.INVALID_REF, left=hir.INVALID_EXPR, right=hir.INVALID_EXPR,
diagnostic=source.INVALID_DIAGNOSTIC,
})
@@ -1154,11 +1154,11 @@ ct_eval_expr :: proc(
return ct_add_value(state, Ct_Value{kind=.Type, type=types.INVALID, index=u64(resolved)}), ct_flow(.Normal), types.is_valid(resolved)
case .Enum_Literal:
return ct_eval_enum_literal(state, expr, expected, depth+1)
case .None:
case .Null:
if !types.is_optional(expected, store) {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "'none' requires an optional context")
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "'null' requires an optional context")
}
return ct_add_value(state, Ct_Value{kind=.None, type=expected}), ct_flow(.Normal), true
return ct_add_value(state, Ct_Value{kind=.Null, type=expected}), ct_flow(.Normal), true
case .Field:
if enum_type, enum_ok := enum_type_from_field_expr(checker, expr, state.pkg, state.file); enum_ok {
member, ok := find_enum_member(checker, enum_type, expr.name)
@@ -1246,7 +1246,7 @@ ct_eval_expr :: proc(
return children[0], ct_flow(.Normal), true
}
}
if v.kind == .None {
if v.kind == .Null {
child := types.child_type(v.type, store)
return ct_eval_expr(state, expr.right, child, depth+1)
}
@@ -1289,7 +1289,7 @@ ct_eval_expr :: proc(
}
left_expr := checker.ast_module.exprs[expr.left]
right_expr := checker.ast_module.exprs[expr.right]
if left_expr.kind == .None && right_expr.kind != .None &&
if left_expr.kind == .Null && right_expr.kind != .Null &&
(expr.kind == .Eq || expr.kind == .Ne) {
right, right_flow, right_ok := ct_eval_expr(state, expr.right, types.INVALID, depth+1)
if !right_ok || right_flow.kind != .Normal {
@@ -2071,8 +2071,8 @@ ct_unwrap_optional :: proc(state: ^Ct_State, id: Ct_Value_Id, span: source.Span)
return children[0], ct_flow(.Normal), true
}
}
if value.kind == .None {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "comptime optional unwrap of none")
if value.kind == .Null {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "comptime optional unwrap of null")
}
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "postfix '?' requires an optional")
}
@@ -2132,13 +2132,13 @@ ct_eval_binary :: proc(state: ^Ct_State, op: ast.Expr_Kind, left_id, right_id: C
left := state.values[left_id]
right := state.values[right_id]
is_compare := op == .Eq || op == .Ne || op == .Lt || op == .Le || op == .Gt || op == .Ge
if left.kind == .None || right.kind == .None {
if left.kind == .Null || right.kind == .Null {
if (op != .Eq && op != .Ne) ||
!types.is_optional(left.type, &state.checker.module.types) ||
!types.equal(left.type, right.type) {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "'none' only supports '==' and '!=' with an optional value")
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "'null' only supports '==' and '!=' with an optional value")
}
equal := left.kind == .None && right.kind == .None
equal := left.kind == .Null && right.kind == .Null
if op == .Ne {
equal = !equal
}
@@ -2781,7 +2781,7 @@ ct_typeinfo_value :: proc(state: ^Ct_State, target: types.Type, span: source.Spa
kind=.Array, type=fields_type, start=fields_start, count=u32(len(field_values)),
})
name_optional_type := types.optional(store, types.slice(store, types.U8, false))
record_name := ct_add_value(state, Ct_Value{kind=.None, type=name_optional_type})
record_name := ct_add_value(state, Ct_Value{kind=.Null, type=name_optional_type})
if item.name != 0 {
name_value := ct_reflection_string(state, symbol_text(checker, symbol.Id(item.name)))
name_start := u32(len(state.children))
@@ -3053,7 +3053,7 @@ ct_struct_type :: proc(
)
}
default := state.values[value]
if default.kind == .None {
if default.kind == .Null {
continue
}
children := ct_child_slice(state, default)
@@ -3616,7 +3616,7 @@ ct_write_comptime_key :: proc(state: ^Ct_State, id: Ct_Value_Id, builder: ^strin
}
strings.write_string(builder, "];")
return true
case .None:
case .Null:
strings.write_string(builder, "n;")
return true
case .Optional_Some:
@@ -4156,7 +4156,7 @@ ct_exec_if :: proc(state: ^Ct_State, statement: ast.Stmt, yield_returns: bool, d
return flow, ok
}
v := state.values[value]
if v.kind == .None {
if v.kind == .Null {
matched = false
break
}
+1 -1
View File
@@ -82,7 +82,7 @@ Expr_Kind :: enum u8 {
Undefined,
Array,
Struct,
None,
Null,
Optional_Some,
Local,
Global,
+1 -1
View File
@@ -71,7 +71,7 @@ Opcode :: enum u8 {
Poison,
String,
Aggregate,
None,
Null,
Optional_Some,
Load_Global,
Function_Address,
+1 -1
View File
@@ -30,7 +30,7 @@ keyword_kind :: proc(text: string) -> token.Kind {
case "try": return .Keyword_Try
case "catch": return .Keyword_Catch
case "mut": return .Keyword_Mut
case "none": return .Keyword_None
case "null": return .Keyword_Null
case "undefined": return .Keyword_Undefined
case "orelse": return .Keyword_Orelse
case "and": return .Keyword_And
+4 -4
View File
@@ -275,7 +275,7 @@ valid_value :: proc(
return false
}
switch instructions[value_id].op {
case .Param, .Const, .Poison, .String, .Aggregate, .None, .Optional_Some,
case .Param, .Const, .Poison, .String, .Aggregate, .Null, .Optional_Some,
.Load_Global, .Function_Address, .Address_Of, .Load, .Union_Tag, .Slice, .Length, .Slice_Ptr,
.Fallible_Error, .Extract, .Select, .Unwrap,
.Optional_Is_Some, .Optional_Value, .Orelse,
@@ -1109,10 +1109,10 @@ emit_instruction_stream :: proc(
}
fmt.sbprintf(&emitter.builder, ", %d\n", arg_index)
}
case .None:
case .Null:
item, ok := types.node(&emitter.module.types, instruction.type)
if !ok || item.kind != .Optional {
emit_recovery_value(emitter, instruction_index, instruction, "invalid optional none")
emit_recovery_value(emitter, instruction_index, instruction, "invalid optional null")
continue
}
if types.is_pointer(item.child, &emitter.module.types) {
@@ -1656,7 +1656,7 @@ emit_instruction_stream :: proc(
fmt.sbprintf(&emitter.builder, " %%optional_ok%d = extractvalue %s %%v%d, 0\n", instruction_index, llvm_type(optional_type, &emitter.module.types), instruction.a)
}
fmt.sbprintf(&emitter.builder, " br i1 %%optional_ok%d, label %%optional_continue%d, label %%optional_trap%d\noptional_trap%d:\n", instruction_index, instruction_index, instruction_index, instruction_index)
message := diagnostic_message(emitter, source.INVALID_DIAGNOSTIC, instruction.span, "attempted to unwrap none")
message := diagnostic_message(emitter, source.INVALID_DIAGNOSTIC, instruction.span, "attempted to unwrap null")
emit_trap_call(emitter, message)
fmt.sbprintf(&emitter.builder, " unreachable\noptional_continue%d:\n", instruction_index)
if types.is_pointer(item.child, &emitter.module.types) {
+2 -2
View File
@@ -437,7 +437,7 @@ add_macro_zero_expr :: proc(
return ast.INVALID_EXPR, false
}
return add_import_expr(state, ast.Expr{
kind=.None, span=span,
kind=.Null, span=span,
left=ast.INVALID_EXPR, right=ast.INVALID_EXPR,
diagnostic=source.INVALID_DIAGNOSTIC,
}), true
@@ -680,7 +680,7 @@ add_converted_macro_value_expr :: proc(
}, span), true
case .Null:
return add_import_expr(state, ast.Expr{
kind=.None, span=span,
kind=.Null, span=span,
left=ast.INVALID_EXPR, right=ast.INVALID_EXPR,
diagnostic=source.INVALID_DIAGNOSTIC,
}), true
+5 -5
View File
@@ -244,9 +244,9 @@ lower_compound_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instructi
b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
case .None:
case .Null:
return append_instruction(state, ir.Instruction{
op=.None, span=expr.span, type=expr.type,
op=.Null, span=expr.span, type=expr.type,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
@@ -563,8 +563,8 @@ lower_compound_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instructi
case .Eq, .Ne, .Lt, .Le, .Gt, .Ge:
left_expr := state.hir_module.exprs[expr.left]
right_expr := state.hir_module.exprs[expr.right]
if left_expr.kind == .None || right_expr.kind == .None {
optional_expr := expr.right if left_expr.kind == .None else expr.left
if left_expr.kind == .Null || right_expr.kind == .Null {
optional_expr := expr.right if left_expr.kind == .Null else expr.left
optional := lower_nested_expr(state, optional_expr)
present := append_instruction(state, ir.Instruction{
op=.Optional_Is_Some, span=expr.span, type=types.BOOL,
@@ -716,7 +716,7 @@ lower_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instruction_Id {
diagnostic=source.INVALID_DIAGNOSTIC,
})
_ = pop(&stack)
case .String, .Array, .Struct, .Range, .None, .Optional_Some, .Address, .Deref,
case .String, .Array, .Struct, .Range, .Null, .Optional_Some, .Address, .Deref,
.Index, .Slice, .Field, .Union_Tag, .Length, .Slice_Ptr, .Unwrap, .Orelse,
.Try, .Catch, .Not, .Eq, .Ne, .Lt, .Le, .Gt, .Ge, .And, .Or:
last = lower_compound_expr(state, frame.expr)
+2 -2
View File
@@ -966,10 +966,10 @@ parse_primary :: proc(parser: ^Parser, nesting: int) -> ast.Expr_Id {
right=ast.INVALID_EXPR,
diagnostic=source.INVALID_DIAGNOSTIC,
})
case .Keyword_None:
case .Keyword_Null:
advance(parser)
return add_expr(parser, ast.Expr{
kind=.None,
kind=.Null,
span=tok.span,
left=ast.INVALID_EXPR,
right=ast.INVALID_EXPR,
+1 -1
View File
@@ -76,7 +76,7 @@ Kind :: enum u8 {
Keyword_Try,
Keyword_Catch,
Keyword_Mut,
Keyword_None,
Keyword_Null,
Keyword_Undefined,
Keyword_Orelse,
Keyword_And,
+34 -32
View File
@@ -206,7 +206,7 @@ parser_applies_bitwise_precedence_and_preserves_capture_and_deref_pipes :: proc(
value i32 = 1
pointer *i32 = &value
_ = pointer^ & 1
if (none | none) |captured| { _ = captured }
if (null | null) |captured| { _ = captured }
}
`
source_file := source.Source{path="test.bro", text=text}
@@ -346,7 +346,7 @@ bitwise_checker_rejects_invalid_operands_and_known_overshifts :: proc(t: ^testin
text := `D :: distinct u8
E :: enum { one }
main func() void {
p *u8 = none
p *u8 = null
d D = D(1)
e E = .one
signed_count i8 = 1
@@ -1380,7 +1380,7 @@ main func() void {
testing.expect(t, strings.contains(llvm_text, "declare i32 @exact(i32)"))
testing.expect(t, strings.contains(llvm_text, "@bro.str.0 = private unnamed_addr constant [6 x i8] c\"hello\\00\""))
testing.expect(t, strings.contains(llvm_text, "getelementptr [3 x i8]"))
testing.expect(t, strings.contains(llvm_text, "attempted to unwrap none"))
testing.expect(t, strings.contains(llvm_text, "attempted to unwrap null"))
testing.expect(t, strings.contains(llvm_text, "orelse_fallback"))
testing.expect(t, strings.contains(llvm_text, "orelse_some"))
}
@@ -2109,7 +2109,7 @@ main func() void {
p^ = 2
}
handle ?@mut Handle = none
handle ?@mut Handle = null
use_handle(handle)
}
`
@@ -2147,7 +2147,7 @@ main func() void {
anyopaque_by_value_and_invalid_ptrcasts_are_rejected :: proc(t: ^testing.T) {
text := `Callback :: alias c_func() void
main func() void {
raw ?*mut anyopaque = none
raw ?*mut anyopaque = null
value anyopaque = undefined
_ = ptrcast!(void, raw)
_ = ptrcast!(anyopaque, raw)
@@ -5685,7 +5685,7 @@ yield_compiles_and_runs :: proc(t: ^testing.T) {
state := run_executable(output)
// Value blocks, value if-statements (incl. `return` branches and unwrap-`if`),
// `orelse`, value loops, labeled value blocks (`blk: { … yield :blk v }`, incl.
// defer-capture, `{T,none}` optional, and `none` before a concrete yield that uses a
// defer-capture, `{T,null}` optional, and `null` before a concrete yield that uses a
// block local), outer-loop control (`yield :outer v` / `break :outer`), and labeled
// block statements exited via `break :blk` together produce 42.
testing.expect_value(t, state.exit_code, 42)
@@ -5706,7 +5706,7 @@ value_loop_label_does_not_shadow_own_yield_target :: proc(t: ^testing.T) {
text := `main func() i32 {
idx :: for 0..10 |i| hit: {
if (i == 3) yield :hit i
yield none
yield null
}
if idx |found| {
if (found == 3) return 0
@@ -9247,7 +9247,7 @@ apply func($callback func() i32) i32 { return callback() }
main func() i32 {
box top.Box(i32) :: top.Box(i32) { value = 2 }
point top.Point :: top.Point { value = 3 }
maybe facade.MaybePoint :: none
maybe facade.MaybePoint :: null
scalar facade.Scalar :: 5
top.counter = 7
if box.value != 2 or point.value != 3 { return 1 }
@@ -14084,15 +14084,15 @@ TokenKind :: enum(u8) {
}
Simple :: enum { first, second }
Names :: alias meta.EnumFieldStruct(TokenKind, ?[]u8, some!(none))
Names :: alias meta.EnumFieldStruct(TokenKind, ?[]u8, some!(null))
Flags :: alias meta.EnumFieldStruct(Simple, bool, false)
Direct :: alias struct_type!(.auto, {"x", "name"}, {i32, []u8}, {none, "bro"})
CPoint :: alias struct_type!(.c, {"x"}, {c_int}, {none})
Direct :: alias struct_type!(.auto, {"x", "name"}, {i32, []u8}, {null, "bro"})
CPoint :: alias struct_type!(.c, {"x"}, {c_int}, {null})
ArrayInput :: alias struct_type!(.auto, ["value"], [i32], [7])
Empty :: alias struct_type!(.auto, {}, {}, {})
direct_position struct_type!(.auto, {"value"}, {i32}, {none}) = {value = 5}
direct_position struct_type!(.auto, {"value"}, {i32}, {null}) = {value = 5}
make_direct func() struct_type!(.auto, {"value"}, {i32}, {none}) {
make_direct func() struct_type!(.auto, {"value"}, {i32}, {null}) {
return {value = 6}
}
@@ -14108,8 +14108,9 @@ Generated func($T type) type {
GeneratedInt :: alias Generated(i32)
ordered Names = {}
static_none ?i32 :: none
static_none ?i32 :: null
static_some ?i32 :: 1
none :: 41
Map func($E, $V type) type {
match typeinfo!(E) {
@@ -14121,7 +14122,7 @@ Map func($E, $V type) type {
}
}
init func($E, $V type, values meta.EnumFieldStruct(E, ?V, some!(none))) Map(E, V) {
init func($E, $V type, values meta.EnumFieldStruct(E, ?V, some!(null))) Map(E, V) {
map Map(E, V) = undefined
match typeinfo!(E) {
.enum |info|: expand for info.fields |field, index| {
@@ -14141,7 +14142,7 @@ get func($E, $V type, map @Map(E, V), key E) ?V {
.enum |info|: expand for info.fields |field, index| {
if key == field!(E, field.name) {
if map.present[index] { return map.values[index] }
return none
return null
}
}
else: compile_error!("EnumMap key must be an enum")
@@ -14150,8 +14151,9 @@ get func($E, $V type, map @Map(E, V), key E) ?V {
main func() i32 {
_ = ordered
if !$(static_none == none) or !$(none == static_none) or
$(static_some == none) or $(none == static_some) { return 23 }
if none != 41 { return 24 }
if !$(static_none == null) or !$(null == static_none) or
$(static_some == null) or $(null == static_some) { return 23 }
inferred :: {x = 40, name = "bro"}
if inferred.x != 40 or inferred.name.len != 3 { return 1 }
direct Direct = {x = 7}
@@ -14167,7 +14169,7 @@ main func() i32 {
if direct_position.value != 5 or make_direct().value != 6 { return 17 }
c_point CPoint = {x = 9}
if c_point.x != 9 { return 12 }
nested ??i32 = some!(none)
nested ??i32 = some!(null)
if nested |inner| {
if inner |_| { return 13 }
} else { return 14 }
@@ -14196,14 +14198,14 @@ main func() i32 {
if !map.present[0] or !map.present[1] or map.present[2] { return 9 }
if map.values[0].len != 10 or map.values[1].len != 7 { return 18 }
ident :: get(TokenKind, []u8, &map, TokenKind.ident)
if ident == none or none == ident { return 19 }
if ident == null or null == ident { return 19 }
if ident |value| {
if value.len != 10 { return 19 }
} else { return 20 }
eof :: get(TokenKind, []u8, &map, TokenKind.eof)
if eof != none or none != eof { return 21 }
if eof != null or null != eof { return 21 }
location testing.SourceLocation = {file = "test.bro", line = 1, column = 1}
testing.expect_equal(none, eof, location) catch |_| { return 24 }
testing.expect_equal(null, eof, location) catch |_| { return 24 }
testing.expect_equal(ident, ident, location) catch |_| { return 25 }
if eof |_| { return 22 }
return 0
@@ -14275,18 +14277,18 @@ anonymous_records_and_struct_type_report_targeted_errors :: proc(t: ^testing.T)
main_path := "/tmp/brolang-test-bad-struct-type/main.bro"
text := `meta :: import "@std/meta"
E :: enum { value }
Fields :: alias struct_type!(.auto, {"value"}, {i32}, {none})
RequiredEnum :: alias meta.EnumFieldStruct(E, i32, none)
NotEnum :: alias meta.EnumFieldStruct(i32, i32, none)
BadField :: alias struct_type!(.auto, {"value"}, {void}, {none})
BadComptimeField :: alias struct_type!(.auto, {"T"}, {type}, {none})
Fields :: alias struct_type!(.auto, {"value"}, {i32}, {null})
RequiredEnum :: alias meta.EnumFieldStruct(E, i32, null)
NotEnum :: alias meta.EnumFieldStruct(i32, i32, null)
BadField :: alias struct_type!(.auto, {"value"}, {void}, {null})
BadComptimeField :: alias struct_type!(.auto, {"T"}, {type}, {null})
BadDefault :: alias struct_type!(.auto, {"value"}, {i32}, {"no"})
UnstableDefault :: alias struct_type!(.auto, {"value"}, {i32}, {undefined})
BadLengths :: alias struct_type!(.auto, {"one", "two"}, {i32}, {none})
BadNames :: alias struct_type!(.auto, {"not-valid"}, {i32}, {none})
DuplicateNames :: alias struct_type!(.auto, {"same", "same"}, {i32, i32}, {none, none})
BadLayout :: alias struct_type!(.packed, {"value"}, {i32}, {none})
BadC :: alias struct_type!(.c, {"value"}, {[]u8}, {none})
BadLengths :: alias struct_type!(.auto, {"one", "two"}, {i32}, {null})
BadNames :: alias struct_type!(.auto, {"not-valid"}, {i32}, {null})
DuplicateNames :: alias struct_type!(.auto, {"same", "same"}, {i32, i32}, {null, null})
BadLayout :: alias struct_type!(.packed, {"value"}, {i32}, {null})
BadC :: alias struct_type!(.c, {"value"}, {[]u8}, {null})
EmptyC :: alias struct_type!(.c, {}, {}, {})
unknown Fields = {missing = 1}
duplicate Fields = {value = 1, value = 2}
+2 -2
View File
@@ -40,7 +40,7 @@ Ball :: struct {
}
# A frame's worth of player intent, as a tagged union. Each arm carries exactly
# the data that action needs (or `void` when it needs none).
# the data that action needs (or `void` when it needs null).
Command :: union(enum) {
spawn rl.Vector2 # spawn a shape at this point
push struct { dx f32, dy f32 } # blow every shape this way
@@ -213,7 +213,7 @@ main func() i32 {
sel :: for 0..(count) |i| hover: {
c rl.Vector2 = rl.Vector2{ x = balls[i].x, y = balls[i].y }
if rl.CheckCollisionPointCircle(mouse, c, balls[i].radius) yield :hover i
yield none
yield null
}
# --- draw -----------------------------------------------------------
+1 -1
View File
@@ -5,7 +5,7 @@ native :: import "../include/native.h"
# self-referential `?*mut Node` field as C-layout-compatible.
main func() i32 {
node native.Node = native.Node { next = none, value = 7 }
node native.Node = native.Node { next = null, value = 7 }
if node.next |_| {
return 1
}
+3 -3
View File
@@ -20,11 +20,11 @@ init func(allocator mem.Allocator) State {
}
hide fail_alloc func(_ ?@mut anyopaque, _ usize, _ usize) ?*mut u8 {
return none
return null
}
hide fail_realloc func(_ ?@mut anyopaque, _ ?*mut u8, _ usize, _ usize, _ usize) ?*mut u8 {
return none
return null
}
hide fail_free func(_ ?@mut anyopaque, _ ?*mut u8, _ usize, _ usize) void {}
@@ -36,7 +36,7 @@ hide fail_vtable mem.AllocatorVTable :: mem.AllocatorVTable {
}
hide fail_allocator mem.Allocator :: mem.Allocator {
context = none,
context = null,
vtable = &fail_vtable,
}
@@ -91,7 +91,7 @@ main func() i32 {
_ = assigned
_ = take_i32(zero())
_ = return_zero()
optional ?u32 = none
optional ?u32 = null
if !same_type(?u32, optional) {
return 8
}
+1 -1
View File
@@ -73,7 +73,7 @@ maybe func(flag bool) ?i32 {
if flag {
return 9
}
return none
return null
}
may_fail func(flag bool) i32 ! Error {
@@ -16,8 +16,8 @@ main func() i32 {
total = total + 99
}
# none -> else branch taken; the binding is not in scope there
b ?i32 = none
# null -> else branch taken; the binding is not in scope there
b ?i32 = null
if b |v| {
total = total + v
} else {
@@ -31,8 +31,8 @@ main func() i32 {
total = total + q^ # +0
}
# optional pointer none -> skipped
z ?@i32 = none
# optional pointer null -> skipped
z ?@i32 = null
if z |_| {
total = total + 1000
}
+6 -6
View File
@@ -38,7 +38,7 @@ hide write_too_much func(_ ?@mut anyopaque, _ io.Handle, bytes []u8) usize ! io.
ok_reader func() io.Reader {
return io.Reader {
context = none,
context = null,
handle = io.Handle {file_desc = 0},
read = read_ok,
}
@@ -46,7 +46,7 @@ ok_reader func() io.Reader {
bad_reader func() io.Reader {
return io.Reader {
context = none,
context = null,
handle = io.Handle {file_desc = 0},
read = read_too_much,
}
@@ -54,7 +54,7 @@ bad_reader func() io.Reader {
eof_reader func() io.Reader {
return io.Reader {
context = none,
context = null,
handle = io.Handle {file_desc = 0},
read = read_eof,
}
@@ -62,7 +62,7 @@ eof_reader func() io.Reader {
short_writer func() io.Writer {
return io.Writer {
context = none,
context = null,
handle = io.Handle {file_desc = 0},
write = write_short,
}
@@ -70,7 +70,7 @@ short_writer func() io.Writer {
none_writer func() io.Writer {
return io.Writer {
context = none,
context = null,
handle = io.Handle {file_desc = 0},
write = write_none,
}
@@ -78,7 +78,7 @@ none_writer func() io.Writer {
bad_writer func() io.Writer {
return io.Writer {
context = none,
context = null,
handle = io.Handle {file_desc = 0},
write = write_too_much,
}
@@ -1,7 +1,7 @@
mem :: import "@std/mem"
raw_allocator_test func() i32 {
resized ?*mut u8 = mem.raw_realloc(mem.c_allocator, none, 0, 4, 1)
resized ?*mut u8 = mem.raw_realloc(mem.c_allocator, null, 0, 4, 1)
if resized |bytes| {
bytes[0] = 10
bytes[1] = 20
@@ -11,9 +11,9 @@ TaskList :: struct {
task_list_init func(allocator mem.Allocator) TaskList {
return TaskList {
ids = none,
priorities = none,
durations = none,
ids = null,
priorities = null,
durations = null,
len = 0,
capacity = 0,
allocator = allocator,
@@ -27,7 +27,7 @@ alloc_i32s func(allocator mem.Allocator, count usize) ?[]mut i32 {
failed = true
yield (&fallback).ptr[..0]
}
if (failed) return none
if (failed) return null
return values
}
@@ -144,9 +144,9 @@ task_list_deinit func(list @mut TaskList) void {
free_i32s(list.allocator, list.ids)
free_i32s(list.allocator, list.priorities)
free_i32s(list.allocator, list.durations)
list.ids = none
list.priorities = none
list.durations = none
list.ids = null
list.priorities = null
list.durations = null
list.len = 0
list.capacity = 0
}
@@ -9,12 +9,12 @@ hide probe_count func(context ?@mut anyopaque) void {
hide probe_alloc func(context ?@mut anyopaque, _ usize, _ usize) ?*mut u8 {
probe_count(context)
return none
return null
}
hide probe_realloc func(context ?@mut anyopaque, _ ?*mut u8, _ usize, _ usize, _ usize) ?*mut u8 {
probe_count(context)
return none
return null
}
hide probe_free func(context ?@mut anyopaque, _ ?*mut u8, _ usize, _ usize) void {
@@ -42,8 +42,8 @@ typed_allocator_test func() i32 {
}
_ = mem.raw_alloc(first_allocator, 1, 1)
_ = mem.raw_realloc(first_allocator, none, 0, 1, 1)
mem.raw_free(first_allocator, none, 0, 1)
_ = mem.raw_realloc(first_allocator, null, 0, 1, 1)
mem.raw_free(first_allocator, null, 0, 1)
_ = mem.raw_alloc(second_allocator, 1, 1)
if (first_calls[0] != 3 or second_calls[0] != 1) return 32
+1 -1
View File
@@ -54,7 +54,7 @@ different Config :: Config {
state = .idle,
values = [21, 21],
pair = Pair {7, false},
maybe = none,
maybe = null,
payload = .empty,
}
+16 -16
View File
@@ -93,13 +93,13 @@ vif_expression func(old_entries []u8) usize {
# --- value loops (milestone 20.5) --------------------------------------------
# Labeled `for` used as a value: `yield :blk i` exits early with a value, the
# trailing `yield none` supplies the value when the loop completes. The `{i,
# none}` yields resolve the result to an optional.
# trailing `yield null` supplies the value when the loop completes. The `{i,
# null}` yields resolve the result to an optional.
loop_search func() i32 {
# first i in 0..10 whose square exceeds 40 (6*6=36 no, 7*7=49 yes -> 7).
idx :: for 0..10 |i| blk: {
if (i * i > 40) yield :blk i
yield none
yield null
}
if idx |found| {
if (found == 7) return 0
@@ -108,11 +108,11 @@ loop_search func() i32 {
return 2
}
# Same loop, but nothing matches -> the fall-through `yield none` is the result.
# Same loop, but nothing matches -> the fall-through `yield null` is the result.
loop_none func() i32 {
idx :: for 0..10 |i| blk: {
if (i > 100) yield :blk i
yield none
yield null
}
if idx |found| {
_ = found
@@ -126,7 +126,7 @@ loop_while func() i32 {
n i32 = 0
found :: while n < 100 : n += 1 blk: {
if (n == 8) yield :blk n
yield none
yield null
}
if found |v| {
if (v == 8) return 0
@@ -164,13 +164,13 @@ orelse_value func(opt ?i32) i32 {
return r
}
# Untyped value loop where `none` is yielded (in a labeled yield) before any
# Untyped value loop where `null` is yielded (in a labeled yield) before any
# concrete value: the element type still resolves to ?<i> from `yield :blk i`.
loop_none_first func() i32 {
r :: for 0..10 |i| blk: {
if (i > 100) yield :blk none
if (i > 100) yield :blk null
if (i * i > 40) yield :blk i # first concrete yield: i == 7
yield none
yield null
}
if r |found| {
if (found == 7) return 0
@@ -207,10 +207,10 @@ lblock_defer func() i32 {
return r # 5, not 999
}
# A labeled block whose `{T, none}` yields resolve the result to an optional.
# A labeled block whose `{T, null}` yields resolve the result to an optional.
lblock_optional func(present i32) i32 {
r :: blk: {
if (present == 0) yield :blk none
if (present == 0) yield :blk null
yield :blk 8
}
if r |v| {
@@ -225,7 +225,7 @@ yield_outer func(target i32) i32 {
for 0..3 |col| {
if (row * 3 + col == target) yield :outer (row * 10 + col)
}
yield none
yield null
}
if found |v| {
return v
@@ -284,11 +284,11 @@ stmt_block_nested func() i32 {
return hits
}
# Item B: a `none` yielded before a concrete `yield :blk` that references a block local.
# Item B: a `null` yielded before a concrete `yield :blk` that references a block local.
lblock_local func() i32 {
r :: blk: {
val :: 9
if (false) yield :blk none
if (false) yield :blk null
yield :blk val
}
if r |v| {
@@ -321,9 +321,9 @@ main func() i32 {
if (vif_return(0) != 11) return 116
if (vif_return(1) != 55) return 117
if (vif_unwrap(21) != 42) return 118
if (vif_unwrap(none) != 99) return 119
if (vif_unwrap(null) != 99) return 119
if (orelse_value(5) != 5) return 120
if (orelse_value(none) != 7) return 121
if (orelse_value(null) != 7) return 121
if (loop_none_first() != 0) return 122
if (lblock(0) != 10) return 123
+1 -1
View File
@@ -16,7 +16,7 @@
(boolean) @boolean
[
(none)
(null)
(undefined)
] @constant.builtin
+1 -1
View File
@@ -3,7 +3,7 @@ import "@std/io"
print func($format []u8, $Args type, args Args) void {
writer io.Writer :: io.Writer{
context = none,
context = null,
handle = io.Handle{ file_desc = c_int(io.Stream.stderr) },
write = write,
}
+1 -1
View File
@@ -138,7 +138,7 @@ hide system_vtable IoVTable :: IoVTable {
hide system func() Io {
return Io {
context = none,
context = null,
vtable = &system_vtable,
}
}
+9 -9
View File
@@ -90,7 +90,7 @@ realloc func($T type, allocator Allocator, memory []mut T, new_count usize) []mu
return .out_of_memory
}
old_memory ?*mut u8 = none
old_memory ?*mut u8 = null
old_size usize = 0
if memory.len != 0 {
old_memory = ptrcast!(u8, memory.ptr)
@@ -137,17 +137,17 @@ hide power_of_two func(value usize) bool {
hide c_alloc func(_ ?@mut anyopaque, size usize, alignment usize) ?*mut u8 {
if power_of_two(alignment) == false {
return none
return null
}
if alignment <= malloc_alignment {
return ptrcast!(u8, c.malloc(c_ulong(size)))
}
memory [1]mut ?*mut anyopaque = [none]
memory [1]mut ?*mut anyopaque = [null]
status c_int = c.posix_memalign((&memory).ptr, c_ulong(alignment), c_ulong(size))
if status != 0 {
return none
return null
}
return ptrcast!(u8, memory[0])
@@ -155,12 +155,12 @@ hide c_alloc func(_ ?@mut anyopaque, size usize, alignment usize) ?*mut u8 {
hide c_realloc func(_ ?@mut anyopaque, memory ?*mut u8, old_size usize, new_size usize, alignment usize) ?*mut u8 {
if power_of_two(alignment) == false {
return none
return null
}
if new_size == 0 {
c.free(memory)
return none
return null
}
if memory |old_memory| {
@@ -168,7 +168,7 @@ hide c_realloc func(_ ?@mut anyopaque, memory ?*mut u8, old_size usize, new_size
return ptrcast!(u8, c.realloc(old_memory, c_ulong(new_size)))
}
new_memory ?*mut u8 = c_alloc(none, new_size, alignment)
new_memory ?*mut u8 = c_alloc(null, new_size, alignment)
if new_memory |new_bytes| {
copy_size usize = old_size
if new_size < copy_size {
@@ -180,7 +180,7 @@ hide c_realloc func(_ ?@mut anyopaque, memory ?*mut u8, old_size usize, new_size
return new_memory
}
return c_alloc(none, new_size, alignment)
return c_alloc(null, new_size, alignment)
}
hide c_free func(_ ?@mut anyopaque, memory ?*mut u8, _ usize, _ usize) void {
@@ -194,6 +194,6 @@ hide c_vtable AllocatorVTable :: AllocatorVTable {
}
c_allocator Allocator :: Allocator {
context = none,
context = null,
vtable = &c_vtable,
}
+1 -1
View File
@@ -6,7 +6,7 @@ TestTokenKind :: enum(u8) {
eof = 21
}
TestNames :: alias EnumFieldStruct(TestTokenKind, ?[]u8, some!(none))
TestNames :: alias EnumFieldStruct(TestTokenKind, ?[]u8, some!(null))
enum_field_struct_defaults test {
names TestNames = {
+2 -2
View File
@@ -26,11 +26,11 @@ expect_equal func($T type, expected, actual T, location SourceLocation) void ! E
try expect_equal(expected_value, actual_value, location)
return
}
debug.print("{s}:{d}:{d}: expected an optional value, found none\n", {location.file, location.line, location.column})
debug.print("{s}:{d}:{d}: expected an optional value, found null\n", {location.file, location.line, location.column})
return .expectation_failed
}
if actual |_| {
debug.print("{s}:{d}:{d}: expected none, found an optional value\n", {location.file, location.line, location.column})
debug.print("{s}:{d}:{d}: expected null, found an optional value\n", {location.file, location.line, location.column})
return .expectation_failed
}
}
+2 -2
View File
@@ -38,7 +38,7 @@ Ball :: struct {
}
# A frame's worth of player intent, as a tagged union. Each arm carries exactly
# the data that action needs (or `void` when it needs none).
# the data that action needs (or `void` when it needs null).
Command :: union(enum) {
spawn Vector2 # spawn a shape at this point
push struct { dx f32, dy f32 } # blow every shape this way
@@ -211,7 +211,7 @@ main func() i32 {
sel :: for 0..(count) |i| blk: {
c Vector2 = Vector2{ x = balls[i].x, y = balls[i].y }
if CheckCollisionPointCircle(mouse, c, balls[i].radius) yield :blk i
yield none
yield null
}
# --- draw -----------------------------------------------------------
+1 -1
View File
@@ -116,7 +116,7 @@ hide system_vtable IoVTable :: IoVTable {
hide system func() Io {
return Io {
context = none,
context = null,
vtable = &system_vtable,
}
}
+9 -9
View File
@@ -90,7 +90,7 @@ realloc func($T type, allocator Allocator, memory []mut T, new_count usize) []mu
return .out_of_memory
}
old_memory ?*mut u8 = none
old_memory ?*mut u8 = null
old_size usize = 0
if memory.len != 0 {
old_memory = ptrcast!(u8, memory.ptr)
@@ -137,17 +137,17 @@ hide power_of_two func(value usize) bool {
hide c_alloc func(_ ?*mut anyopaque, size usize, alignment usize) ?*mut u8 {
if power_of_two(alignment) == false {
return none
return null
}
if alignment <= malloc_alignment {
return ptrcast!(u8, c.malloc(c_ulong(size)))
}
memory [1]mut ?*mut anyopaque = [none]
memory [1]mut ?*mut anyopaque = [null]
status c_int = c.posix_memalign((&memory).ptr, c_ulong(alignment), c_ulong(size))
if status != 0 {
return none
return null
}
return ptrcast!(u8, memory[0])
@@ -155,12 +155,12 @@ hide c_alloc func(_ ?*mut anyopaque, size usize, alignment usize) ?*mut u8 {
hide c_realloc func(_ ?*mut anyopaque, memory ?*mut u8, old_size usize, new_size usize, alignment usize) ?*mut u8 {
if power_of_two(alignment) == false {
return none
return null
}
if new_size == 0 {
c.free(memory)
return none
return null
}
if memory |old_memory| {
@@ -168,7 +168,7 @@ hide c_realloc func(_ ?*mut anyopaque, memory ?*mut u8, old_size usize, new_size
return ptrcast!(u8, c.realloc(old_memory, c_ulong(new_size)))
}
new_memory ?*mut u8 = c_alloc(none, new_size, alignment)
new_memory ?*mut u8 = c_alloc(null, new_size, alignment)
if new_memory |new_bytes| {
copy_size usize = old_size
if new_size < copy_size {
@@ -183,7 +183,7 @@ hide c_realloc func(_ ?*mut anyopaque, memory ?*mut u8, old_size usize, new_size
return new_memory
}
return c_alloc(none, new_size, alignment)
return c_alloc(null, new_size, alignment)
}
hide c_free func(_ ?*mut anyopaque, memory ?*mut u8, _ usize, _ usize) void {
@@ -197,6 +197,6 @@ hide c_vtable AllocatorVTable :: AllocatorVTable {
}
c_allocator Allocator :: Allocator {
context = none,
context = null,
vtable = &c_vtable,
}
+1 -1
View File
@@ -42,7 +42,7 @@ apply_decay func(players []mut Player) void {
}
best_player func(players []mut Player) ?@mut Player {
best ?@mut Player = none
best ?@mut Player = null
best_score i32 = 0
for players |@player| {
+8 -8
View File
@@ -82,17 +82,17 @@ hide power_of_two func(value usize) bool {
hide c_alloc func(_ ?*mut anyopaque, size usize, alignment usize) ?*mut u8 {
if power_of_two(alignment) == false {
return none
return null
}
if alignment <= malloc_alignment {
return ptrcast!(u8, c.malloc(c_ulong(size)))
}
memory [1]mut ?*mut anyopaque = [none]
memory [1]mut ?*mut anyopaque = [null]
status c_int = c.posix_memalign((&memory).ptr, c_ulong(alignment), c_ulong(size))
if status != 0 {
return none
return null
}
return ptrcast!(u8, memory[0])
@@ -100,12 +100,12 @@ hide c_alloc func(_ ?*mut anyopaque, size usize, alignment usize) ?*mut u8 {
hide c_realloc func(_ ?*mut anyopaque, memory ?*mut u8, old_size usize, new_size usize, alignment usize) ?*mut u8 {
if power_of_two(alignment) == false {
return none
return null
}
if new_size == 0 {
c.free(memory)
return none
return null
}
if memory |old_memory| {
@@ -113,7 +113,7 @@ hide c_realloc func(_ ?*mut anyopaque, memory ?*mut u8, old_size usize, new_size
return ptrcast!(u8, c.realloc(old_memory, c_ulong(new_size)))
}
new_memory ?*mut u8 = c_alloc(none, new_size, alignment)
new_memory ?*mut u8 = c_alloc(null, new_size, alignment)
if new_memory |new_bytes| {
copy_size usize = old_size
if new_size < copy_size {
@@ -128,7 +128,7 @@ hide c_realloc func(_ ?*mut anyopaque, memory ?*mut u8, old_size usize, new_size
return new_memory
}
return c_alloc(none, new_size, alignment)
return c_alloc(null, new_size, alignment)
}
hide c_free func(_ ?*mut anyopaque, memory ?*mut u8, _ usize, _ usize) void {
@@ -142,6 +142,6 @@ hide c_vtable AllocatorVTable :: AllocatorVTable {
}
c_allocator Allocator :: Allocator {
context = none,
context = null,
vtable = &c_vtable,
}
+3 -3
View File
@@ -461,7 +461,7 @@ module.exports = grammar({
$.multiline_string,
$.character,
$.boolean,
$.none,
$.null,
$.undefined,
),
@@ -644,7 +644,7 @@ module.exports = grammar({
)),
boolean: _ => choice('true', 'false'),
none: _ => 'none',
null: _ => 'null',
undefined: _ => 'undefined',
sink: _ => '_',
@@ -653,7 +653,7 @@ module.exports = grammar({
alias(choice(
'test', 'func', 'c_func', 'struct', 'c_struct', 'opaque', 'union', 'enum',
'distinct', 'alias', 'import', 'hide', 'return', 'try', 'catch', 'mut',
'none', 'undefined', 'orelse', 'and', 'or', 'xor', 'if', 'while', 'for',
'null', 'undefined', 'orelse', 'and', 'or', 'xor', 'if', 'while', 'for',
'expand', 'break', 'continue', 'defer', 'errdefer', 'yield', 'match', 'else',
'true', 'false',
'void', 'type', 'anyopaque', 'bool', 'int', 'uint', 'float', 'range',
+1 -1
View File
@@ -16,7 +16,7 @@
(boolean) @constant.builtin
[
(none)
(null)
(undefined)
] @constant.builtin
+4 -4
View File
@@ -3213,7 +3213,7 @@
},
{
"type": "SYMBOL",
"name": "none"
"name": "null"
},
{
"type": "SYMBOL",
@@ -4999,9 +4999,9 @@
}
]
},
"none": {
"null": {
"type": "STRING",
"value": "none"
"value": "null"
},
"undefined": {
"type": "STRING",
@@ -5092,7 +5092,7 @@
},
{
"type": "STRING",
"value": "none"
"value": "null"
},
{
"type": "STRING",
+3 -3
View File
@@ -840,7 +840,7 @@
"named": true
},
{
"type": "none",
"type": "null",
"named": true
},
{
@@ -1654,7 +1654,7 @@
}
},
{
"type": "none",
"type": "null",
"named": true,
"fields": {}
},
@@ -2925,7 +2925,7 @@
"named": false
},
{
"type": "none",
"type": "null",
"named": false
},
{
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -211,7 +211,7 @@ main func() void {
Intrinsic type construction
==================
Generated :: alias struct_type!(.auto, {"value"}, {i32}, {none})
Generated :: alias struct_type!(.auto, {"value"}, {i32}, {null})
---
@@ -238,7 +238,7 @@ Generated :: alias struct_type!(.auto, {"value"}, {i32}, {none})
(expression
(tuple_literal
(expression
(none))))))))))
(null))))))))))
==================
Errdefer
+1 -1
View File
@@ -20,5 +20,5 @@ clear func($K, $V type) void {
# ^ operator
}
Generated :: alias struct_type!(.auto, {"value"}, {i32}, {none})
Generated :: alias struct_type!(.auto, {"value"}, {i32}, {null})
# ^^^^^^^^^^^^ function.builtin