Diagnostics
Common compiler diagnostics and how to read current 0.0.0-dev errors.
Diagnostics are part of the user experience, but exact wording and source
ranges are not compatibility API while Zynx is 0.0.0-dev. Treat this page as
a map of common error categories and fixes.
Error unions are not booleans or nullable wrappers. Use try or catch before
using the success value.
let name: str? = load_user()?.name;
Relevant excerpt from current compiler output:
tests/errors/nullable_error_union_optional_before_try_error.zx:14:18: error: optional member access on error union requires `try` or `catch` before member access
|
14 | let name: str? = load_user()?.name;
| ^^^^^^^^^^^
15 | _ = name;
|
Fix:
let name: str? = (try load_user())?.name;
let fallback: str? = (load_user() catch { _ => null })?.name;
Foreign calls, raw pointer dereference, volatile access, and inline asm
require unsafe:
foreign "C" fn abs(value: i32) -> i32;
fn main() {
_ = abs(-1);
}
Current compiler output:
tests/unsafe/extern_call_error.zx:4:5: error: call to foreign function requires unsafe context
|
3 | fn main() {
4 | _ = abs(-1);
| ^^^^^^^
5 | }
|
Fix:
fn magnitude(value: i32) -> i32 {
unsafe {
return abs(value);
}
}
unsafe fn also requires unsafe context at the call site. It does not make the
function body implicitly unsafe.
Use ?. for every nullable hop:
let name: str? = user?.profile?.name;
a?.b.c is diagnosed when b is nullable, because plain . would require a
non-null receiver.
Current compiler output:
tests/nullable/optional_chaining_mixed_error.zx:11:21: error: optional member access expects an identifier; use `?.` for each step
|
10 | let a: A? = A { b: B { c: 1 } };
11 | let v: i32? = a?.b.c;
| ^^^
12 | }
|
Fix:
let v: i32? = a?.b?.c;
Reference parameters require explicit borrows in ordinary calls:
fn inc(a: &u32, b: const &u32) {
*a += *b;
}
fn main() {
let a: u32 = 0;
let b: u32 = 10;
inc(a, b);
}
Current compiler output:
reference argument requires an explicit `&` borrow or reference-valued expression
Fix:
inc(&a, &b);
Whole-condition call style is rejected:
assert(value > 0);
Current compiler output:
`assert` is a keyword; write `assert <expr>;` or `assert <expr>, <message>;`
Fix:
assert value > 0;
assert value > 0, "positive";
Most owned values move on assignment or call. After a move, use the new owner or clone explicitly when the type offers clone-style API:
import {
String
} from std.string;
fn main() {
let text = try String("hello");
let moved = text;
assert moved.length() == 5;
assert text.length() == 5;
}
Current compiler output:
tests/copy/string_move_error.zx:10:8: error: use of moved value `text`
|
9 | _ = moved.length();
10 | _ = text.length();
| ^^^^
11 | }
|
Fix: use moved, or clone before moving when independent owned storage is
intended.
Program entry points should use the shorthand form when they do not need a precise library-style signature.
Current compiler warnings:
warning: explicit `-> void` on `main` is unnecessary; write `fn main()`
warning: `main` infers propagated errors; remove explicit `throws(...) -> void`
Fix:
fn main() {
try io.stdout.write("hello\n");
}
Library functions should still use precise fallible signatures. For the program
entry point, prefer bare main; the compiler accepts explicit fallible-main
signatures for now but warns because the entry shorthand already infers
propagated errors:
error AppError {
Failed
}
fn start() throws(AppError) -> void {
throw AppError.Failed;
}
The compiler warns when an explicit cast repeats an already known contextual type:
let byte: u8 = 1 as u8;
Current compiler output:
warning [unnecessary_cast]: unnecessary cast to `u8`; numeric literal already has this expected type
Fix:
let byte: u8 = 1;
Keep as for computed narrowing, distinct type conversions, raw pointer casts,
and generic type selection:
let low = (word & 0xff) as u8;
A final bare return; in a void-returning function also warns because
fallthrough is the canonical spelling. Guard and branch return; statements
remain valid.
Current compiler output:
warning [unnecessary_return]: final `return;` in void function is unnecessary
Some syntax may appear in old tests, internal fixtures, or editor grammars but is not normal source language:
- block comments
/* ... */ - dynamic runtime
require(...) - user-defined decorators
- public atomics
- broad C ABI forms such as managed strings, futures, or throwing foreign functions
Relevant excerpt from current compiler output for require(...):
tests/errors/require_dynamic_type_value_error.zx:2:15: error: dynamic require is not part of the current language; use import and locked packages
|
2 | const m = try require("librequire_types_mod.zxm");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3 | _ = m.Point;
|
When a diagnostic points at one of these forms, check Current Language Surface before assuming the feature is meant to work.