enum, union

This commit is contained in:
agra
2026-02-14 13:17:22 +02:00
parent 4ff828fd1a
commit 025b790411
14 changed files with 537 additions and 245 deletions

126
specs.md
View File

@@ -48,6 +48,8 @@ GLSL;
### Keywords
`if`, `else`, `then`, `while`, `break`, `continue`, `true`, `false`, `enum`, `struct`, `union`, `case`, `return`, `defer`, `xx`, `and`, `or`
> Note: `enum` is used for both payload-less and payload-bearing sum types (tagged unions). `union` is reserved for C-style untagged unions (memory overlays).
### Operators
| Operator | Meaning |
@@ -102,15 +104,88 @@ GLSL;
- `Type` — compile-time type value. At runtime, represented as an `i64` type tag (same tag space as `Any`).
### Enum Types
User-defined sum types with named variants.
User-defined sum types with named variants. Variants may optionally carry typed data (tagged unions). Internally, payload-less enums are represented as `i64` (variant index). Enums with payloads are represented as `{ i64, [max_payload_size x i8] }` (tag + data).
#### Declaration
```sx
Foo :: enum {
variant1;
variant2;
// Payload-less enum
Color :: enum {
red;
green;
blue;
}
// Enum with payloads (tagged union)
Shape :: enum {
circle: f32; // typed variant
rect: s32; // typed variant
none; // void variant
}
```
Variants are referenced with dot-prefix syntax: `.variant1`
#### Construction
```sx
c := Color.red; // payload-less
s :Shape = .circle(3.14); // inferred from context
s = .none; // void variant
s = Shape.rect(42); // explicit prefix
```
#### Payload Access
```sx
r := s.circle; // load payload as f32 (undefined behavior if wrong variant active)
```
#### Pattern Matching
```sx
if s == {
case .circle: print("circle\n");
case .rect: print("rect\n");
case .none: print("none\n");
}
```
#### Enum Interpolation
Payload-less enums print as `.variant`. Enums with payloads print as `.variant(value)` or `<TypeName tag=N>`:
```sx
print("{}", s); // .circle(3.140000)
```
### Union Types (Untagged)
C-style untagged unions for zero-cost memory overlays (type punning). All fields share the same memory — no tag, no runtime overhead. The LLVM representation is `[max_field_size x i8]`.
#### Declaration
```sx
Overlay :: union {
f: f32;
i: s32;
}
```
All fields must have types (unlike enums, which may have void variants).
#### Anonymous Struct Fields (Member Promotion)
Anonymous `struct` fields inside a union have their members promoted to the union namespace:
```sx
Vec2 :: union {
data: [2]f32;
struct { x, y: f32; };
}
```
Access promoted members directly: `v.x`, `v.y` — these are zero-cost GEPs into the same underlying memory as `v.data[0]`, `v.data[1]`.
#### Initialization
Unions must be initialized with `---` (undefined) and then assigned per-field:
```sx
o :Overlay = ---;
o.f = 3.14;
print("{}\n", o.i); // reinterpret bits as s32
```
#### Restrictions
- Pattern matching (`if x == { case ... }`) is not supported on unions.
- Unions cannot be printed directly via `print("{}", union_val)` — access individual fields instead.
### Struct Types
User-defined product types with named fields.
```sx
@@ -159,45 +234,6 @@ Struct values in string interpolation print as `TypeName{field:value, ...}`:
print("{}", v1); // Vec4{x:1.0, y:2.0, z:3.0, w:0.0}
```
### Union Types (Tagged Unions)
Sum types where each variant can carry typed data or be void. Internally represented as `{ i64, [max_payload_size x i8] }`.
#### Declaration
```sx
Shape :: union {
circle: f32; // typed variant
rect: s32; // typed variant
none; // void variant
}
```
#### Construction
```sx
s :Shape = .circle(3.14); // inferred from context
s = .none; // void variant (enum literal syntax)
s = Shape.rect(42); // explicit prefix
```
#### Payload Access
```sx
r := s.circle; // load payload as f32 (undefined behavior if wrong variant active)
```
#### Pattern Matching
```sx
if s == {
case .circle: print("circle\n");
case .rect: print("rect\n");
case .none: print("none\n");
}
```
#### Union Interpolation
Union values in string interpolation print as `<TypeName tag=N>`:
```sx
print("{}", s); // <Shape tag=0>
```
### Array Types
Fixed-size arrays with element type and length.
```sx
@@ -623,7 +659,9 @@ if type == {
case enum: result = enum_to_string(cast(type) val);
}
```
Available categories: `int`, `float`, `bool`, `string`, `struct`, `enum`, `union`.
Available categories: `int`, `float`, `bool`, `string`, `struct`, `enum`, `vector`, `array`, `slice`, `pointer`, `type`.
> Note: `case enum:` matches both payload-less enums and tagged enums (enums with payloads). C-style untagged unions are not registered with the Any type system and cannot be matched by category.
Inside a category arm, `cast(type) val` performs **runtime generic dispatch**: the compiler generates a switch over all types in the category, monomorphizing the callee for each concrete type.