optionals

This commit is contained in:
agra
2026-02-22 22:16:30 +02:00
parent d3e574eae5
commit 1cc67f9b5a
17 changed files with 1952 additions and 32 deletions

116
specs.md
View File

@@ -429,7 +429,7 @@ set_x :: (p: *Vec2, val: f32) {
set_x(@v, 99.0);
```
**Null**: All pointer types are nullable. `null` is the null pointer literal.
**Null**: Pointer types are currently nullable by default. `null` is the null pointer literal.
```sx
np : *Vec2 = null;
```
@@ -451,6 +451,120 @@ val := mp[2]; // 30
**Fat pointer layout**: `[:0]u8`, `string`, and `[]T` are `{ptr, i64}` structs. The raw pointer is always the first field at offset 0. This means `*[:0]u8` works as C's `char**` — a C function dereferences through the outer pointer and reads the raw `char*` from offset 0.
### Optional Types
Optional types represent values that may or may not be present.
#### Type Syntax
```sx
x: ?s32 = 42; // optional s32, has value
y: ?s32 = null; // optional s32, no value
```
Any type `T` can be made optional: `?s32`, `?string`, `?Point`, `?*T`, `?[]T`.
#### LLVM Representation
- Non-pointer optionals (`?s32`, `?Point`): `{ T, i1 }` struct — payload + has_value flag
- Pointer optionals (`?*T`): bare pointer — null represents absence
#### Implicit Wrapping
A value of type `T` implicitly converts to `?T`:
```sx
wrap :: (n: s32) -> ?s32 {
if n > 0 { return n; } // s32 → ?s32 (wraps)
return null; // null → ?s32
}
```
#### Force Unwrap (`!`)
Extracts the payload, traps at runtime if null:
```sx
x: ?s32 = 42;
val := x!; // val : s32 = 42
```
#### Null Coalescing (`??`)
Returns the payload if present, otherwise evaluates the right-hand side:
```sx
x: ?s32 = 42;
y: ?s32 = null;
a := x ?? 0; // 42
b := y ?? 99; // 99
```
#### Safe Unwrap (`if val := expr`)
Binds the payload to a variable if present:
```sx
x: ?s32 = 42;
if val := x {
print("{}\n", val); // val : s32 = 42
} else {
print("none\n");
}
```
#### While-Optional Binding
```sx
while val := get_next() {
// val is the unwrapped value
}
```
#### Pattern Matching
Optionals support `.some` and `.none` virtual enum variants:
```sx
result := if opt == {
case .some: (val) { val * 2; }
case .none: { 0; }
};
```
#### Optional Chaining (`?.`)
Short-circuits field access on optionals:
```sx
x: ?Point = Point.{ x = 1, y = 2 };
y: ?Point = null;
a := x?.x ?? 0; // 1
b := y?.x ?? 0; // 0
```
Result type of `x?.field` is always `?FieldType`.
#### Flow-Sensitive Narrowing
The compiler narrows `?T` to `T` in control flow branches:
```sx
x: ?s32 = 42;
if x != null {
print("{}\n", x); // x is s32 here (narrowed)
}
if x == null { return; }
print("{}\n", x); // x is s32 here (guard narrowing)
```
Compound conditions:
```sx
if a != null and b != null {
// both a and b are narrowed to their inner types
}
if a == null or b == null { return; }
// both a and b are narrowed after the guard
```
Reassignment kills narrowing.
#### Struct Field Defaults
Optional fields in structs default to `null`:
```sx
Node :: struct { value: s32; next: ?s32; }
n := Node.{ value = 10 }; // n.next is null
```
#### Printing
`print("{}", opt)` prints the payload value if present, or `"null"`.
#### Comptime
Optionals work in `#run` blocks — `??`, `!`, `if val :=`, null checks all supported.
### Foreign Function Interface (C Interop)
To call C functions, declare a library constant with `#library` and bind functions with `#foreign`: