rename none to null
This commit is contained in:
+4
-4
@@ -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`
|
||||
|
||||
@@ -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
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ Expr_Kind :: enum u8 {
|
||||
String,
|
||||
Bool,
|
||||
Array,
|
||||
None,
|
||||
Null,
|
||||
Undefined,
|
||||
Inference_Hole,
|
||||
Type,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ Expr_Kind :: enum u8 {
|
||||
Undefined,
|
||||
Array,
|
||||
Struct,
|
||||
None,
|
||||
Null,
|
||||
Optional_Some,
|
||||
Local,
|
||||
Global,
|
||||
|
||||
+1
-1
@@ -71,7 +71,7 @@ Opcode :: enum u8 {
|
||||
Poison,
|
||||
String,
|
||||
Aggregate,
|
||||
None,
|
||||
Null,
|
||||
Optional_Some,
|
||||
Load_Global,
|
||||
Function_Address,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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}
|
||||
|
||||
@@ -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 -----------------------------------------------------------
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ different Config :: Config {
|
||||
state = .idle,
|
||||
values = [21, 21],
|
||||
pair = Pair {7, false},
|
||||
maybe = none,
|
||||
maybe = null,
|
||||
payload = .empty,
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
(boolean) @boolean
|
||||
|
||||
[
|
||||
(none)
|
||||
(null)
|
||||
(undefined)
|
||||
] @constant.builtin
|
||||
|
||||
|
||||
+1
-1
@@ -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
@@ -138,7 +138,7 @@ hide system_vtable IoVTable :: IoVTable {
|
||||
|
||||
hide system func() Io {
|
||||
return Io {
|
||||
context = none,
|
||||
context = null,
|
||||
vtable = &system_vtable,
|
||||
}
|
||||
}
|
||||
|
||||
+9
-9
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 -----------------------------------------------------------
|
||||
|
||||
@@ -116,7 +116,7 @@ hide system_vtable IoVTable :: IoVTable {
|
||||
|
||||
hide system func() Io {
|
||||
return Io {
|
||||
context = none,
|
||||
context = null,
|
||||
vtable = &system_vtable,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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| {
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
(boolean) @constant.builtin
|
||||
|
||||
[
|
||||
(none)
|
||||
(null)
|
||||
(undefined)
|
||||
] @constant.builtin
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
},
|
||||
{
|
||||
|
||||
+3536
-3536
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user