extend default to s64

This commit is contained in:
agra
2026-02-11 01:05:21 +02:00
parent 70435d3c85
commit 25e1372731
11 changed files with 754 additions and 208 deletions

View File

@@ -1,5 +1,5 @@
#import "modules/std.sx"; #import "modules/std.sx";
math :: #import "modules/std/math.sx"; math :: #import "modules/math.sx";
vec3 :: (x:f32, y:f32, z:f32) -> Vector(3,f32) { vec3 :: (x:f32, y:f32, z:f32) -> Vector(3,f32) {
.[x, y, z]; .[x, y, z];

View File

@@ -1,12 +1,27 @@
#import "modules/std.sx"; #import "modules/std.sx";
#import "modules/math.sx"; #import "modules/math.sx";
test :: () -> s32 {
0;
}
Vec4 :: Vector(4,f32);
main :: () { main :: () {
x:Type = f64; x : Type = f64;
v:f64 = 3.2; v:f64 = 3.2;
print("{}\n", x); print("{}\n", x);
print("{}\n", v); print("{}\n", v);
x= Vec4; x = Vec4;
print("{}\n", x);
x = test;
print("{}\n", x); print("{}\n", x);
} }
// ** stdout **
// f64
// 3.200000
// Vec4
// () -> s32

View File

@@ -11,9 +11,12 @@ Color :: struct {
main :: () { main :: () {
p := Point.{10, 20}; p := Point.{10, 20};
c := Color.{255, 128, 0}; c := Color.{255, 128, 0};
pc := &p;
print("p: {}\n", p); print("p: {}\n", p);
print("c: {}\n", c); print("c: {}\n", c);
print("n: {}\n", 42); print("n: {}\n", 42);
print("s: {}\n", "hello"); print("s: {}\n", "hello");
print("b: {}\n", true); print("b: {}\n", true);
print("&p: {}\n", pc);
print("&p: {}\n", &p);
} }

View File

@@ -1,7 +1,7 @@
#import "modules/std.sx"; #import "modules/std.sx";
quickSort :: (items: []$T) { quick_sort :: (items: []$T) {
partition :: (items: []T, lo: s32, hi: s32) -> s32 { partition :: (items: []T, lo: s64, hi: s64) -> s64 {
pivot := items[hi]; pivot := items[hi];
i := lo - 1; i := lo - 1;
j := lo; j := lo;
@@ -21,7 +21,7 @@ quickSort :: (items: []$T) {
i; i;
} }
sort :: (items: []T, lo: s32, hi: s32) { sort :: (items: []T, lo: s64, hi: s64) {
if lo < hi { if lo < hi {
pi := partition(items, lo, hi); pi := partition(items, lo, hi);
sort(items, lo, pi - 1); sort(items, lo, pi - 1);
@@ -33,7 +33,7 @@ quickSort :: (items: []$T) {
} }
main :: () { main :: () {
arr : []s32 = .[333, 2, 3, 5, 2, 2, 3, 4, 5, 6, 6, 1]; arr : []s64 = .[333, 2, 3, 5, 2, 2, 3, 4, 5, 6, 6, 1];
quickSort(arr); quick_sort(arr);
print("{}\n", arr); print("{}\n", arr);
} }

12
examples/24-list.sx Normal file
View File

@@ -0,0 +1,12 @@
#import "modules/std.sx";
List :: struct ($T: Type) {
items: [*]T = .[];
len: s32 = 0;
cap: s32 = 0;
}
main :: () {
list := List(s32).{};
print("{}\n", list);
}

31
examples/25-slices.sx Normal file
View File

@@ -0,0 +1,31 @@
#import "modules/std.sx";
main :: () {
arr : [5]s32 = .[3, 1, 4, 1, 5];
print("arr.len = {}\n", arr.len);
// subslice array
sub := arr[1..4];
print("arr[1..4] = {}\n", sub);
print("sub.len = {}\n", sub.len);
// open-ended
head := arr[..3];
tail := arr[2..];
print("arr[..3] = {}\n", head);
print("arr[2..] = {}\n", tail);
// slice of slice
sl : []s32 = .[10, 20, 30, 40, 50];
mid := sl[1..4];
print("sl[1..4] = {}\n", mid);
rest := mid[1..];
print("mid[1..] = {}\n", rest);
// string subslicing
msg := "hello world";
word := msg[6..11];
print("msg[6..11] = {}\n", word);
prefix := msg[..5];
print("msg[..5] = {}\n", prefix);
}

View File

@@ -1,15 +1,15 @@
Vector :: ($N: int, $T: Type) -> Type #builtin; Vector :: ($N: int, $T: Type) -> Type #builtin;
write :: (str: string) -> void #builtin; write :: (str: string) -> void #builtin;
sqrt :: (x: $T) -> T #builtin; sqrt :: (x: $T) -> T #builtin;
size_of :: ($T: Type) -> s32 #builtin; size_of :: ($T: Type) -> s64 #builtin;
alloc :: (size: s32) -> string #builtin; alloc :: (size: s64) -> string #builtin;
type_of :: (val: $T) -> Type #builtin; type_of :: (val: $T) -> Type #builtin;
type_name :: ($T: Type) -> string #builtin; type_name :: ($T: Type) -> string #builtin;
field_count :: ($T: Type) -> s32 #builtin; field_count :: ($T: Type) -> s64 #builtin;
field_name :: ($T: Type, idx: s32) -> string #builtin; field_name :: ($T: Type, idx: s64) -> string #builtin;
field_value :: (s: $T, idx: s32) -> Any #builtin; field_value :: (s: $T, idx: s64) -> Any #builtin;
int_to_string :: (n: s32) -> string { int_to_string :: (n: s64) -> string {
if n == 0 { return "0"; } if n == 0 { return "0"; }
neg := n < 0; neg := n < 0;
v := if neg then 0 - n else n; v := if neg then 0 - n else n;
@@ -35,8 +35,8 @@ bool_to_string :: (b: bool) -> string {
float_to_string :: (f: f64) -> string { float_to_string :: (f: f64) -> string {
neg := f < 0.0; neg := f < 0.0;
v := if neg then 0.0 - f else f; v := if neg then 0.0 - f else f;
int_part := cast(s32) v; int_part := cast(s64) v;
frac := cast(s32) ((v - cast(f64) int_part) * 1000000.0); frac := cast(s64) ((v - cast(f64) int_part) * 1000000.0);
if frac < 0 { frac = 0 - frac; } if frac < 0 { frac = 0 - frac; }
istr := int_to_string(int_part); istr := int_to_string(int_part);
fstr := int_to_string(frac); fstr := int_to_string(frac);
@@ -59,6 +59,68 @@ float_to_string :: (f: f64) -> string {
buf; buf;
} }
int_to_hex_string :: (n: s64) -> string {
if n == 0 { return "0"; }
// Split into four 16-bit groups for correct unsigned treatment
g0 := n % 65536;
if g0 < 0 { g0 = g0 + 65536; }
r1 := (n - g0) / 65536;
g1 := r1 % 65536;
if g1 < 0 { g1 = g1 + 65536; }
r2 := (r1 - g1) / 65536;
g2 := r2 % 65536;
if g2 < 0 { g2 = g2 + 65536; }
r3 := (r2 - g2) / 65536;
g3 := r3 % 65536;
if g3 < 0 { g3 = g3 + 65536; }
buf := alloc(16);
// Group 3: digits 0-3 (bits 48-63)
i := 3;
v := g3;
while i >= 0 {
d := v % 16;
buf[i] = if d < 10 then d + 48 else d - 10 + 97;
v = v / 16;
i -= 1;
}
// Group 2: digits 4-7 (bits 32-47)
i = 7;
v = g2;
while i >= 4 {
d := v % 16;
buf[i] = if d < 10 then d + 48 else d - 10 + 97;
v = v / 16;
i -= 1;
}
// Group 1: digits 8-11 (bits 16-31)
i = 11;
v = g1;
while i >= 8 {
d := v % 16;
buf[i] = if d < 10 then d + 48 else d - 10 + 97;
v = v / 16;
i -= 1;
}
// Group 0: digits 12-15 (bits 0-15)
i = 15;
v = g0;
while i >= 12 {
d := v % 16;
buf[i] = if d < 10 then d + 48 else d - 10 + 97;
v = v / 16;
i -= 1;
}
// Skip leading zeros (keep at least 1 digit)
start := 0;
while start < 15 {
if buf[start] != 48 { break; }
start += 1;
}
substr(buf, start, 16 - start);
}
concat :: (a: string, b: string) -> string { concat :: (a: string, b: string) -> string {
al := a.len; al := a.len;
bl := b.len; bl := b.len;
@@ -70,7 +132,7 @@ concat :: (a: string, b: string) -> string {
buf; buf;
} }
substr :: (s: string, start: s32, len: s32) -> string { substr :: (s: string, start: s64, len: s64) -> string {
buf := alloc(len); buf := alloc(len);
i := 0; i := 0;
while i < len { while i < len {
@@ -94,7 +156,7 @@ struct_to_string :: (s: $T) -> string {
} }
enum_to_string :: (e: $T) -> string { enum_to_string :: (e: $T) -> string {
concat(".", field_name(T, cast(s32) e)); concat(".", field_name(T, cast(s64) e));
} }
vector_to_string :: (v: $T) -> string { vector_to_string :: (v: $T) -> string {
@@ -130,8 +192,13 @@ slice_to_string :: (items: []$T) -> string {
concat(result, "]"); concat(result, "]");
} }
pointer_to_string :: (p: $T) -> string {
addr : s64 = xx p;
concat(type_name(T), concat("@", int_to_hex_string(addr)));
}
union_to_string :: (u: $T) -> string { union_to_string :: (u: $T) -> string {
tag := cast(s32) u; tag := cast(s64) u;
result := concat(".", field_name(T, tag)); result := concat(".", field_name(T, tag));
payload := field_value(u, tag); payload := field_value(u, tag);
pstr := any_to_string(payload); pstr := any_to_string(payload);
@@ -156,6 +223,8 @@ any_to_string :: (val: Any) -> string {
case array: result = array_to_string(cast(type) val); case array: result = array_to_string(cast(type) val);
case slice: result = slice_to_string(cast(type) val); case slice: result = slice_to_string(cast(type) val);
case union: result = union_to_string(cast(type) val); case union: result = union_to_string(cast(type) val);
case pointer: result = pointer_to_string(cast(type) val);
case type: { s : string = xx val; result = s; }
} }
result; result;
} }

View File

@@ -17,7 +17,7 @@ Line comments start with `//` and extend to end of line.
| Kind | Examples | Type | | Kind | Examples | Type |
|-----------|---------------------|---------| |-----------|---------------------|---------|
| Integer | `0`, `42`, `0xFF`, `0b1010` | `s32` | | Integer | `0`, `42`, `0xFF`, `0b1010` | `s64` |
| Float | `0.3`, `0.9` | `f32` | | Float | `0.3`, `0.9` | `f32` |
| String | `"Hello"`, `"z: {z}"` | `string` | | String | `"Hello"`, `"z: {z}"` | `string` |
| Boolean | `true`, `false` | `bool` | | Boolean | `true`, `false` | `bool` |
@@ -71,14 +71,14 @@ Line comments start with `//` and extend to end of line.
## 2. Type System ## 2. Type System
### Primitive Types ### Primitive Types
- `s1`..`s64` — signed integers (1 to 64 bits). `s32` is the default for integer literals. - `s1`..`s64` — signed integers (1 to 64 bits). `s64` is the default for integer literals.
- `u1`..`u64` — unsigned integers (1 to 64 bits). - `u1`..`u64` — unsigned integers (1 to 64 bits).
- `f32` — 32-bit floating point - `f32` — 32-bit floating point
- `f64` — 64-bit floating point - `f64` — 64-bit floating point
- `bool` — boolean (`true` / `false`) - `bool` — boolean (`true` / `false`)
- `string` — string of characters - `string` — string of characters
- `Any` — type-erased value, represented as `{ i32, i64 }` (type tag + payload). Used for variadic arguments and runtime type dispatch. - `Any` — type-erased value, represented as `{ i64, i64 }` (type tag + payload). Used for variadic arguments and runtime type dispatch.
- `Type` — compile-time type value. At runtime, represented as an `i32` type tag (same tag space as `Any`). - `Type` — compile-time type value. At runtime, represented as an `i64` type tag (same tag space as `Any`).
### Enum Types ### Enum Types
User-defined sum types with named variants. User-defined sum types with named variants.
@@ -139,7 +139,7 @@ print("{}", v1); // Vec4{x:1.0, y:2.0, z:3.0, w:0.0}
``` ```
### Union Types (Tagged Unions) ### Union Types (Tagged Unions)
Sum types where each variant can carry typed data or be void. Internally represented as `{ i32, [max_payload_size x i8] }`. Sum types where each variant can carry typed data or be void. Internally represented as `{ i64, [max_payload_size x i8] }`.
#### Declaration #### Declaration
```sx ```sx
@@ -182,7 +182,7 @@ Fixed-size arrays with element type and length.
```sx ```sx
buffer : [5]f32 = .[0, 2, 3.5, 4, 0]; buffer : [5]f32 = .[0, 2, 3.5, 4, 0];
val := buffer[2]; // 3.5 val := buffer[2]; // 3.5
buffer.len // 5 (compile-time constant, s32) buffer.len // 5 (compile-time constant, s64)
``` ```
Arrays can also be constructed programmatically with the `Array` builtin: Arrays can also be constructed programmatically with the `Array` builtin:
@@ -191,7 +191,7 @@ MyArr :: Array(5, s32); // equivalent to [5]s32
``` ```
### Slice Types ### Slice Types
A slice `[]T` is a fat pointer `{ptr, i32}` referencing a contiguous sequence of `T` elements. Same runtime layout as `string`. A slice `[]T` is a fat pointer `{ptr, i64}` referencing a contiguous sequence of `T` elements. Same runtime layout as `string`.
```sx ```sx
// Arrays implicitly coerce to slices at call sites // Arrays implicitly coerce to slices at call sites
arr : [5]s32 = .[3, 1, 4, 1, 5]; arr : [5]s32 = .[3, 1, 4, 1, 5];
@@ -200,7 +200,7 @@ sortSlice(arr); // [5]s32 → []s32 coercion
// Slice operations // Slice operations
items[i] // read element at index items[i] // read element at index
items[i] = val; // write element at index items[i] = val; // write element at index
items.len // length (s32) items.len // length (s64)
items.ptr // raw pointer items.ptr // raw pointer
``` ```
@@ -352,7 +352,7 @@ print :: (fmt: string, args: ..Any) { ... }
- `::` bindings infer type from the right-hand side - `::` bindings infer type from the right-hand side
- `:=` bindings infer type from the right-hand side - `:=` bindings infer type from the right-hand side
- Explicit annotation overrides inference: `NAME : f64 : 0.9;` - Explicit annotation overrides inference: `NAME : f64 : 0.9;`
- Integer literals default to `s32` - Integer literals default to `s64`
- Float literals default to `f32` - Float literals default to `f32`
- Enum literals (`.variant`) infer their enum type from context (expected type) - Enum literals (`.variant`) infer their enum type from context (expected type)
@@ -609,13 +609,13 @@ while i < 10 {
```sx ```sx
for iterable { for iterable {
// `it` is the current element // `it` is the current element
// `it_index` is the current index (s32) // `it_index` is the current index (s64)
print("{it}\n"); print("{it}\n");
} }
``` ```
Iterates over arrays and slices. The loop body has two implicit variables: Iterates over arrays and slices. The loop body has two implicit variables:
- `it` — the current element value - `it` — the current element value
- `it_index` — the current index (s32, starting at 0) - `it_index` — the current index (s64, starting at 0)
`break;` exits the loop. `continue;` skips to the next iteration. `break;` exits the loop. `continue;` skips to the next iteration.
```sx ```sx
@@ -730,15 +730,15 @@ Built-in functions are declared in `std.sx` with the `#builtin` suffix, which te
- `sqrt(x: $T) -> T` — square root (maps to LLVM intrinsic) - `sqrt(x: $T) -> T` — square root (maps to LLVM intrinsic)
### Memory ### Memory
- `alloc(size: s32) -> string` — allocate `size` bytes of memory, returned as a string slice - `alloc(size: s64) -> string` — allocate `size` bytes of memory, returned as a string slice
- `size_of($T: Type) -> s32` — size of type `T` in bytes - `size_of($T: Type) -> s64` — size of type `T` in bytes
### Type Introspection ### Type Introspection
- `type_of(val: $T) -> Type` — returns the runtime type tag of a value - `type_of(val: $T) -> Type` — returns the runtime type tag of a value
- `type_name($T: Type) -> string` — returns the name of type `T` as a string (e.g., `"Point"`) - `type_name($T: Type) -> string` — returns the name of type `T` as a string (e.g., `"Point"`)
- `field_count($T: Type) -> s32` — returns the number of fields (struct), variants (enum), or elements (vector) in type `T` - `field_count($T: Type) -> s64` — returns the number of fields (struct), variants (enum), or elements (vector) in type `T`
- `field_name($T: Type, idx: s32) -> string` — returns the name of the `idx`-th field (struct) or variant (enum) of type `T` - `field_name($T: Type, idx: s64) -> string` — returns the name of the `idx`-th field (struct) or variant (enum) of type `T`
- `field_value(s: $T, idx: s32) -> Any` — returns the `idx`-th field (struct) or element (vector) of `s`, boxed as `Any` - `field_value(s: $T, idx: s64) -> Any` — returns the `idx`-th field (struct) or element (vector) of `s`, boxed as `Any`
### Type Conversion ### Type Conversion
- `cast(Type) expr` — prefix operator that converts `expr` to `Type`. Examples: `cast(s32) 3.14`, `cast(f64) n`. When `Type` is a runtime `Type` value inside a type-category match arm, the compiler generates a dispatch switch over all types in the category, monomorphizing the callee for each concrete type. - `cast(Type) expr` — prefix operator that converts `expr` to `Type`. Examples: `cast(s32) 3.14`, `cast(f64) n`. When `Type` is a runtime `Type` value inside a type-category match arm, the compiler generates a dispatch switch over all types in the category, monomorphizing the callee for each concrete type.

View File

@@ -58,6 +58,8 @@ pub const CodeGen = struct {
namespaces: std.StringHashMap(void), namespaces: std.StringHashMap(void),
// Functions declared with #builtin (only available when imported) // Functions declared with #builtin (only available when imported)
builtin_functions: std.StringHashMap(void), builtin_functions: std.StringHashMap(void),
// Function signatures: maps function name to display signature (e.g. "test :: () -> s32")
fn_signatures: std.StringHashMap([]const u8),
// Active namespace during body generation of imported modules // Active namespace during body generation of imported modules
current_namespace: ?[]const u8 = null, current_namespace: ?[]const u8 = null,
// Diagnostics list (optional, for structured error reporting) // Diagnostics list (optional, for structured error reporting)
@@ -102,6 +104,7 @@ pub const CodeGen = struct {
vector_cat, vector_cat,
array_cat, array_cat,
slice_cat, slice_cat,
pointer_cat,
}; };
const AnyTypeEntry = struct { const AnyTypeEntry = struct {
@@ -181,6 +184,7 @@ pub const CodeGen = struct {
.generic_struct_templates = std.StringHashMap(GenericStructTemplate).init(allocator), .generic_struct_templates = std.StringHashMap(GenericStructTemplate).init(allocator),
.namespaces = std.StringHashMap(void).init(allocator), .namespaces = std.StringHashMap(void).init(allocator),
.builtin_functions = std.StringHashMap(void).init(allocator), .builtin_functions = std.StringHashMap(void).init(allocator),
.fn_signatures = std.StringHashMap([]const u8).init(allocator),
.variadic_functions = std.StringHashMap(VariadicInfo).init(allocator), .variadic_functions = std.StringHashMap(VariadicInfo).init(allocator),
.fn_param_types = std.StringHashMap([]const Type).init(allocator), .fn_param_types = std.StringHashMap([]const Type).init(allocator),
.any_type_id_map = std.StringHashMap(u64).init(allocator), .any_type_id_map = std.StringHashMap(u64).init(allocator),
@@ -201,6 +205,7 @@ pub const CodeGen = struct {
self.generic_struct_templates.deinit(); self.generic_struct_templates.deinit();
self.namespaces.deinit(); self.namespaces.deinit();
self.builtin_functions.deinit(); self.builtin_functions.deinit();
self.fn_signatures.deinit();
self.variadic_functions.deinit(); self.variadic_functions.deinit();
self.any_type_id_map.deinit(); self.any_type_id_map.deinit();
self.any_type_entries.deinit(); self.any_type_entries.deinit();
@@ -229,7 +234,7 @@ pub const CodeGen = struct {
.void_type => c.LLVMVoidTypeInContext(self.context), .void_type => c.LLVMVoidTypeInContext(self.context),
.boolean => c.LLVMInt1TypeInContext(self.context), .boolean => c.LLVMInt1TypeInContext(self.context),
.string_type, .slice_type => self.getStringStructType(), // slices use same {ptr, i32} layout .string_type, .slice_type => self.getStringStructType(), // slices use same {ptr, i32} layout
.enum_type => c.LLVMInt32TypeInContext(self.context), .enum_type => c.LLVMInt64TypeInContext(self.context),
.struct_type => |name| if (self.struct_types.get(name)) |info| info.llvm_type else unreachable, .struct_type => |name| if (self.struct_types.get(name)) |info| info.llvm_type else unreachable,
.union_type => |name| if (self.union_types.get(name)) |info| info.llvm_type else unreachable, .union_type => |name| if (self.union_types.get(name)) |info| info.llvm_type else unreachable,
.array_type => |info| { .array_type => |info| {
@@ -249,7 +254,7 @@ pub const CodeGen = struct {
fn getAnyStructType(self: *CodeGen) c.LLVMTypeRef { fn getAnyStructType(self: *CodeGen) c.LLVMTypeRef {
if (self.any_struct_type) |t| return t; if (self.any_struct_type) |t| return t;
var field_types = [_]c.LLVMTypeRef{ var field_types = [_]c.LLVMTypeRef{
c.LLVMInt32TypeInContext(self.context), // type tag c.LLVMInt64TypeInContext(self.context), // type tag
c.LLVMInt64TypeInContext(self.context), // value (fits all primitives) c.LLVMInt64TypeInContext(self.context), // value (fits all primitives)
}; };
self.any_struct_type = c.LLVMStructTypeInContext(self.context, &field_types, 2, 0); self.any_struct_type = c.LLVMStructTypeInContext(self.context, &field_types, 2, 0);
@@ -285,6 +290,7 @@ pub const CodeGen = struct {
.vector_type => .vector_cat, .vector_type => .vector_cat,
.array_type => .array_cat, .array_type => .array_cat,
.slice_type => .slice_cat, .slice_type => .slice_cat,
.pointer_type, .many_pointer_type => .pointer_cat,
else => .struct_cat, // fallback else => .struct_cat, // fallback
}; };
try self.any_type_entries.put(name, .{ try self.any_type_entries.put(name, .{
@@ -320,6 +326,8 @@ pub const CodeGen = struct {
.vector_type => |info| _ = try self.getAnyTypeId(try std.fmt.allocPrint(self.allocator, "vec[{d}]{s}", .{ info.length, info.element_name }), sx_type), .vector_type => |info| _ = try self.getAnyTypeId(try std.fmt.allocPrint(self.allocator, "vec[{d}]{s}", .{ info.length, info.element_name }), sx_type),
.array_type => |info| _ = try self.getAnyTypeId(try std.fmt.allocPrint(self.allocator, "[{d}]{s}", .{ info.length, info.element_name }), sx_type), .array_type => |info| _ = try self.getAnyTypeId(try std.fmt.allocPrint(self.allocator, "[{d}]{s}", .{ info.length, info.element_name }), sx_type),
.slice_type => |info| _ = try self.getAnyTypeId(try std.fmt.allocPrint(self.allocator, "[]{s}", .{info.element_name}), sx_type), .slice_type => |info| _ = try self.getAnyTypeId(try std.fmt.allocPrint(self.allocator, "[]{s}", .{info.element_name}), sx_type),
.pointer_type => |info| _ = try self.getAnyTypeId(try std.fmt.allocPrint(self.allocator, "*{s}", .{info.pointee_name}), sx_type),
.many_pointer_type => |info| _ = try self.getAnyTypeId(try std.fmt.allocPrint(self.allocator, "[*]{s}", .{info.element_name}), sx_type),
else => {}, else => {},
} }
} }
@@ -329,7 +337,6 @@ pub const CodeGen = struct {
/// Complex values (strings, structs, unions) are stored via pointer (alloca + ptr-to-int). /// Complex values (strings, structs, unions) are stored via pointer (alloca + ptr-to-int).
fn buildAnyValue(self: *CodeGen, val: c.LLVMValueRef, ty: Type) !c.LLVMValueRef { fn buildAnyValue(self: *CodeGen, val: c.LLVMValueRef, ty: Type) !c.LLVMValueRef {
const any_ty = self.getAnyStructType(); const any_ty = self.getAnyStructType();
const i32_ty = c.LLVMInt32TypeInContext(self.context);
const i64_ty = c.LLVMInt64TypeInContext(self.context); const i64_ty = c.LLVMInt64TypeInContext(self.context);
const undef = c.LLVMGetUndef(any_ty); const undef = c.LLVMGetUndef(any_ty);
@@ -353,7 +360,7 @@ pub const CodeGen = struct {
.meta_type => ANY_TAG_TYPE, .meta_type => ANY_TAG_TYPE,
else => ANY_TAG_S32, else => ANY_TAG_S32,
}; };
const tag_val = c.LLVMConstInt(i32_ty, tag, 0); const tag_val = c.LLVMConstInt(i64_ty, tag, 0);
const with_tag = c.LLVMBuildInsertValue(self.builder, undef, tag_val, 0, "any_tag"); const with_tag = c.LLVMBuildInsertValue(self.builder, undef, tag_val, 0, "any_tag");
// Convert value to i64 // Convert value to i64
@@ -413,9 +420,12 @@ pub const CodeGen = struct {
break :blk c.LLVMBuildPtrToInt(self.builder, alloca, i64_ty, "any_slice"); break :blk c.LLVMBuildPtrToInt(self.builder, alloca, i64_ty, "any_slice");
}, },
.pointer_type, .many_pointer_type => c.LLVMBuildPtrToInt(self.builder, val, i64_ty, "any_ptr"), .pointer_type, .many_pointer_type => c.LLVMBuildPtrToInt(self.builder, val, i64_ty, "any_ptr"),
.meta_type => blk: { .meta_type => |mt| blk: {
// Meta type is a pointer (global string) — convert via ptrtoint // Meta type: wrap raw char ptr in string slice {ptr, len} for extraction
break :blk c.LLVMBuildPtrToInt(self.builder, val, i64_ty, "any_type"); const str_slice = self.buildStringSlice(val, mt.name.len);
const alloca = c.LLVMBuildAlloca(self.builder, self.getStringStructType(), "any_type_tmp");
_ = c.LLVMBuildStore(self.builder, str_slice, alloca);
break :blk c.LLVMBuildPtrToInt(self.builder, alloca, i64_ty, "any_type");
}, },
else => c.LLVMBuildSExt(self.builder, val, i64_ty, "any_val"), else => c.LLVMBuildSExt(self.builder, val, i64_ty, "any_val"),
}; };
@@ -426,18 +436,18 @@ pub const CodeGen = struct {
if (self.string_struct_type) |t| return t; if (self.string_struct_type) |t| return t;
var field_types = [_]c.LLVMTypeRef{ var field_types = [_]c.LLVMTypeRef{
c.LLVMPointerTypeInContext(self.context, 0), // ptr c.LLVMPointerTypeInContext(self.context, 0), // ptr
c.LLVMInt32TypeInContext(self.context), // len c.LLVMInt64TypeInContext(self.context), // len
}; };
self.string_struct_type = c.LLVMStructTypeInContext(self.context, &field_types, 2, 0); self.string_struct_type = c.LLVMStructTypeInContext(self.context, &field_types, 2, 0);
return self.string_struct_type.?; return self.string_struct_type.?;
} }
/// Build a string slice {ptr, len} from a raw pointer and a constant length. /// Build a string slice {ptr, len} from a raw pointer and a constant length.
fn buildStringSlice(self: *CodeGen, ptr: c.LLVMValueRef, len: u32) c.LLVMValueRef { fn buildStringSlice(self: *CodeGen, ptr: c.LLVMValueRef, len: u64) c.LLVMValueRef {
const str_ty = self.getStringStructType(); const str_ty = self.getStringStructType();
const undef = c.LLVMGetUndef(str_ty); const undef = c.LLVMGetUndef(str_ty);
const with_ptr = c.LLVMBuildInsertValue(self.builder, undef, ptr, 0, "str_ptr"); const with_ptr = c.LLVMBuildInsertValue(self.builder, undef, ptr, 0, "str_ptr");
const len_val = c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), len, 0); const len_val = c.LLVMConstInt(c.LLVMInt64TypeInContext(self.context), len, 0);
return c.LLVMBuildInsertValue(self.builder, with_ptr, len_val, 1, "str_slice"); return c.LLVMBuildInsertValue(self.builder, with_ptr, len_val, 1, "str_slice");
} }
@@ -525,6 +535,7 @@ pub const CodeGen = struct {
} else { } else {
try self.registerFnDecl(fd); try self.registerFnDecl(fd);
} }
try self.fn_signatures.put(fd.name, self.buildFnSignature(fd));
}, },
.enum_decl => |ed| { .enum_decl => |ed| {
try self.enum_types.put(ed.name, ed.variants); try self.enum_types.put(ed.name, ed.variants);
@@ -560,6 +571,14 @@ pub const CodeGen = struct {
} else if (result_ty.isUnion()) { } else if (result_ty.isUnion()) {
try self.type_aliases.put(cd.name, result_ty.union_type); try self.type_aliases.put(cd.name, result_ty.union_type);
} }
} else if (self.builtin_functions.contains(cn)) {
// Builtin type function (e.g., Vector(4, f32), Array(5, s32))
if (self.resolveBuiltinType(cn, cd.value.data.call.args)) |result_ty| {
const display = try result_ty.displayName(self.allocator);
try self.type_aliases.put(cd.name, display);
} else {
try self.registerTopLevelConstant(cd);
}
} else { } else {
try self.registerTopLevelConstant(cd); try self.registerTopLevelConstant(cd);
} }
@@ -727,6 +746,8 @@ pub const CodeGen = struct {
break :blk self.buildStringSlice(ptr, @intCast(v.len)); break :blk self.buildStringSlice(ptr, @intCast(v.len));
}, },
.void_val => c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0), .void_val => c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0),
.pointer_val => c.LLVMConstNull(c.LLVMPointerTypeInContext(self.context, 0)),
.null_val => c.LLVMConstNull(c.LLVMPointerTypeInContext(self.context, 0)),
.struct_val, .array_val, .type_val, .function_val => unreachable, .struct_val, .array_val, .type_val, .function_val => unreachable,
}; };
} }
@@ -1084,10 +1105,10 @@ pub const CodeGen = struct {
const name_z = try self.allocator.dupeZ(u8, mangled_name); const name_z = try self.allocator.dupeZ(u8, mangled_name);
const union_ty = c.LLVMStructCreateNamed(self.context, name_z.ptr); const union_ty = c.LLVMStructCreateNamed(self.context, name_z.ptr);
const i32_ty = c.LLVMInt32TypeInContext(self.context); const i64_ty = c.LLVMInt64TypeInContext(self.context);
const i8_ty = c.LLVMInt8TypeInContext(self.context); const i8_ty = c.LLVMInt8TypeInContext(self.context);
const payload_array_ty = c.LLVMArrayType2(i8_ty, max_payload_size); const payload_array_ty = c.LLVMArrayType2(i8_ty, max_payload_size);
var fields = [2]c.LLVMTypeRef{ i32_ty, payload_array_ty }; var fields = [2]c.LLVMTypeRef{ i64_ty, payload_array_ty };
c.LLVMStructSetBody(union_ty, &fields, 2, 0); c.LLVMStructSetBody(union_ty, &fields, 2, 0);
try self.union_types.put(mangled_name, .{ try self.union_types.put(mangled_name, .{
@@ -1273,8 +1294,8 @@ pub const CodeGen = struct {
// For function calls, look up the registered function's return type // For function calls, look up the registered function's return type
if (expr.data == .call) { if (expr.data == .call) {
if (self.resolveCalleeName(expr.data.call)) |callee_name| { if (self.resolveCalleeName(expr.data.call)) |callee_name| {
const callee_name_z = self.allocator.dupeZ(u8, callee_name) catch return Type.s(32); const callee_name_z = self.allocator.dupeZ(u8, callee_name) catch return Type.s(64);
const callee_fn = c.LLVMGetNamedFunction(self.module, callee_name_z.ptr) orelse return Type.s(32); const callee_fn = c.LLVMGetNamedFunction(self.module, callee_name_z.ptr) orelse return Type.s(64);
const fn_type = c.LLVMGlobalGetValueType(callee_fn); const fn_type = c.LLVMGlobalGetValueType(callee_fn);
const ret_llvm = c.LLVMGetReturnType(fn_type); const ret_llvm = c.LLVMGetReturnType(fn_type);
return self.llvmTypeToSxType(ret_llvm); return self.llvmTypeToSxType(ret_llvm);
@@ -1315,7 +1336,7 @@ pub const CodeGen = struct {
const elem_llvm = c.LLVMGetElementType(llvm_ty); const elem_llvm = c.LLVMGetElementType(llvm_ty);
const length: u32 = @intCast(c.LLVMGetArrayLength2(llvm_ty)); const length: u32 = @intCast(c.LLVMGetArrayLength2(llvm_ty));
const elem_ty = self.llvmTypeToSxType(elem_llvm); const elem_ty = self.llvmTypeToSxType(elem_llvm);
const elem_name = elem_ty.displayName(self.allocator) catch return Type.s(32); const elem_name = elem_ty.displayName(self.allocator) catch return Type.s(64);
return .{ .array_type = .{ .element_name = elem_name, .length = length } }; return .{ .array_type = .{ .element_name = elem_name, .length = length } };
} }
// Check for vector types // Check for vector types
@@ -1323,10 +1344,10 @@ pub const CodeGen = struct {
const elem_llvm = c.LLVMGetElementType(llvm_ty); const elem_llvm = c.LLVMGetElementType(llvm_ty);
const length = c.LLVMGetVectorSize(llvm_ty); const length = c.LLVMGetVectorSize(llvm_ty);
const elem_ty = self.llvmTypeToSxType(elem_llvm); const elem_ty = self.llvmTypeToSxType(elem_llvm);
const elem_name = elem_ty.displayName(self.allocator) catch return Type.s(32); const elem_name = elem_ty.displayName(self.allocator) catch return Type.s(64);
return .{ .vector_type = .{ .element_name = elem_name, .length = length } }; return .{ .vector_type = .{ .element_name = elem_name, .length = length } };
} }
return Type.s(32); return Type.s(64);
} }
fn registerComptimeGlobal(self: *CodeGen, name: []const u8, expr: *Node, type_override: ?Type) !void { fn registerComptimeGlobal(self: *CodeGen, name: []const u8, expr: *Node, type_override: ?Type) !void {
@@ -1673,7 +1694,7 @@ pub const CodeGen = struct {
if (vd.value) |val| { if (vd.value) |val| {
const meta_name = self.asTypeName(val); const meta_name = self.asTypeName(val);
if (meta_name) |raw_name| { if (meta_name) |raw_name| {
const type_name = self.resolveDisplayName(raw_name); const type_name = try self.allocator.dupeZ(u8, raw_name);
const name_z = try self.allocator.dupeZ(u8, vd.name); const name_z = try self.allocator.dupeZ(u8, vd.name);
const ptr_ty = c.LLVMPointerTypeInContext(self.context, 0); const ptr_ty = c.LLVMPointerTypeInContext(self.context, 0);
const alloca = c.LLVMBuildAlloca(self.builder, ptr_ty, name_z.ptr); const alloca = c.LLVMBuildAlloca(self.builder, ptr_ty, name_z.ptr);
@@ -1685,7 +1706,7 @@ pub const CodeGen = struct {
} }
} }
var sx_ty: Type = Type.s(32); var sx_ty: Type = Type.s(64);
if (vd.type_annotation) |ta| { if (vd.type_annotation) |ta| {
sx_ty = self.resolveType(ta); sx_ty = self.resolveType(ta);
@@ -1928,7 +1949,7 @@ pub const CodeGen = struct {
return null; return null;
} }
var sx_ty: Type = Type.s(32); var sx_ty: Type = Type.s(64);
if (cd.type_annotation) |ta| { if (cd.type_annotation) |ta| {
sx_ty = self.resolveType(ta); sx_ty = self.resolveType(ta);
@@ -1989,14 +2010,22 @@ pub const CodeGen = struct {
return self.emitErrorFmt("undefined variable '{s}'", .{name}); return self.emitErrorFmt("undefined variable '{s}'", .{name});
}; };
// Meta type reassignment: x = Vec4 or x = f64 // Meta type reassignment: x = Vec4, x = f64, x = test
if (entry.ty == .meta_type and asgn.op == .assign) { if (entry.ty == .meta_type and asgn.op == .assign) {
if (self.asTypeName(asgn.value)) |raw_name| { const raw_name = self.asTypeName(asgn.value) orelse blk: {
const type_name = self.resolveDisplayName(raw_name); // Also accept function names as meta_type values (use signature)
if (asgn.value.data == .identifier) {
const fn_name = asgn.value.data.identifier.name;
if (self.fn_signatures.get(fn_name)) |sig| break :blk sig;
}
break :blk @as(?[]const u8, null);
};
if (raw_name) |rn| {
const type_name = try self.allocator.dupeZ(u8, rn);
const str_val = c.LLVMBuildGlobalStringPtr(self.builder, type_name.ptr, "type_name"); const str_val = c.LLVMBuildGlobalStringPtr(self.builder, type_name.ptr, "type_name");
_ = c.LLVMBuildStore(self.builder, str_val, entry.ptr); _ = c.LLVMBuildStore(self.builder, str_val, entry.ptr);
if (self.named_values.getPtr(name)) |entry_ptr| { if (self.named_values.getPtr(name)) |entry_ptr| {
entry_ptr.ty = .{ .meta_type = .{ .name = raw_name } }; entry_ptr.ty = .{ .meta_type = .{ .name = rn } };
} }
return null; return null;
} }
@@ -2258,8 +2287,8 @@ pub const CodeGen = struct {
self.current_span = node.span; self.current_span = node.span;
switch (node.data) { switch (node.data) {
.int_literal => |lit| { .int_literal => |lit| {
const i32_type = c.LLVMInt32TypeInContext(self.context); const i64_type = c.LLVMInt64TypeInContext(self.context);
return c.LLVMConstInt(i32_type, @bitCast(@as(i64, lit.value)), 0); return c.LLVMConstInt(i64_type, @bitCast(@as(i64, lit.value)), 0);
}, },
.float_literal => |lit| { .float_literal => |lit| {
const f32_type = c.LLVMFloatTypeInContext(self.context); const f32_type = c.LLVMFloatTypeInContext(self.context);
@@ -2617,13 +2646,13 @@ pub const CodeGen = struct {
} }
} }
// Union LLVM type: { i32, [max_payload_size x i8] } // Union LLVM type: { i64, [max_payload_size x i8] }
const name_z = try self.allocator.dupeZ(u8, ud.name); const name_z = try self.allocator.dupeZ(u8, ud.name);
const union_ty = c.LLVMStructCreateNamed(self.context, name_z.ptr); const union_ty = c.LLVMStructCreateNamed(self.context, name_z.ptr);
const i32_ty = c.LLVMInt32TypeInContext(self.context); const i64_ty = c.LLVMInt64TypeInContext(self.context);
const i8_ty = c.LLVMInt8TypeInContext(self.context); const i8_ty = c.LLVMInt8TypeInContext(self.context);
const payload_array_ty = c.LLVMArrayType2(i8_ty, max_payload_size); const payload_array_ty = c.LLVMArrayType2(i8_ty, max_payload_size);
var fields = [2]c.LLVMTypeRef{ i32_ty, payload_array_ty }; var fields = [2]c.LLVMTypeRef{ i64_ty, payload_array_ty };
c.LLVMStructSetBody(union_ty, &fields, 2, 0); c.LLVMStructSetBody(union_ty, &fields, 2, 0);
try self.union_types.put(ud.name, .{ try self.union_types.put(ud.name, .{
@@ -2654,11 +2683,11 @@ pub const CodeGen = struct {
// Alloca union // Alloca union
const alloca = c.LLVMBuildAlloca(self.builder, info.llvm_type, "union_tmp"); const alloca = c.LLVMBuildAlloca(self.builder, info.llvm_type, "union_tmp");
const i32_ty = c.LLVMInt32TypeInContext(self.context); const i64_ty = c.LLVMInt64TypeInContext(self.context);
// Store tag (field 0) // Store tag (field 0)
const tag_gep = c.LLVMBuildStructGEP2(self.builder, info.llvm_type, alloca, 0, "tag"); const tag_gep = c.LLVMBuildStructGEP2(self.builder, info.llvm_type, alloca, 0, "tag");
_ = c.LLVMBuildStore(self.builder, c.LLVMConstInt(i32_ty, idx, 0), tag_gep); _ = c.LLVMBuildStore(self.builder, c.LLVMConstInt(i64_ty, idx, 0), tag_gep);
// Store payload (field 1) if not void // Store payload (field 1) if not void
if (ul.payload) |payload_node| { if (ul.payload) |payload_node| {
@@ -2790,14 +2819,14 @@ pub const CodeGen = struct {
} }
// Build slice {ptr, len} // Build slice {ptr, len}
const i32_ty = c.LLVMInt32TypeInContext(self.context); const i64_ty = c.LLVMInt64TypeInContext(self.context);
const zero = c.LLVMConstInt(i32_ty, 0, 0); const zero = c.LLVMConstInt(i64_ty, 0, 0);
var gep_indices = [_]c.LLVMValueRef{ zero, zero }; var gep_indices = [_]c.LLVMValueRef{ zero, zero };
const elem_ptr = c.LLVMBuildGEP2(self.builder, llvm_arr_ty, arr_alloca, &gep_indices, 2, "slice_data"); const elem_ptr = c.LLVMBuildGEP2(self.builder, llvm_arr_ty, arr_alloca, &gep_indices, 2, "slice_data");
const slice_llvm_ty = self.getStringStructType(); const slice_llvm_ty = self.getStringStructType();
var slice_val = c.LLVMGetUndef(slice_llvm_ty); var slice_val = c.LLVMGetUndef(slice_llvm_ty);
slice_val = c.LLVMBuildInsertValue(self.builder, slice_val, elem_ptr, 0, "slice_ptr"); slice_val = c.LLVMBuildInsertValue(self.builder, slice_val, elem_ptr, 0, "slice_ptr");
const len_val = c.LLVMConstInt(i32_ty, n, 0); const len_val = c.LLVMConstInt(i64_ty, n, 0);
slice_val = c.LLVMBuildInsertValue(self.builder, slice_val, len_val, 1, "slice_len"); slice_val = c.LLVMBuildInsertValue(self.builder, slice_val, len_val, 1, "slice_len");
return slice_val; return slice_val;
} }
@@ -2942,15 +2971,15 @@ pub const CodeGen = struct {
break :blk try self.genExpr(node); break :blk try self.genExpr(node);
}; };
// GEP to get pointer to first element // GEP to get pointer to first element
const i32_ty = c.LLVMInt32TypeInContext(self.context); const i64_ty = c.LLVMInt64TypeInContext(self.context);
const zero = c.LLVMConstInt(i32_ty, 0, 0); const zero = c.LLVMConstInt(i64_ty, 0, 0);
var indices = [_]c.LLVMValueRef{ zero, zero }; var indices = [_]c.LLVMValueRef{ zero, zero };
const elem_ptr = c.LLVMBuildGEP2(self.builder, self.typeToLLVM(src_ty), arr_alloca, &indices, 2, "arr_data"); const elem_ptr = c.LLVMBuildGEP2(self.builder, self.typeToLLVM(src_ty), arr_alloca, &indices, 2, "arr_data");
// Build slice struct {ptr, len} // Build slice struct {ptr, len}
const slice_ty = self.getStringStructType(); const slice_ty = self.getStringStructType();
var slice_val = c.LLVMGetUndef(slice_ty); var slice_val = c.LLVMGetUndef(slice_ty);
slice_val = c.LLVMBuildInsertValue(self.builder, slice_val, elem_ptr, 0, "slice_ptr"); slice_val = c.LLVMBuildInsertValue(self.builder, slice_val, elem_ptr, 0, "slice_ptr");
const len_val = c.LLVMConstInt(i32_ty, arr_info.length, 0); const len_val = c.LLVMConstInt(i64_ty, arr_info.length, 0);
slice_val = c.LLVMBuildInsertValue(self.builder, slice_val, len_val, 1, "slice_len"); slice_val = c.LLVMBuildInsertValue(self.builder, slice_val, len_val, 1, "slice_len");
return slice_val; return slice_val;
} }
@@ -3022,7 +3051,7 @@ pub const CodeGen = struct {
} }
} }
if (target_ty.isEnum()) { if (target_ty.isEnum()) {
return c.LLVMBuildTrunc(self.builder, i64_val, c.LLVMInt32TypeInContext(self.context), "any_to_enum"); return i64_val;
} }
if (target_ty.isUnion()) { if (target_ty.isUnion()) {
const uname = target_ty.union_type; const uname = target_ty.union_type;
@@ -3031,6 +3060,9 @@ pub const CodeGen = struct {
return c.LLVMBuildLoad2(self.builder, info.llvm_type, ptr, "any_to_union"); return c.LLVMBuildLoad2(self.builder, info.llvm_type, ptr, "any_to_union");
} }
} }
if (target_ty.isPointer() or target_ty.isManyPointer()) {
return c.LLVMBuildIntToPtr(self.builder, i64_val, c.LLVMPointerTypeInContext(self.context, 0), "any_to_ptr");
}
return i64_val; return i64_val;
} }
@@ -3061,6 +3093,15 @@ pub const CodeGen = struct {
} }
} }
// Pointer → int: PtrToInt
if ((src_ty.isPointer() or src_ty.isManyPointer()) and target_ty.isInt()) {
const as_i64 = c.LLVMBuildPtrToInt(self.builder, val, c.LLVMInt64TypeInContext(self.context), "ptrtoint");
if (target_ty.bitWidth() < 64) {
return c.LLVMBuildTrunc(self.builder, as_i64, target_llvm, "ptr_trunc");
}
return as_i64;
}
// Union → int: extract the tag field (index 0) // Union → int: extract the tag field (index 0)
if (src_ty.isUnion() and target_ty.isInt()) { if (src_ty.isUnion() and target_ty.isInt()) {
const uname = src_ty.union_type; const uname = src_ty.union_type;
@@ -3149,19 +3190,19 @@ pub const CodeGen = struct {
if (call_node.args.len != 1) return self.emitError("size_of expects exactly 1 argument"); if (call_node.args.len != 1) return self.emitError("size_of expects exactly 1 argument");
const ty = self.resolveType(call_node.args[0]); const ty = self.resolveType(call_node.args[0]);
if (std.meta.eql(ty, Type.void_type)) { if (std.meta.eql(ty, Type.void_type)) {
return c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0); return c.LLVMConstInt(c.LLVMInt64TypeInContext(self.context), 0, 0);
} }
const llvm_ty = self.typeToLLVM(ty); const llvm_ty = self.typeToLLVM(ty);
const data_layout = c.LLVMGetModuleDataLayout(self.module); const data_layout = c.LLVMGetModuleDataLayout(self.module);
const size = c.LLVMStoreSizeOfType(data_layout, llvm_ty); const size = c.LLVMStoreSizeOfType(data_layout, llvm_ty);
return c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), size, 0); return c.LLVMConstInt(c.LLVMInt64TypeInContext(self.context), size, 0);
} }
fn genTypeOf(self: *CodeGen, call_node: ast.Call) !c.LLVMValueRef { fn genTypeOf(self: *CodeGen, call_node: ast.Call) !c.LLVMValueRef {
if (call_node.args.len != 1) return self.emitError("type_of expects exactly 1 argument"); if (call_node.args.len != 1) return self.emitError("type_of expects exactly 1 argument");
const arg = call_node.args[0]; const arg = call_node.args[0];
const arg_ty = self.inferType(arg); const arg_ty = self.inferType(arg);
const i32_ty = c.LLVMInt32TypeInContext(self.context); const i64_ty = c.LLVMInt64TypeInContext(self.context);
// For Any values: extract the runtime tag (field 0) // For Any values: extract the runtime tag (field 0)
if (arg_ty.isAny()) { if (arg_ty.isAny()) {
@@ -3184,7 +3225,7 @@ pub const CodeGen = struct {
.meta_type => ANY_TAG_TYPE, .meta_type => ANY_TAG_TYPE,
else => ANY_TAG_S32, else => ANY_TAG_S32,
}; };
return c.LLVMConstInt(i32_ty, tag, 0); return c.LLVMConstInt(i64_ty, tag, 0);
} }
fn genTypeName(self: *CodeGen, call_node: ast.Call) !c.LLVMValueRef { fn genTypeName(self: *CodeGen, call_node: ast.Call) !c.LLVMValueRef {
@@ -3197,27 +3238,27 @@ pub const CodeGen = struct {
fn genFieldCount(self: *CodeGen, call_node: ast.Call) !c.LLVMValueRef { fn genFieldCount(self: *CodeGen, call_node: ast.Call) !c.LLVMValueRef {
if (call_node.args.len != 1) return self.emitError("field_count expects exactly 1 argument"); if (call_node.args.len != 1) return self.emitError("field_count expects exactly 1 argument");
const ty = self.resolveType(call_node.args[0]); const ty = self.resolveType(call_node.args[0]);
const i32_ty = c.LLVMInt32TypeInContext(self.context); const i64_ty = c.LLVMInt64TypeInContext(self.context);
if (ty.isStruct()) { if (ty.isStruct()) {
const info = self.struct_types.get(ty.struct_type) orelse const info = self.struct_types.get(ty.struct_type) orelse
return self.emitErrorFmt("unknown struct type '{s}'", .{ty.struct_type}); return self.emitErrorFmt("unknown struct type '{s}'", .{ty.struct_type});
return c.LLVMConstInt(i32_ty, info.field_names.len, 0); return c.LLVMConstInt(i64_ty, info.field_names.len, 0);
} }
if (ty.isEnum()) { if (ty.isEnum()) {
const variants = self.enum_types.get(ty.enum_type) orelse const variants = self.enum_types.get(ty.enum_type) orelse
return self.emitErrorFmt("unknown enum type '{s}'", .{ty.enum_type}); return self.emitErrorFmt("unknown enum type '{s}'", .{ty.enum_type});
return c.LLVMConstInt(i32_ty, variants.len, 0); return c.LLVMConstInt(i64_ty, variants.len, 0);
} }
if (ty.isVector()) { if (ty.isVector()) {
return c.LLVMConstInt(i32_ty, ty.vector_type.length, 0); return c.LLVMConstInt(i64_ty, ty.vector_type.length, 0);
} }
if (ty.isUnion()) { if (ty.isUnion()) {
const info = self.union_types.get(ty.union_type) orelse const info = self.union_types.get(ty.union_type) orelse
return self.emitErrorFmt("unknown union type '{s}'", .{ty.union_type}); return self.emitErrorFmt("unknown union type '{s}'", .{ty.union_type});
return c.LLVMConstInt(i32_ty, info.variant_names.len, 0); return c.LLVMConstInt(i64_ty, info.variant_names.len, 0);
} }
if (ty.isArray()) { if (ty.isArray()) {
return c.LLVMConstInt(i32_ty, ty.array_type.length, 0); return c.LLVMConstInt(i64_ty, ty.array_type.length, 0);
} }
return self.emitError("field_count requires a struct, enum, vector, union, or array type"); return self.emitError("field_count requires a struct, enum, vector, union, or array type");
} }
@@ -3294,7 +3335,7 @@ pub const CodeGen = struct {
// Read tag (field 0) // Read tag (field 0)
const tag_ptr = c.LLVMBuildStructGEP2(self.builder, uinfo.llvm_type, union_alloca, 0, "fv_tag_ptr"); const tag_ptr = c.LLVMBuildStructGEP2(self.builder, uinfo.llvm_type, union_alloca, 0, "fv_tag_ptr");
const tag_val = c.LLVMBuildLoad2(self.builder, c.LLVMInt32TypeInContext(self.context), tag_ptr, "fv_tag"); const tag_val = c.LLVMBuildLoad2(self.builder, c.LLVMInt64TypeInContext(self.context), tag_ptr, "fv_tag");
const payload_ptr = c.LLVMBuildStructGEP2(self.builder, uinfo.llvm_type, union_alloca, 1, "fv_payload_ptr"); const payload_ptr = c.LLVMBuildStructGEP2(self.builder, uinfo.llvm_type, union_alloca, 1, "fv_payload_ptr");
const n = uinfo.variant_names.len; const n = uinfo.variant_names.len;
@@ -3309,13 +3350,13 @@ pub const CodeGen = struct {
for (uinfo.variant_types, 0..) |vty, vi| { for (uinfo.variant_types, 0..) |vty, vi| {
const case_bb = c.LLVMAppendBasicBlockInContext(self.context, function, "fv_ucase"); const case_bb = c.LLVMAppendBasicBlockInContext(self.context, function, "fv_ucase");
c.LLVMAddCase(sw, c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), @intCast(vi), 0), case_bb); c.LLVMAddCase(sw, c.LLVMConstInt(c.LLVMInt64TypeInContext(self.context), @intCast(vi), 0), case_bb);
c.LLVMPositionBuilderAtEnd(self.builder, case_bb); c.LLVMPositionBuilderAtEnd(self.builder, case_bb);
const any_val = if (vty == .void_type) blk: { const any_val = if (vty == .void_type) blk: {
// Void variant: return Any with void tag // Void variant: return Any with void tag
const undef = c.LLVMGetUndef(any_ty); const undef = c.LLVMGetUndef(any_ty);
const void_tag = c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), ANY_TAG_VOID, 0); const void_tag = c.LLVMConstInt(c.LLVMInt64TypeInContext(self.context), ANY_TAG_VOID, 0);
const with_tag = c.LLVMBuildInsertValue(self.builder, undef, void_tag, 0, "void_tag"); const with_tag = c.LLVMBuildInsertValue(self.builder, undef, void_tag, 0, "void_tag");
const zero_val = c.LLVMConstInt(c.LLVMInt64TypeInContext(self.context), 0, 0); const zero_val = c.LLVMConstInt(c.LLVMInt64TypeInContext(self.context), 0, 0);
break :blk c.LLVMBuildInsertValue(self.builder, with_tag, zero_val, 1, "void_any"); break :blk c.LLVMBuildInsertValue(self.builder, with_tag, zero_val, 1, "void_any");
@@ -3403,7 +3444,7 @@ pub const CodeGen = struct {
for (0..n) |i| { for (0..n) |i| {
const case_bb = c.LLVMAppendBasicBlockInContext(self.context, function, "fv_case"); const case_bb = c.LLVMAppendBasicBlockInContext(self.context, function, "fv_case");
const case_val = c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), i, 0); const case_val = c.LLVMConstInt(c.LLVMInt64TypeInContext(self.context), i, 0);
c.LLVMAddCase(sw, case_val, case_bb); c.LLVMAddCase(sw, case_val, case_bb);
c.LLVMPositionBuilderAtEnd(self.builder, case_bb); c.LLVMPositionBuilderAtEnd(self.builder, case_bb);
@@ -3444,11 +3485,9 @@ pub const CodeGen = struct {
const builtins = self.builtins orelse return self.emitError("builtins not available (missing #builtin import)"); const builtins = self.builtins orelse return self.emitError("builtins not available (missing #builtin import)");
const size_val = try self.genExpr(args[0]); const size_val = try self.genExpr(args[0]);
const i64_type = c.LLVMInt64TypeInContext(self.context); const i64_type = c.LLVMInt64TypeInContext(self.context);
// Extend size to i64 for calloc
const size_i64 = c.LLVMBuildSExt(self.builder, size_val, i64_type, "size64");
// calloc(size + 1, 1) — extra byte for null terminator // calloc(size + 1, 1) — extra byte for null terminator
const one_i64 = c.LLVMConstInt(i64_type, 1, 0); const one_i64 = c.LLVMConstInt(i64_type, 1, 0);
const size_plus_one = c.LLVMBuildAdd(self.builder, size_i64, one_i64, "szp1"); const size_plus_one = c.LLVMBuildAdd(self.builder, size_val, one_i64, "szp1");
const calloc_fn = builtins.calloc_fn; const calloc_fn = builtins.calloc_fn;
const calloc_ty = c.LLVMGlobalGetValueType(calloc_fn); const calloc_ty = c.LLVMGlobalGetValueType(calloc_fn);
var calloc_args = [_]c.LLVMValueRef{ size_plus_one, one_i64 }; var calloc_args = [_]c.LLVMValueRef{ size_plus_one, one_i64 };
@@ -3549,7 +3588,7 @@ pub const CodeGen = struct {
} }
if (entry.ty.isArray()) { if (entry.ty.isArray()) {
if (std.mem.eql(u8, fa.field, "len")) { if (std.mem.eql(u8, fa.field, "len")) {
return c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), entry.ty.array_type.length, 0); return c.LLVMConstInt(c.LLVMInt64TypeInContext(self.context), entry.ty.array_type.length, 0);
} }
return self.emitErrorFmt("no field '{s}' on array (available: .len)", .{fa.field}); return self.emitErrorFmt("no field '{s}' on array (available: .len)", .{fa.field});
} }
@@ -3623,15 +3662,14 @@ pub const CodeGen = struct {
} }
} }
if (obj_ty == .string_type) { if (obj_ty == .string_type) {
// String indexing: extract ptr from slice, GEP + load i8 + zext to i32 // String indexing: extract ptr from slice, GEP + load u8
const str_val = try self.genExpr(ie.object); const str_val = try self.genExpr(ie.object);
const ptr = c.LLVMBuildExtractValue(self.builder, str_val, 0, "str_ptr"); const ptr = c.LLVMBuildExtractValue(self.builder, str_val, 0, "str_ptr");
const idx = try self.genExpr(ie.index); const idx = try self.genExpr(ie.index);
const i8_type = c.LLVMInt8TypeInContext(self.context); const i8_type = c.LLVMInt8TypeInContext(self.context);
var gep_indices = [_]c.LLVMValueRef{idx}; var gep_indices = [_]c.LLVMValueRef{idx};
const gep = c.LLVMBuildGEP2(self.builder, i8_type, ptr, &gep_indices, 1, "stridx"); const gep = c.LLVMBuildGEP2(self.builder, i8_type, ptr, &gep_indices, 1, "stridx");
const byte = c.LLVMBuildLoad2(self.builder, i8_type, gep, "byte"); return c.LLVMBuildLoad2(self.builder, i8_type, gep, "byte");
return c.LLVMBuildZExt(self.builder, byte, c.LLVMInt32TypeInContext(self.context), "char");
} }
if (obj_ty.isSlice()) { if (obj_ty.isSlice()) {
// Slice indexing: extract ptr, GEP with element type, load // Slice indexing: extract ptr, GEP with element type, load
@@ -3672,8 +3710,8 @@ pub const CodeGen = struct {
fn genSliceExpr(self: *CodeGen, se: ast.SliceExpr) !c.LLVMValueRef { fn genSliceExpr(self: *CodeGen, se: ast.SliceExpr) !c.LLVMValueRef {
const obj_ty = self.inferType(se.object); const obj_ty = self.inferType(se.object);
const i32_ty = c.LLVMInt32TypeInContext(self.context); const i64_ty = c.LLVMInt64TypeInContext(self.context);
const zero = c.LLVMConstInt(i32_ty, 0, 0); const zero = c.LLVMConstInt(i64_ty, 0, 0);
const slice_struct_ty = self.getStringStructType(); const slice_struct_ty = self.getStringStructType();
// Resolve start (default: 0) // Resolve start (default: 0)
@@ -3682,7 +3720,7 @@ pub const CodeGen = struct {
if (obj_ty.isArray()) { if (obj_ty.isArray()) {
const arr_info = obj_ty.array_type; const arr_info = obj_ty.array_type;
// Resolve end (default: array length) // Resolve end (default: array length)
const end_val = if (se.end) |e| try self.genExpr(e) else c.LLVMConstInt(i32_ty, arr_info.length, 0); const end_val = if (se.end) |e| try self.genExpr(e) else c.LLVMConstInt(i64_ty, arr_info.length, 0);
// Get array alloca // Get array alloca
const arr_ptr = blk: { const arr_ptr = blk: {
if (se.object.data == .identifier) { if (se.object.data == .identifier) {
@@ -4042,7 +4080,7 @@ pub const CodeGen = struct {
} }
} }
// Pack variadic args into a slice {ptr, len} // Pack variadic args into a slice {ptr, len}
const elem_ty = Type.fromName(vi.element_type_name) orelse Type.s(32); const elem_ty = Type.fromName(vi.element_type_name) orelse Type.s(64);
const elem_llvm_ty = self.typeToLLVM(elem_ty); const elem_llvm_ty = self.typeToLLVM(elem_ty);
const var_arg_count = if (call_node.args.len > fixed_count) call_node.args.len - fixed_count else 0; const var_arg_count = if (call_node.args.len > fixed_count) call_node.args.len - fixed_count else 0;
@@ -4056,10 +4094,10 @@ pub const CodeGen = struct {
if (spread_operand.data == .identifier) { if (spread_operand.data == .identifier) {
if (self.named_values.get(spread_operand.data.identifier.name)) |entry| { if (self.named_values.get(spread_operand.data.identifier.name)) |entry| {
const arr_llvm_ty = self.typeToLLVM(spread_ty); const arr_llvm_ty = self.typeToLLVM(spread_ty);
const zero = c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0); const zero = c.LLVMConstInt(c.LLVMInt64TypeInContext(self.context), 0, 0);
var ptr_indices = [_]c.LLVMValueRef{ zero, zero }; var ptr_indices = [_]c.LLVMValueRef{ zero, zero };
const arr_ptr = c.LLVMBuildGEP2(self.builder, arr_llvm_ty, entry.ptr, &ptr_indices, 2, "spread_ptr"); const arr_ptr = c.LLVMBuildGEP2(self.builder, arr_llvm_ty, entry.ptr, &ptr_indices, 2, "spread_ptr");
const len_val = c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), arr_info.length, 0); const len_val = c.LLVMConstInt(c.LLVMInt64TypeInContext(self.context), arr_info.length, 0);
const slice_val = self.buildStringSliceRT(arr_ptr, len_val); const slice_val = self.buildStringSliceRT(arr_ptr, len_val);
try arg_vals.append(self.allocator, slice_val); try arg_vals.append(self.allocator, slice_val);
} else { } else {
@@ -4094,16 +4132,16 @@ pub const CodeGen = struct {
_ = c.LLVMBuildStore(self.builder, arg_val, gep); _ = c.LLVMBuildStore(self.builder, arg_val, gep);
} }
// Build slice: {ptr, len} // Build slice: {ptr, len}
const zero = c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0); const zero = c.LLVMConstInt(c.LLVMInt64TypeInContext(self.context), 0, 0);
var ptr_indices = [_]c.LLVMValueRef{ zero, zero }; var ptr_indices = [_]c.LLVMValueRef{ zero, zero };
const arr_ptr = c.LLVMBuildGEP2(self.builder, arr_ty, arr_alloca, &ptr_indices, 2, "varargs_ptr"); const arr_ptr = c.LLVMBuildGEP2(self.builder, arr_ty, arr_alloca, &ptr_indices, 2, "varargs_ptr");
const len_val = c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), @intCast(var_arg_count), 0); const len_val = c.LLVMConstInt(c.LLVMInt64TypeInContext(self.context), @intCast(var_arg_count), 0);
const slice_val = self.buildStringSliceRT(arr_ptr, len_val); const slice_val = self.buildStringSliceRT(arr_ptr, len_val);
try arg_vals.append(self.allocator, slice_val); try arg_vals.append(self.allocator, slice_val);
} else { } else {
// Zero variadic args: pass empty slice {null, 0} // Zero variadic args: pass empty slice {null, 0}
const null_ptr = c.LLVMConstNull(c.LLVMPointerTypeInContext(self.context, 0)); const null_ptr = c.LLVMConstNull(c.LLVMPointerTypeInContext(self.context, 0));
const zero_len = c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0); const zero_len = c.LLVMConstInt(c.LLVMInt64TypeInContext(self.context), 0, 0);
const slice_val = self.buildStringSliceRT(null_ptr, zero_len); const slice_val = self.buildStringSliceRT(null_ptr, zero_len);
try arg_vals.append(self.allocator, slice_val); try arg_vals.append(self.allocator, slice_val);
} }
@@ -4466,7 +4504,7 @@ pub const CodeGen = struct {
// Create case BB // Create case BB
const case_bb = c.LLVMAppendBasicBlockInContext(self.context, function, "dispatch_case"); const case_bb = c.LLVMAppendBasicBlockInContext(self.context, function, "dispatch_case");
c.LLVMAddCase(sw, c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), tag, 0), case_bb); c.LLVMAddCase(sw, c.LLVMConstInt(c.LLVMInt64TypeInContext(self.context), tag, 0), case_bb);
c.LLVMPositionBuilderAtEnd(self.builder, case_bb); c.LLVMPositionBuilderAtEnd(self.builder, case_bb);
// Convert Any payload to the concrete type // Convert Any payload to the concrete type
@@ -4594,7 +4632,7 @@ pub const CodeGen = struct {
const ptr = c.LLVMBuildIntToPtr(self.builder, any_i64, c.LLVMPointerTypeInContext(self.context, 0), "any_to_struct_ptr"); const ptr = c.LLVMBuildIntToPtr(self.builder, any_i64, c.LLVMPointerTypeInContext(self.context, 0), "any_to_struct_ptr");
break :blk c.LLVMBuildLoad2(self.builder, info.llvm_type, ptr, "any_to_struct"); break :blk c.LLVMBuildLoad2(self.builder, info.llvm_type, ptr, "any_to_struct");
}, },
.enum_type => c.LLVMBuildTrunc(self.builder, any_i64, c.LLVMInt32TypeInContext(self.context), "any_to_enum"), .enum_type => any_i64,
.union_type => |uname| blk: { .union_type => |uname| blk: {
const info = self.union_types.get(uname) orelse return self.emitErrorFmt("unknown union '{s}'", .{uname}); const info = self.union_types.get(uname) orelse return self.emitErrorFmt("unknown union '{s}'", .{uname});
const ptr = c.LLVMBuildIntToPtr(self.builder, any_i64, c.LLVMPointerTypeInContext(self.context, 0), "any_to_union_ptr"); const ptr = c.LLVMBuildIntToPtr(self.builder, any_i64, c.LLVMPointerTypeInContext(self.context, 0), "any_to_union_ptr");
@@ -4609,7 +4647,8 @@ pub const CodeGen = struct {
const ptr = c.LLVMBuildIntToPtr(self.builder, any_i64, c.LLVMPointerTypeInContext(self.context, 0), "any_to_slice_ptr"); const ptr = c.LLVMBuildIntToPtr(self.builder, any_i64, c.LLVMPointerTypeInContext(self.context, 0), "any_to_slice_ptr");
break :blk c.LLVMBuildLoad2(self.builder, self.getStringStructType(), ptr, "any_to_slice"); break :blk c.LLVMBuildLoad2(self.builder, self.getStringStructType(), ptr, "any_to_slice");
}, },
else => c.LLVMBuildTrunc(self.builder, any_i64, c.LLVMInt32TypeInContext(self.context), "any_to_default"), .pointer_type, .many_pointer_type => c.LLVMBuildIntToPtr(self.builder, any_i64, c.LLVMPointerTypeInContext(self.context, 0), "any_to_ptr"),
else => any_i64,
}; };
} }
@@ -4869,19 +4908,19 @@ pub const CodeGen = struct {
fn genForExpr(self: *CodeGen, for_expr: ast.ForExpr) !c.LLVMValueRef { fn genForExpr(self: *CodeGen, for_expr: ast.ForExpr) !c.LLVMValueRef {
const function = self.current_function; const function = self.current_function;
const i32_type = c.LLVMInt32TypeInContext(self.context); const i64_type = c.LLVMInt64TypeInContext(self.context);
// Determine iterable type and get length + element access info // Determine iterable type and get length + element access info
const iter_ty = self.inferType(for_expr.iterable); const iter_ty = self.inferType(for_expr.iterable);
var len_val: c.LLVMValueRef = undefined; var len_val: c.LLVMValueRef = undefined;
var elem_ty: Type = Type.s(32); var elem_ty: Type = Type.s(64);
var iter_ptr: c.LLVMValueRef = undefined; // pointer to data var iter_ptr: c.LLVMValueRef = undefined; // pointer to data
var is_slice = false; var is_slice = false;
if (iter_ty.isSlice()) { if (iter_ty.isSlice()) {
is_slice = true; is_slice = true;
const info = iter_ty.slice_type; const info = iter_ty.slice_type;
elem_ty = Type.fromName(info.element_name) orelse Type.s(32); elem_ty = Type.fromName(info.element_name) orelse Type.s(64);
// Load slice value from alloca // Load slice value from alloca
if (for_expr.iterable.data == .identifier) { if (for_expr.iterable.data == .identifier) {
if (self.named_values.get(for_expr.iterable.data.identifier.name)) |entry| { if (self.named_values.get(for_expr.iterable.data.identifier.name)) |entry| {
@@ -4892,8 +4931,8 @@ pub const CodeGen = struct {
} else return self.emitError("for: slice iterable must be a variable"); } else return self.emitError("for: slice iterable must be a variable");
} else if (iter_ty.isArray()) { } else if (iter_ty.isArray()) {
const info = iter_ty.array_type; const info = iter_ty.array_type;
elem_ty = Type.fromName(info.element_name) orelse Type.s(32); elem_ty = Type.fromName(info.element_name) orelse Type.s(64);
len_val = c.LLVMConstInt(i32_type, info.length, 0); len_val = c.LLVMConstInt(i64_type, info.length, 0);
// Get pointer to array // Get pointer to array
if (for_expr.iterable.data == .identifier) { if (for_expr.iterable.data == .identifier) {
if (self.named_values.get(for_expr.iterable.data.identifier.name)) |entry| { if (self.named_values.get(for_expr.iterable.data.identifier.name)) |entry| {
@@ -4906,15 +4945,15 @@ pub const CodeGen = struct {
const elem_llvm_ty = self.typeToLLVM(elem_ty); const elem_llvm_ty = self.typeToLLVM(elem_ty);
// Allocate it_index (s32) and it (element type) // Allocate it_index (s64) and it (element type)
const idx_alloca = c.LLVMBuildAlloca(self.builder, i32_type, "it_index"); const idx_alloca = c.LLVMBuildAlloca(self.builder, i64_type, "it_index");
_ = c.LLVMBuildStore(self.builder, c.LLVMConstInt(i32_type, 0, 0), idx_alloca); _ = c.LLVMBuildStore(self.builder, c.LLVMConstInt(i64_type, 0, 0), idx_alloca);
const it_alloca = c.LLVMBuildAlloca(self.builder, elem_llvm_ty, "it"); const it_alloca = c.LLVMBuildAlloca(self.builder, elem_llvm_ty, "it");
// Push scope and bind it, it_index // Push scope and bind it, it_index
try self.pushScope(); try self.pushScope();
try self.named_values.put("it", .{ .ptr = it_alloca, .ty = elem_ty }); try self.named_values.put("it", .{ .ptr = it_alloca, .ty = elem_ty });
try self.named_values.put("it_index", .{ .ptr = idx_alloca, .ty = Type.s(32) }); try self.named_values.put("it_index", .{ .ptr = idx_alloca, .ty = Type.s(64) });
// Create basic blocks // Create basic blocks
const cond_bb = c.LLVMAppendBasicBlockInContext(self.context, function, "for.cond"); const cond_bb = c.LLVMAppendBasicBlockInContext(self.context, function, "for.cond");
@@ -4925,13 +4964,13 @@ pub const CodeGen = struct {
// Condition: it_index < len // Condition: it_index < len
c.LLVMPositionBuilderAtEnd(self.builder, cond_bb); c.LLVMPositionBuilderAtEnd(self.builder, cond_bb);
const cur_idx = c.LLVMBuildLoad2(self.builder, i32_type, idx_alloca, "cur_idx"); const cur_idx = c.LLVMBuildLoad2(self.builder, i64_type, idx_alloca, "cur_idx");
const cond_val = c.LLVMBuildICmp(self.builder, c.LLVMIntSLT, cur_idx, len_val, "for_cond"); const cond_val = c.LLVMBuildICmp(self.builder, c.LLVMIntSLT, cur_idx, len_val, "for_cond");
_ = c.LLVMBuildCondBr(self.builder, cond_val, body_bb, after_bb); _ = c.LLVMBuildCondBr(self.builder, cond_val, body_bb, after_bb);
// Body: load it = iterable[it_index], then execute body // Body: load it = iterable[it_index], then execute body
c.LLVMPositionBuilderAtEnd(self.builder, body_bb); c.LLVMPositionBuilderAtEnd(self.builder, body_bb);
const body_idx = c.LLVMBuildLoad2(self.builder, i32_type, idx_alloca, "body_idx"); const body_idx = c.LLVMBuildLoad2(self.builder, i64_type, idx_alloca, "body_idx");
if (is_slice) { if (is_slice) {
// Slice: GEP through data pointer // Slice: GEP through data pointer
@@ -4942,7 +4981,7 @@ pub const CodeGen = struct {
} else { } else {
// Array: GEP with [0, idx] // Array: GEP with [0, idx]
const arr_llvm_ty = self.typeToLLVM(iter_ty); const arr_llvm_ty = self.typeToLLVM(iter_ty);
const zero = c.LLVMConstInt(i32_type, 0, 0); const zero = c.LLVMConstInt(i64_type, 0, 0);
var indices = [_]c.LLVMValueRef{ zero, body_idx }; var indices = [_]c.LLVMValueRef{ zero, body_idx };
const gep = c.LLVMBuildGEP2(self.builder, arr_llvm_ty, iter_ptr, &indices, 2, "for_elem"); const gep = c.LLVMBuildGEP2(self.builder, arr_llvm_ty, iter_ptr, &indices, 2, "for_elem");
const elem_val = c.LLVMBuildLoad2(self.builder, elem_llvm_ty, gep, "it_val"); const elem_val = c.LLVMBuildLoad2(self.builder, elem_llvm_ty, gep, "it_val");
@@ -4963,8 +5002,8 @@ pub const CodeGen = struct {
// Increment it_index // Increment it_index
const current_bb = c.LLVMGetInsertBlock(self.builder); const current_bb = c.LLVMGetInsertBlock(self.builder);
if (c.LLVMGetBasicBlockTerminator(current_bb) == null) { if (c.LLVMGetBasicBlockTerminator(current_bb) == null) {
const inc_idx = c.LLVMBuildLoad2(self.builder, i32_type, idx_alloca, "inc_idx"); const inc_idx = c.LLVMBuildLoad2(self.builder, i64_type, idx_alloca, "inc_idx");
const next_idx = c.LLVMBuildAdd(self.builder, inc_idx, c.LLVMConstInt(i32_type, 1, 0), "next_idx"); const next_idx = c.LLVMBuildAdd(self.builder, inc_idx, c.LLVMConstInt(i64_type, 1, 0), "next_idx");
_ = c.LLVMBuildStore(self.builder, next_idx, idx_alloca); _ = c.LLVMBuildStore(self.builder, next_idx, idx_alloca);
_ = c.LLVMBuildBr(self.builder, cond_bb); _ = c.LLVMBuildBr(self.builder, cond_bb);
} }
@@ -4977,14 +5016,14 @@ pub const CodeGen = struct {
} }
fn genEnumLiteral(self: *CodeGen, variant_name: []const u8, enum_type_name: []const u8) c.LLVMValueRef { fn genEnumLiteral(self: *CodeGen, variant_name: []const u8, enum_type_name: []const u8) c.LLVMValueRef {
const i32_type = c.LLVMInt32TypeInContext(self.context); const i64_type = c.LLVMInt64TypeInContext(self.context);
const variants = self.enum_types.get(enum_type_name) orelse return c.LLVMConstInt(i32_type, 0, 0); const variants = self.enum_types.get(enum_type_name) orelse return c.LLVMConstInt(i64_type, 0, 0);
for (variants, 0..) |v, i| { for (variants, 0..) |v, i| {
if (std.mem.eql(u8, v, variant_name)) { if (std.mem.eql(u8, v, variant_name)) {
return c.LLVMConstInt(i32_type, @intCast(i), 0); return c.LLVMConstInt(i64_type, @intCast(i), 0);
} }
} }
return c.LLVMConstInt(i32_type, 0, 0); return c.LLVMConstInt(i64_type, 0, 0);
} }
fn lookupVariantIndex(variants: ?[]const []const u8, name: []const u8) u64 { fn lookupVariantIndex(variants: ?[]const []const u8, name: []const u8) u64 {
@@ -5013,7 +5052,7 @@ pub const CodeGen = struct {
const entry = self.named_values.get(match.subject.data.identifier.name).?; const entry = self.named_values.get(match.subject.data.identifier.name).?;
const info = self.union_types.get(union_name.?).?; const info = self.union_types.get(union_name.?).?;
const tag_gep = c.LLVMBuildStructGEP2(self.builder, info.llvm_type, entry.ptr, 0, "tag"); const tag_gep = c.LLVMBuildStructGEP2(self.builder, info.llvm_type, entry.ptr, 0, "tag");
break :blk c.LLVMBuildLoad2(self.builder, c.LLVMInt32TypeInContext(self.context), tag_gep, "tag_val"); break :blk c.LLVMBuildLoad2(self.builder, c.LLVMInt64TypeInContext(self.context), tag_gep, "tag_val");
} else try self.genExpr(match.subject); } else try self.genExpr(match.subject);
const variants: ?[]const []const u8 = if (union_name) |un| const variants: ?[]const []const u8 = if (union_name) |un|
@@ -5024,7 +5063,7 @@ pub const CodeGen = struct {
null; null;
const function = self.current_function; const function = self.current_function;
const i32_type = c.LLVMInt32TypeInContext(self.context); const i64_type = c.LLVMInt64TypeInContext(self.context);
const merge_bb = c.LLVMAppendBasicBlockInContext(self.context, function, "match_end"); const merge_bb = c.LLVMAppendBasicBlockInContext(self.context, function, "match_end");
// Create case basic blocks // Create case basic blocks
@@ -5052,19 +5091,19 @@ pub const CodeGen = struct {
const pat = arm.pattern orelse continue; // skip else arm const pat = arm.pattern orelse continue; // skip else arm
if (pat.data == .enum_literal) { if (pat.data == .enum_literal) {
const idx = lookupVariantIndex(variants, pat.data.enum_literal.name); const idx = lookupVariantIndex(variants, pat.data.enum_literal.name);
const case_val = c.LLVMConstInt(i32_type, idx, 0); const case_val = c.LLVMConstInt(i64_type, idx, 0);
c.LLVMAddCase(sw, case_val, case_bbs.items[i]); c.LLVMAddCase(sw, case_val, case_bbs.items[i]);
} else if (pat.data == .type_expr) { } else if (pat.data == .type_expr) {
// Type-match: resolve type name to Any tag value(s) // Type-match: resolve type name to Any tag value(s)
const tag_values = try self.resolveTypeMatchTags(pat.data.type_expr.name); const tag_values = try self.resolveTypeMatchTags(pat.data.type_expr.name);
for (tag_values) |tag| { for (tag_values) |tag| {
c.LLVMAddCase(sw, c.LLVMConstInt(i32_type, tag, 0), case_bbs.items[i]); c.LLVMAddCase(sw, c.LLVMConstInt(i64_type, tag, 0), case_bbs.items[i]);
} }
} else if (pat.data == .identifier) { } else if (pat.data == .identifier) {
// Named type (struct/enum/union name) or category (int/float) // Named type (struct/enum/union name) or category (int/float)
const tag_values = try self.resolveTypeMatchTags(pat.data.identifier.name); const tag_values = try self.resolveTypeMatchTags(pat.data.identifier.name);
for (tag_values) |tag| { for (tag_values) |tag| {
c.LLVMAddCase(sw, c.LLVMConstInt(i32_type, tag, 0), case_bbs.items[i]); c.LLVMAddCase(sw, c.LLVMConstInt(i64_type, tag, 0), case_bbs.items[i]);
} }
} }
} }
@@ -5179,6 +5218,8 @@ pub const CodeGen = struct {
.array_cat .array_cat
else if (std.mem.eql(u8, name, "slice")) else if (std.mem.eql(u8, name, "slice"))
.slice_cat .slice_cat
else if (std.mem.eql(u8, name, "pointer"))
.pointer_cat
else else
null; null;
if (category) |cat| { if (category) |cat| {
@@ -5208,7 +5249,7 @@ pub const CodeGen = struct {
ANY_TAG_F64 ANY_TAG_F64
else if (std.mem.eql(u8, name, "string")) else if (std.mem.eql(u8, name, "string"))
ANY_TAG_STRING ANY_TAG_STRING
else if (std.mem.eql(u8, name, "Type")) else if (std.mem.eql(u8, name, "Type") or std.mem.eql(u8, name, "type"))
ANY_TAG_TYPE ANY_TAG_TYPE
else if (std.mem.eql(u8, name, "void")) else if (std.mem.eql(u8, name, "void"))
ANY_TAG_VOID ANY_TAG_VOID
@@ -5303,7 +5344,9 @@ pub const CodeGen = struct {
const val = try self.genExpr(args[0]); const val = try self.genExpr(args[0]);
// Extract ptr and len from string slice // Extract ptr and len from string slice
const ptr = c.LLVMBuildExtractValue(self.builder, val, 0, "str_ptr"); const ptr = c.LLVMBuildExtractValue(self.builder, val, 0, "str_ptr");
const len = c.LLVMBuildExtractValue(self.builder, val, 1, "str_len"); const len_i64 = c.LLVMBuildExtractValue(self.builder, val, 1, "str_len");
// printf %.*s precision is C int (i32) — truncate from i64
const len = c.LLVMBuildTrunc(self.builder, len_i64, c.LLVMInt32TypeInContext(self.context), "len_trunc");
const fmt = c.LLVMBuildGlobalStringPtr(self.builder, "%.*s", "write_fmt"); const fmt = c.LLVMBuildGlobalStringPtr(self.builder, "%.*s", "write_fmt");
const printf_fn = builtins.printf_fn; const printf_fn = builtins.printf_fn;
const fn_type = c.LLVMGlobalGetValueType(printf_fn); const fn_type = c.LLVMGlobalGetValueType(printf_fn);
@@ -5322,7 +5365,7 @@ pub const CodeGen = struct {
/// Helper: build a constant string slice as a global constant (no builder needed). /// Helper: build a constant string slice as a global constant (no builder needed).
fn buildConstStrGlobal(self: *CodeGen, s: []const u8) c.LLVMValueRef { fn buildConstStrGlobal(self: *CodeGen, s: []const u8) c.LLVMValueRef {
const sz = self.allocator.dupeZ(u8, s) catch unreachable; const sz = self.allocator.dupeZ(u8, s) catch unreachable;
const i32_ty = c.LLVMInt32TypeInContext(self.context); const i64_ty = c.LLVMInt64TypeInContext(self.context);
const i8_ty = c.LLVMInt8TypeInContext(self.context); const i8_ty = c.LLVMInt8TypeInContext(self.context);
// Create a global string constant // Create a global string constant
const str_const = c.LLVMConstStringInContext(self.context, sz.ptr, @intCast(s.len), 0); const str_const = c.LLVMConstStringInContext(self.context, sz.ptr, @intCast(s.len), 0);
@@ -5335,10 +5378,10 @@ pub const CodeGen = struct {
c.LLVMSetGlobalConstant(global, 1); c.LLVMSetGlobalConstant(global, 1);
c.LLVMSetLinkage(global, c.LLVMPrivateLinkage); c.LLVMSetLinkage(global, c.LLVMPrivateLinkage);
} }
// Build constant struct {ptr, i32} // Build constant struct {ptr, i64}
var fields = [_]c.LLVMValueRef{ var fields = [_]c.LLVMValueRef{
c.LLVMConstBitCast(global.?, c.LLVMPointerTypeInContext(self.context, 0)), c.LLVMConstBitCast(global.?, c.LLVMPointerTypeInContext(self.context, 0)),
c.LLVMConstInt(i32_ty, s.len, 0), c.LLVMConstInt(i64_ty, s.len, 0),
}; };
return c.LLVMConstStructInContext(self.context, &fields, 2, 0); return c.LLVMConstStructInContext(self.context, &fields, 2, 0);
} }
@@ -5368,6 +5411,43 @@ pub const CodeGen = struct {
return self.allocator.dupeZ(u8, name) catch unreachable; return self.allocator.dupeZ(u8, name) catch unreachable;
} }
/// Convert a type expression AST node to its source string representation.
fn typeNodeToString(self: *CodeGen, node: *const Node) []const u8 {
return switch (node.data) {
.type_expr => |te| te.name,
.identifier => |id| id.name,
.pointer_type_expr => |pte| std.fmt.allocPrint(self.allocator, "*{s}", .{self.typeNodeToString(pte.pointee_type)}) catch "?",
.many_pointer_type_expr => |mpte| std.fmt.allocPrint(self.allocator, "[*]{s}", .{self.typeNodeToString(mpte.element_type)}) catch "?",
.slice_type_expr => |ste| std.fmt.allocPrint(self.allocator, "[]{s}", .{self.typeNodeToString(ste.element_type)}) catch "?",
.array_type_expr => |ate| blk: {
const elem = self.typeNodeToString(ate.element_type);
if (ate.length.data == .int_literal) {
break :blk std.fmt.allocPrint(self.allocator, "[{d}]{s}", .{ ate.length.data.int_literal.value, elem }) catch "?";
}
break :blk std.fmt.allocPrint(self.allocator, "[?]{s}", .{elem}) catch "?";
},
else => "?",
};
}
/// Build a function type string like "() -> s32" from an fn_decl.
fn buildFnSignature(self: *CodeGen, fd: ast.FnDecl) []const u8 {
var buf = std.ArrayList(u8).empty;
buf.appendSlice(self.allocator, "(") catch return "?";
for (fd.params, 0..) |param, i| {
if (i > 0) buf.appendSlice(self.allocator, ", ") catch {};
if (param.is_variadic) buf.appendSlice(self.allocator, "..") catch {};
const ty_str = self.typeNodeToString(param.type_expr);
buf.appendSlice(self.allocator, ty_str) catch {};
}
buf.appendSlice(self.allocator, ")") catch {};
if (fd.return_type) |rt| {
buf.appendSlice(self.allocator, " -> ") catch {};
buf.appendSlice(self.allocator, self.typeNodeToString(rt)) catch {};
}
return buf.toOwnedSlice(self.allocator) catch "?";
}
/// Extract a qualified name from a callee expression (identifier or field_access chain). /// Extract a qualified name from a callee expression (identifier or field_access chain).
fn calleeToQualifiedName(self: *CodeGen, callee: *Node) ?[]const u8 { fn calleeToQualifiedName(self: *CodeGen, callee: *Node) ?[]const u8 {
if (callee.data == .identifier) return callee.data.identifier.name; if (callee.data == .identifier) return callee.data.identifier.name;
@@ -5415,7 +5495,7 @@ pub const CodeGen = struct {
fn inferType(self: *CodeGen, node: *Node) Type { fn inferType(self: *CodeGen, node: *Node) Type {
return switch (node.data) { return switch (node.data) {
.int_literal => Type.s(32), .int_literal => Type.s(64),
.float_literal => .f32, .float_literal => .f32,
.bool_literal => .boolean, .bool_literal => .boolean,
.string_literal => .string_type, .string_literal => .string_type,
@@ -5435,7 +5515,7 @@ pub const CodeGen = struct {
.identifier => |ident| { .identifier => |ident| {
if (self.named_values.get(ident.name)) |entry| return entry.ty; if (self.named_values.get(ident.name)) |entry| return entry.ty;
if (self.comptime_globals.get(ident.name)) |ct| return ct.ty; if (self.comptime_globals.get(ident.name)) |ct| return ct.ty;
return Type.s(32); return Type.s(64);
}, },
.if_expr => |ie| { .if_expr => |ie| {
return self.inferType(ie.then_branch); return self.inferType(ie.then_branch);
@@ -5477,21 +5557,21 @@ pub const CodeGen = struct {
}; };
if (obj_ty) |uty| return uty; if (obj_ty) |uty| return uty;
} }
const callee_name = self.resolveCalleeName(call_node) orelse return Type.s(32); const callee_name = self.resolveCalleeName(call_node) orelse return Type.s(64);
const base_name = if (std.mem.lastIndexOfScalar(u8, callee_name, '.')) |idx| callee_name[idx + 1 ..] else callee_name; const base_name = if (std.mem.lastIndexOfScalar(u8, callee_name, '.')) |idx| callee_name[idx + 1 ..] else callee_name;
// Built-in: sqrt returns same type as argument // Built-in: sqrt returns same type as argument
if (std.mem.eql(u8, base_name, "sqrt")) { if (std.mem.eql(u8, base_name, "sqrt")) {
if (call_node.args.len > 0) return self.inferType(call_node.args[0]); if (call_node.args.len > 0) return self.inferType(call_node.args[0]);
return .f32; return .f32;
} }
// Built-in: size_of returns s32 // Built-in: size_of returns s64
if (std.mem.eql(u8, base_name, "size_of")) return Type.s(32); if (std.mem.eql(u8, base_name, "size_of")) return Type.s(64);
// Built-in: type_of returns s32 (type tag) // Built-in: type_of returns s64 (type tag)
if (std.mem.eql(u8, base_name, "type_of")) return Type.s(32); if (std.mem.eql(u8, base_name, "type_of")) return Type.s(64);
// Built-in: type_name returns string // Built-in: type_name returns string
if (std.mem.eql(u8, base_name, "type_name")) return .string_type; if (std.mem.eql(u8, base_name, "type_name")) return .string_type;
// Built-in: field_count returns s32 // Built-in: field_count returns s64
if (std.mem.eql(u8, base_name, "field_count")) return Type.s(32); if (std.mem.eql(u8, base_name, "field_count")) return Type.s(64);
// Built-in: field_name returns string // Built-in: field_name returns string
if (std.mem.eql(u8, base_name, "field_name")) return .string_type; if (std.mem.eql(u8, base_name, "field_name")) return .string_type;
// Built-in: field_value returns Any // Built-in: field_value returns Any
@@ -5499,7 +5579,7 @@ pub const CodeGen = struct {
// Built-in: cast returns the target type (first arg) // Built-in: cast returns the target type (first arg)
if (std.mem.eql(u8, base_name, "cast")) { if (std.mem.eql(u8, base_name, "cast")) {
if (call_node.args.len > 0) return self.resolveType(call_node.args[0]); if (call_node.args.len > 0) return self.resolveType(call_node.args[0]);
return Type.s(32); return Type.s(64);
} }
// Built-in: alloc returns string // Built-in: alloc returns string
if (std.mem.eql(u8, base_name, "alloc")) return .string_type; if (std.mem.eql(u8, base_name, "alloc")) return .string_type;
@@ -5507,7 +5587,7 @@ pub const CodeGen = struct {
const template = self.generic_templates.get(callee_name) orelse blk: { const template = self.generic_templates.get(callee_name) orelse blk: {
// Intra-namespace fallback // Intra-namespace fallback
if (self.current_namespace) |ns| { if (self.current_namespace) |ns| {
const qualified = std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns, callee_name }) catch return Type.s(32); const qualified = std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns, callee_name }) catch return Type.s(64);
break :blk self.generic_templates.get(qualified); break :blk self.generic_templates.get(qualified);
} }
break :blk null; break :blk null;
@@ -5544,16 +5624,16 @@ pub const CodeGen = struct {
const resolved = self.resolveType(rt); const resolved = self.resolveType(rt);
if (!std.meta.eql(resolved, Type.void_type)) return resolved; if (!std.meta.eql(resolved, Type.void_type)) return resolved;
} }
return Type.s(32); return Type.s(64);
} }
// Check non-generic LLVM functions // Check non-generic LLVM functions
const callee_name_z = self.allocator.dupeZ(u8, callee_name) catch return Type.s(32); const callee_name_z = self.allocator.dupeZ(u8, callee_name) catch return Type.s(64);
var callee_fn_opt = c.LLVMGetNamedFunction(self.module, callee_name_z.ptr); var callee_fn_opt = c.LLVMGetNamedFunction(self.module, callee_name_z.ptr);
// Intra-namespace fallback // Intra-namespace fallback
if (callee_fn_opt == null) { if (callee_fn_opt == null) {
if (self.current_namespace) |ns| { if (self.current_namespace) |ns| {
const q = std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns, callee_name }) catch return Type.s(32); const q = std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns, callee_name }) catch return Type.s(64);
const qz = self.allocator.dupeZ(u8, q) catch return Type.s(32); const qz = self.allocator.dupeZ(u8, q) catch return Type.s(64);
callee_fn_opt = c.LLVMGetNamedFunction(self.module, qz.ptr); callee_fn_opt = c.LLVMGetNamedFunction(self.module, qz.ptr);
} }
} }
@@ -5562,44 +5642,44 @@ pub const CodeGen = struct {
const ret_llvm = c.LLVMGetReturnType(fn_type); const ret_llvm = c.LLVMGetReturnType(fn_type);
return self.llvmTypeToSxType(ret_llvm); return self.llvmTypeToSxType(ret_llvm);
} }
return Type.s(32); return Type.s(64);
}, },
.unary_op => |unop| { .unary_op => |unop| {
if (unop.op == .address_of) { if (unop.op == .address_of) {
const operand_ty = self.inferType(unop.operand); const operand_ty = self.inferType(unop.operand);
const name = operand_ty.displayName(self.allocator) catch return Type.s(32); const name = operand_ty.displayName(self.allocator) catch return Type.s(64);
return .{ .pointer_type = .{ .pointee_name = name } }; return .{ .pointer_type = .{ .pointee_name = name } };
} }
return self.inferType(unop.operand); return self.inferType(unop.operand);
}, },
.deref_expr => |de| { .deref_expr => |de| {
const ptr_ty = self.inferType(de.operand); const ptr_ty = self.inferType(de.operand);
if (ptr_ty.isPointer()) return self.resolveTypeFromName(ptr_ty.pointer_type.pointee_name) orelse Type.s(32); if (ptr_ty.isPointer()) return self.resolveTypeFromName(ptr_ty.pointer_type.pointee_name) orelse Type.s(64);
return Type.s(32); return Type.s(64);
}, },
.null_literal => return .{ .pointer_type = .{ .pointee_name = "void" } }, .null_literal => return .{ .pointer_type = .{ .pointee_name = "void" } },
.field_access => |fa| { .field_access => |fa| {
var obj_ty = self.inferType(fa.object); var obj_ty = self.inferType(fa.object);
// Auto-deref: if pointer, unwrap to pointee // Auto-deref: if pointer, unwrap to pointee
if (obj_ty.isPointer()) { if (obj_ty.isPointer()) {
obj_ty = self.resolveTypeFromName(obj_ty.pointer_type.pointee_name) orelse Type.s(32); obj_ty = self.resolveTypeFromName(obj_ty.pointer_type.pointee_name) orelse Type.s(64);
} }
if (obj_ty == .string_type) { if (obj_ty == .string_type) {
if (std.mem.eql(u8, fa.field, "len")) return Type.s(32); if (std.mem.eql(u8, fa.field, "len")) return Type.s(64);
if (std.mem.eql(u8, fa.field, "ptr")) return .string_type; if (std.mem.eql(u8, fa.field, "ptr")) return .string_type;
} }
if (obj_ty.isSlice()) { if (obj_ty.isSlice()) {
if (std.mem.eql(u8, fa.field, "len")) return Type.s(32); if (std.mem.eql(u8, fa.field, "len")) return Type.s(64);
} }
if (obj_ty.isArray()) { if (obj_ty.isArray()) {
if (std.mem.eql(u8, fa.field, "len")) return Type.s(32); if (std.mem.eql(u8, fa.field, "len")) return Type.s(64);
} }
if (obj_ty.isAny()) { if (obj_ty.isAny()) {
if (std.mem.eql(u8, fa.field, "tag")) return Type.s(32); if (std.mem.eql(u8, fa.field, "tag")) return Type.s(64);
if (std.mem.eql(u8, fa.field, "value")) return Type.s(64); if (std.mem.eql(u8, fa.field, "value")) return Type.s(64);
} }
if (obj_ty.isVector()) { if (obj_ty.isVector()) {
return obj_ty.vectorElementType() orelse Type.s(32); return obj_ty.vectorElementType() orelse Type.s(64);
} }
if (obj_ty.isStruct()) { if (obj_ty.isStruct()) {
if (self.struct_types.get(obj_ty.struct_type)) |info| { if (self.struct_types.get(obj_ty.struct_type)) |info| {
@@ -5617,23 +5697,24 @@ pub const CodeGen = struct {
} }
} }
} }
return Type.s(32); return Type.s(64);
}, },
.index_expr => |ie| { .index_expr => |ie| {
const obj_ty = self.inferType(ie.object); const obj_ty = self.inferType(ie.object);
if (obj_ty == .string_type) return Type.u(8);
if (obj_ty.isVector()) { if (obj_ty.isVector()) {
return obj_ty.vectorElementType() orelse Type.s(32); return obj_ty.vectorElementType() orelse Type.s(64);
} }
if (obj_ty.isArray()) { if (obj_ty.isArray()) {
return Type.fromName(obj_ty.array_type.element_name) orelse Type.s(32); return Type.fromName(obj_ty.array_type.element_name) orelse Type.s(64);
} }
if (obj_ty.isSlice()) { if (obj_ty.isSlice()) {
return obj_ty.sliceElementType() orelse Type.s(32); return obj_ty.sliceElementType() orelse Type.s(64);
} }
if (obj_ty.isManyPointer()) { if (obj_ty.isManyPointer()) {
return self.resolveTypeFromName(obj_ty.many_pointer_type.element_name) orelse Type.s(32); return self.resolveTypeFromName(obj_ty.many_pointer_type.element_name) orelse Type.s(64);
} }
return Type.s(32); return Type.s(64);
}, },
.slice_expr => |se| { .slice_expr => |se| {
const obj_ty = self.inferType(se.object); const obj_ty = self.inferType(se.object);
@@ -5645,11 +5726,11 @@ pub const CodeGen = struct {
.array_literal => |al| { .array_literal => |al| {
if (al.elements.len == 0) return .void_type; if (al.elements.len == 0) return .void_type;
const elem_ty = self.inferType(al.elements[0]); const elem_ty = self.inferType(al.elements[0]);
const elem_name = elem_ty.displayName(self.allocator) catch return Type.s(32); const elem_name = elem_ty.displayName(self.allocator) catch return Type.s(64);
return .{ .array_type = .{ .element_name = elem_name, .length = @intCast(al.elements.len) } }; return .{ .array_type = .{ .element_name = elem_name, .length = @intCast(al.elements.len) } };
}, },
.while_expr, .for_expr, .break_expr, .continue_expr => .void_type, .while_expr, .for_expr, .break_expr, .continue_expr => .void_type,
else => Type.s(32), else => Type.s(64),
}; };
} }

View File

@@ -15,6 +15,12 @@ pub const Value = union(enum) {
array_val: ArrayValue, array_val: ArrayValue,
type_val: Type, type_val: Type,
function_val: FunctionVal, function_val: FunctionVal,
pointer_val: PointerValue,
null_val: void,
pub const PointerValue = struct {
target: [*]Value,
};
pub const StructValue = struct { pub const StructValue = struct {
type_name: []const u8, type_name: []const u8,
@@ -96,6 +102,11 @@ pub const Value = union(enum) {
try buf.append(allocator, ']'); try buf.append(allocator, ']');
return buf.items; return buf.items;
}, },
.pointer_val => |pv| {
const inner = try pv.target[0].format(allocator);
return std.fmt.allocPrint(allocator, "&{s}", .{inner});
},
.null_val => allocator.dupe(u8, "null"),
}; };
} }
}; };
@@ -160,6 +171,13 @@ pub const Instruction = union(enum) {
get_field: u16, get_field: u16,
set_field: u16, set_field: u16,
// Pointers
address_of_local: u16, // push pointer to local slot
address_of_index, // pop idx, pop array, push pointer to element
deref, // pop pointer, push dereferenced value
deref_set, // pop value, pop pointer, store through pointer
push_null, // push null pointer
// Arrays // Arrays
make_array: u32, // element count on stack make_array: u32, // element count on stack
get_index, get_index,
@@ -295,6 +313,29 @@ pub const Compiler = struct {
return idx; return idx;
} }
/// Look up a struct field index by name, handling pointer auto-deref.
fn resolveFieldIndex(self: *Compiler, object: *Node, field: []const u8) ?u16 {
if (self.sema_result) |sr| {
const obj_ty = sr.type_map.get(object) orelse return null;
const struct_name: ?[]const u8 = if (obj_ty.isStruct())
obj_ty.struct_type
else if (obj_ty.isPointer())
obj_ty.pointer_type.pointee_name
else
null;
if (struct_name) |sn| {
if (sr.struct_types.get(sn)) |info| {
for (info.field_names, 0..) |fname, idx| {
if (std.mem.eql(u8, fname, field)) {
return @intCast(idx);
}
}
}
}
}
return null;
}
fn resolveLocal(self: *Compiler, name: []const u8) ?u16 { fn resolveLocal(self: *Compiler, name: []const u8) ?u16 {
var i = self.locals.items.len; var i = self.locals.items.len;
while (i > 0) { while (i > 0) {
@@ -451,12 +492,29 @@ pub const Compiler = struct {
} }
}, },
.unary_op => |unop| { .unary_op => |unop| {
try self.compileNode(unop.operand); if (unop.op == .address_of) {
switch (unop.op) { if (unop.operand.data == .identifier) {
.negate => try self.emit(.negate), if (self.resolveLocal(unop.operand.data.identifier.name)) |slot| {
.not => try self.emit(.not), try self.emit(.{ .address_of_local = slot });
.xx => {}, // cast — handle later } else {
.address_of => {}, // pointers not supported in comptime return error.UnsupportedExpression;
}
} else if (unop.operand.data == .index_expr) {
// &arr[i] — push array, push index, address_of_index
try self.compileNode(unop.operand.data.index_expr.object);
try self.compileNode(unop.operand.data.index_expr.index);
try self.emit(.address_of_index);
} else {
return error.UnsupportedExpression;
}
} else {
try self.compileNode(unop.operand);
switch (unop.op) {
.negate => try self.emit(.negate),
.not => try self.emit(.not),
.xx => {}, // cast — handle later
.address_of => unreachable, // handled above
}
} }
}, },
.comptime_expr => |ct| { .comptime_expr => |ct| {
@@ -533,6 +591,26 @@ pub const Compiler = struct {
try self.emit(.{ .set_local = slot }); try self.emit(.{ .set_local = slot });
} }
} }
} else if (asgn.target.data == .field_access) {
// obj.field = val (works with auto-deref for pointers)
const fa = asgn.target.data.field_access;
const field_idx = self.resolveFieldIndex(fa.object, fa.field) orelse return error.UnsupportedExpression;
try self.compileNode(fa.object);
if (asgn.op != .assign) return error.UnsupportedExpression;
try self.compileNode(asgn.value);
try self.emit(.{ .set_field = field_idx });
// Store back to local
if (fa.object.data == .identifier) {
if (self.resolveLocal(fa.object.data.identifier.name)) |slot| {
try self.emit(.{ .set_local = slot });
}
}
} else if (asgn.target.data == .deref_expr) {
// p.* = val
try self.compileNode(asgn.target.data.deref_expr.operand);
if (asgn.op != .assign) return error.UnsupportedExpression;
try self.compileNode(asgn.value);
try self.emit(.deref_set);
} }
}, },
.return_stmt => |rs| { .return_stmt => |rs| {
@@ -655,20 +733,10 @@ pub const Compiler = struct {
} }
} }
} }
// Look up field index from sema struct_types // Look up field index from sema struct_types (handles pointer auto-deref)
if (self.sema_result) |sr| { if (self.resolveFieldIndex(fa.object, fa.field)) |field_idx| {
// Infer the object type to find the struct name try self.emit(.{ .get_field = field_idx });
const obj_ty = sr.type_map.get(fa.object); return;
if (obj_ty != null and obj_ty.?.isStruct()) {
if (sr.struct_types.get(obj_ty.?.struct_type)) |info| {
for (info.field_names, 0..) |fname, idx| {
if (std.mem.eql(u8, fname, fa.field)) {
try self.emit(.{ .get_field = @intCast(idx) });
return;
}
}
}
}
} }
// Fallback: use field name for well-known string fields // Fallback: use field name for well-known string fields
// (sema may not have type info for nodes in imported function bodies) // (sema may not have type info for nodes in imported function bodies)
@@ -748,6 +816,16 @@ pub const Compiler = struct {
const offset = @as(i32, @intCast(target)) - @as(i32, @intCast(self.instructions.items.len)) - 1; const offset = @as(i32, @intCast(target)) - @as(i32, @intCast(self.instructions.items.len)) - 1;
try self.emit(.{ .jump = offset }); try self.emit(.{ .jump = offset });
}, },
.deref_expr => |de| {
try self.compileNode(de.operand);
try self.emit(.deref);
},
.null_literal => {
try self.emit(.push_null);
},
.pointer_type_expr, .many_pointer_type_expr => {
try self.emit(.push_void); // type expressions not meaningful as values
},
.defer_stmt => {}, // defer not meaningful in comptime .defer_stmt => {}, // defer not meaningful in comptime
.insert_expr => {}, // handled by codegen, not VM .insert_expr => {}, // handled by codegen, not VM
else => { else => {
@@ -871,6 +949,51 @@ pub const VM = struct {
self.stack[abs_slot] = val; self.stack[abs_slot] = val;
}, },
// Pointers
.address_of_local => |slot| {
const abs_slot = frame.base_slot + slot;
// Grow stack if needed so the target slot exists
while (self.sp <= abs_slot) {
self.stack[self.sp] = .{ .void_val = {} };
self.sp += 1;
}
const ptr: [*]Value = @ptrCast(&self.stack[abs_slot]);
try self.push(.{ .pointer_val = .{ .target = ptr } });
},
.address_of_index => {
const idx_val = try self.pop();
const arr = try self.pop();
const idx: usize = @intCast(idx_val.asInt() orelse return error.TypeError);
if (arr == .array_val) {
if (idx >= arr.array_val.elements.len) return error.IndexOutOfBounds;
try self.push(.{ .pointer_val = .{ .target = arr.array_val.elements.ptr + idx } });
} else {
return error.TypeError;
}
},
.deref => {
const v = try self.pop();
if (v == .pointer_val) {
try self.push(try self.cloneValue(v.pointer_val.target[0]));
} else if (v == .null_val) {
return error.NullDereference;
} else {
return error.TypeError;
}
},
.deref_set => {
const val = try self.pop();
const ptr_v = try self.pop();
if (ptr_v == .pointer_val) {
ptr_v.pointer_val.target[0] = val;
} else if (ptr_v == .null_val) {
return error.NullDereference;
} else {
return error.TypeError;
}
},
.push_null => try self.push(.{ .null_val = {} }),
// Global variables (lazily resolved from root_decls) // Global variables (lazily resolved from root_decls)
.get_global => |name_idx| { .get_global => |name_idx| {
const name = if (name_idx < frame.chunk.strings.len) frame.chunk.strings[name_idx] else return error.InvalidGlobal; const name = if (name_idx < frame.chunk.strings.len) frame.chunk.strings[name_idx] else return error.InvalidGlobal;
@@ -1014,7 +1137,9 @@ pub const VM = struct {
try self.push(.{ .struct_val = .{ .type_name = sm.type_name, .field_names = sm.field_names, .fields = fields } }); try self.push(.{ .struct_val = .{ .type_name = sm.type_name, .field_names = sm.field_names, .fields = fields } });
}, },
.get_field => |idx| { .get_field => |idx| {
const obj = try self.pop(); const raw_obj = try self.pop();
// Auto-deref pointer
const obj = if (raw_obj == .pointer_val) raw_obj.pointer_val.target[0] else raw_obj;
if (obj == .struct_val) { if (obj == .struct_val) {
if (idx < obj.struct_val.fields.len) { if (idx < obj.struct_val.fields.len) {
try self.push(obj.struct_val.fields[idx]); try self.push(obj.struct_val.fields[idx]);
@@ -1034,12 +1159,22 @@ pub const VM = struct {
}, },
.set_field => |idx| { .set_field => |idx| {
const val = try self.pop(); const val = try self.pop();
const obj = try self.pop(); const raw_obj = try self.pop();
if (obj == .struct_val) { if (raw_obj == .pointer_val) {
if (idx < obj.struct_val.fields.len) { // Auto-deref: mutate field in-place through pointer
obj.struct_val.fields[idx] = val; const target = raw_obj.pointer_val.target;
if (target[0] == .struct_val) {
const sv = target[0].struct_val;
if (idx < sv.fields.len) {
sv.fields[idx] = val;
}
} }
try self.push(obj); try self.push(raw_obj); // push pointer back
} else if (raw_obj == .struct_val) {
if (idx < raw_obj.struct_val.fields.len) {
raw_obj.struct_val.fields[idx] = val;
}
try self.push(raw_obj);
} else { } else {
return error.TypeError; return error.TypeError;
} }
@@ -1073,6 +1208,10 @@ pub const VM = struct {
} else { } else {
return error.IndexOutOfBounds; return error.IndexOutOfBounds;
} }
} else if (arr == .pointer_val) {
// Many-pointer indexing: ptr[i]
const idx: usize = @intCast(idx_val.asInt() orelse return error.TypeError);
try self.push(arr.pointer_val.target[idx]);
} else { } else {
return error.TypeError; return error.TypeError;
} }
@@ -1096,6 +1235,11 @@ pub const VM = struct {
mutable[idx] = byte_val; mutable[idx] = byte_val;
} }
try self.push(arr); try self.push(arr);
} else if (arr == .pointer_val) {
// Many-pointer index assignment: ptr[i] = val
const idx: usize = @intCast(idx_val.asInt() orelse return error.TypeError);
arr.pointer_val.target[idx] = val;
try self.push(arr);
} else { } else {
return error.TypeError; return error.TypeError;
} }
@@ -1166,11 +1310,37 @@ pub const VM = struct {
} }; } };
} }
/// Clone a value, allocating new backing storage for composites (structs/arrays)
/// so the clone is independent. Does not follow pointers.
fn cloneValue(self: *VM, val: Value) !Value {
return switch (val) {
.struct_val => |sv| {
const new_fields = try self.allocator.alloc(Value, sv.fields.len);
for (sv.fields, 0..) |f, i| {
new_fields[i] = try self.cloneValue(f);
}
return .{ .struct_val = .{ .type_name = sv.type_name, .field_names = sv.field_names, .fields = new_fields } };
},
.array_val => |av| {
const new_elements = try self.allocator.alloc(Value, av.elements.len);
for (av.elements, 0..) |e, i| {
new_elements[i] = try self.cloneValue(e);
}
return .{ .array_val = .{ .elements = new_elements } };
},
else => val,
};
}
fn valEqual(self: *VM, a: Value, b: Value) bool { fn valEqual(self: *VM, a: Value, b: Value) bool {
_ = self; _ = self;
if (a == .int_val and b == .int_val) return a.int_val == b.int_val; if (a == .int_val and b == .int_val) return a.int_val == b.int_val;
if (a == .bool_val and b == .bool_val) return a.bool_val == b.bool_val; if (a == .bool_val and b == .bool_val) return a.bool_val == b.bool_val;
if (a == .string_val and b == .string_val) return std.mem.eql(u8, a.string_val, b.string_val); if (a == .string_val and b == .string_val) return std.mem.eql(u8, a.string_val, b.string_val);
// Pointer comparison
if (a == .null_val and b == .null_val) return true;
if (a == .null_val or b == .null_val) return false;
if (a == .pointer_val and b == .pointer_val) return a.pointer_val.target == b.pointer_val.target;
// Float comparison // Float comparison
const af = a.asFloat(); const af = a.asFloat();
const bf = b.asFloat(); const bf = b.asFloat();
@@ -1377,6 +1547,7 @@ pub const VM = struct {
StackUnderflow, StackUnderflow,
IndexOutOfBounds, IndexOutOfBounds,
DivisionByZero, DivisionByZero,
NullDereference,
UnsupportedExpression, UnsupportedExpression,
OutOfMemory, OutOfMemory,
}; };
@@ -1752,3 +1923,167 @@ test "VM: function with return statement" {
); );
try std.testing.expectEqual(@as(i64, 36), result.int_val); try std.testing.expectEqual(@as(i64, 36), result.int_val);
} }
test "VM: address-of and deref" {
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena.deinit();
const alloc = arena.allocator();
// x := 42; ptr := &x; ptr.*
const code = [_]Instruction{
.{ .push_int = 42 },
.{ .set_local = 0 }, // x = 42
.{ .address_of_local = 0 }, // &x
.{ .set_local = 1 }, // ptr = &x
.{ .get_local = 1 }, // ptr
.deref, // ptr.*
};
const chunk = Chunk{
.code = &code,
.strings = &.{},
.local_count = 2,
.name = "<test>",
};
var vm = VM.init(alloc, null, &.{}, null);
const result = try vm.execute(&chunk);
try std.testing.expectEqual(@as(i64, 42), result.int_val);
}
test "VM: deref_set modifies through pointer" {
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena.deinit();
const alloc = arena.allocator();
// x := 10; ptr := &x; ptr.* = 99; x
const code = [_]Instruction{
.{ .push_int = 10 },
.{ .set_local = 0 }, // x = 10
.{ .address_of_local = 0 }, // &x
.{ .set_local = 1 }, // ptr = &x
.{ .get_local = 1 }, // ptr
.{ .push_int = 99 },
.deref_set, // ptr.* = 99
.{ .get_local = 0 }, // x (should be 99 now)
};
const chunk = Chunk{
.code = &code,
.strings = &.{},
.local_count = 2,
.name = "<test>",
};
var vm = VM.init(alloc, null, &.{}, null);
const result = try vm.execute(&chunk);
try std.testing.expectEqual(@as(i64, 99), result.int_val);
}
test "VM: null pointer" {
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena.deinit();
const alloc = arena.allocator();
const code = [_]Instruction{
.push_null,
};
const chunk = Chunk{
.code = &code,
.strings = &.{},
.local_count = 0,
.name = "<test>",
};
var vm = VM.init(alloc, null, &.{}, null);
const result = try vm.execute(&chunk);
try std.testing.expect(result == .null_val);
}
test "VM: pointer to struct field access" {
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena.deinit();
const alloc = arena.allocator();
// Build: struct{x: 10, y: 20}, &struct, get_field(1) — auto-deref
const code = [_]Instruction{
.{ .push_int = 10 },
.{ .push_int = 20 },
.{ .make_struct = .{ .type_name = "Point", .field_count = 2, .field_names = &.{ "x", "y" } } },
.{ .set_local = 0 }, // v = Point{10, 20}
.{ .address_of_local = 0 }, // &v
.{ .get_field = 1 }, // auto-deref, get y
};
const chunk = Chunk{
.code = &code,
.strings = &.{},
.local_count = 1,
.name = "<test>",
};
var vm = VM.init(alloc, null, &.{}, null);
const result = try vm.execute(&chunk);
try std.testing.expectEqual(@as(i64, 20), result.int_val);
}
test "VM: pointer to struct field mutation" {
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena.deinit();
const alloc = arena.allocator();
// v = Point{10, 20}; ptr = &v; ptr.x = 99; v.x
const code = [_]Instruction{
.{ .push_int = 10 },
.{ .push_int = 20 },
.{ .make_struct = .{ .type_name = "Point", .field_count = 2, .field_names = &.{ "x", "y" } } },
.{ .set_local = 0 }, // v = Point{10, 20}
.{ .address_of_local = 0 }, // &v
.{ .set_local = 1 }, // ptr = &v
.{ .get_local = 1 }, // ptr
.{ .push_int = 99 },
.{ .set_field = 0 }, // ptr.x = 99 (auto-deref)
.{ .set_local = 1 }, // store ptr back
.{ .get_local = 0 }, // v
.{ .get_field = 0 }, // v.x
};
const chunk = Chunk{
.code = &code,
.strings = &.{},
.local_count = 2,
.name = "<test>",
};
var vm = VM.init(alloc, null, &.{}, null);
const result = try vm.execute(&chunk);
try std.testing.expectEqual(@as(i64, 99), result.int_val);
}
test "VM: many-pointer indexing" {
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena.deinit();
const alloc = arena.allocator();
// arr = [10, 20, 30]; mp = &arr[0]; mp[2]
const code = [_]Instruction{
.{ .push_int = 10 },
.{ .push_int = 20 },
.{ .push_int = 30 },
.{ .make_array = 3 },
.{ .set_local = 0 }, // arr = [10, 20, 30]
.{ .get_local = 0 }, // arr
.{ .push_int = 0 },
.address_of_index, // &arr[0]
.{ .set_local = 1 }, // mp = &arr[0]
.{ .get_local = 1 }, // mp
.{ .push_int = 2 },
.get_index, // mp[2]
};
const chunk = Chunk{
.code = &code,
.strings = &.{},
.local_count = 2,
.name = "<test>",
};
var vm = VM.init(alloc, null, &.{}, null);
const result = try vm.execute(&chunk);
try std.testing.expectEqual(@as(i64, 30), result.int_val);
}

View File

@@ -125,7 +125,7 @@ pub const Analyzer = struct {
var param_types = std.ArrayList(Type).empty; var param_types = std.ArrayList(Type).empty;
var has_variadic = false; var has_variadic = false;
for (fd.params) |param| { for (fd.params) |param| {
const pt = Type.fromTypeExpr(param.type_expr) orelse Type.s(32); const pt = Type.fromTypeExpr(param.type_expr) orelse Type.s(64);
if (param.is_variadic) { if (param.is_variadic) {
has_variadic = true; has_variadic = true;
// Variadic param becomes a slice type // Variadic param becomes a slice type
@@ -158,7 +158,7 @@ pub const Analyzer = struct {
const lam = cd.value.data.lambda; const lam = cd.value.data.lambda;
var param_types = std.ArrayList(Type).empty; var param_types = std.ArrayList(Type).empty;
for (lam.params) |param| { for (lam.params) |param| {
const pt = Type.fromTypeExpr(param.type_expr) orelse Type.s(32); const pt = Type.fromTypeExpr(param.type_expr) orelse Type.s(64);
try param_types.append(self.allocator, pt); try param_types.append(self.allocator, pt);
} }
const ret = if (lam.return_type) |rt| Type.fromTypeExpr(rt) orelse .void_type else .void_type; const ret = if (lam.return_type) |rt| Type.fromTypeExpr(rt) orelse .void_type else .void_type;
@@ -185,7 +185,7 @@ pub const Analyzer = struct {
// Populate struct_types registry // Populate struct_types registry
var field_types = std.ArrayList(Type).empty; var field_types = std.ArrayList(Type).empty;
for (sd.field_types) |ft| { for (sd.field_types) |ft| {
const resolved = Type.fromTypeExpr(ft) orelse Type.s(32); const resolved = Type.fromTypeExpr(ft) orelse Type.s(64);
try field_types.append(self.allocator, resolved); try field_types.append(self.allocator, resolved);
} }
try self.struct_types.put(sd.name, .{ try self.struct_types.put(sd.name, .{
@@ -272,7 +272,7 @@ pub const Analyzer = struct {
/// symbols for identifier types, and Type.widen for arithmetic promotion. /// symbols for identifier types, and Type.widen for arithmetic promotion.
pub fn inferExprType(self: *Analyzer, node: *const Node) Type { pub fn inferExprType(self: *Analyzer, node: *const Node) Type {
return switch (node.data) { return switch (node.data) {
.int_literal => Type.s(32), .int_literal => Type.s(64),
.float_literal => .f32, .float_literal => .f32,
.bool_literal => .boolean, .bool_literal => .boolean,
.string_literal => .string_type, .string_literal => .string_type,
@@ -296,10 +296,10 @@ pub const Analyzer = struct {
i -= 1; i -= 1;
const sym = self.symbols.items[i]; const sym = self.symbols.items[i];
if (sym.scope_depth <= self.scope_depth and std.mem.eql(u8, sym.name, ident.name)) { if (sym.scope_depth <= self.scope_depth and std.mem.eql(u8, sym.name, ident.name)) {
return sym.ty orelse Type.s(32); return sym.ty orelse Type.s(64);
} }
} }
return Type.s(32); return Type.s(64);
}, },
.if_expr => |ie| { .if_expr => |ie| {
return self.inferExprType(ie.then_branch); return self.inferExprType(ie.then_branch);
@@ -317,7 +317,7 @@ pub const Analyzer = struct {
return .void_type; return .void_type;
}, },
.call => |call_node| { .call => |call_node| {
const callee_name = self.resolveCalleeName(call_node) orelse return Type.s(32); const callee_name = self.resolveCalleeName(call_node) orelse return Type.s(64);
// Check fn_signatures registry // Check fn_signatures registry
if (self.fn_signatures.get(callee_name)) |sig| { if (self.fn_signatures.get(callee_name)) |sig| {
return sig.return_type; return sig.return_type;
@@ -328,7 +328,7 @@ pub const Analyzer = struct {
if (call_node.args.len > 0) return self.inferExprType(call_node.args[0]); if (call_node.args.len > 0) return self.inferExprType(call_node.args[0]);
return .f32; return .f32;
} }
return Type.s(32); return Type.s(64);
}, },
.unary_op => |unop| { .unary_op => |unop| {
return self.inferExprType(unop.operand); return self.inferExprType(unop.operand);
@@ -336,7 +336,7 @@ pub const Analyzer = struct {
.field_access => |fa| { .field_access => |fa| {
const obj_ty = self.inferExprType(fa.object); const obj_ty = self.inferExprType(fa.object);
if (obj_ty == .string_type) { if (obj_ty == .string_type) {
if (std.mem.eql(u8, fa.field, "len")) return Type.s(32); if (std.mem.eql(u8, fa.field, "len")) return Type.s(64);
if (std.mem.eql(u8, fa.field, "ptr")) return .string_type; if (std.mem.eql(u8, fa.field, "ptr")) return .string_type;
} }
if (obj_ty.isStruct()) { if (obj_ty.isStruct()) {
@@ -349,17 +349,17 @@ pub const Analyzer = struct {
} }
} }
if (obj_ty.isArray()) { if (obj_ty.isArray()) {
return Type.fromName(obj_ty.array_type.element_name) orelse Type.s(32); return Type.fromName(obj_ty.array_type.element_name) orelse Type.s(64);
} }
return Type.s(32); return Type.s(64);
}, },
.index_expr => |ie| { .index_expr => |ie| {
const obj_ty = self.inferExprType(ie.object); const obj_ty = self.inferExprType(ie.object);
if (obj_ty == .string_type) return Type.s(32); if (obj_ty == .string_type) return Type.u(8);
if (obj_ty.isArray()) { if (obj_ty.isArray()) {
return Type.fromName(obj_ty.array_type.element_name) orelse Type.s(32); return Type.fromName(obj_ty.array_type.element_name) orelse Type.s(64);
} }
return Type.s(32); return Type.s(64);
}, },
.slice_expr => |se| { .slice_expr => |se| {
const obj_ty = self.inferExprType(se.object); const obj_ty = self.inferExprType(se.object);
@@ -741,7 +741,7 @@ pub const Analyzer = struct {
fn inferValueType(value: *Node) ?Type { fn inferValueType(value: *Node) ?Type {
return switch (value.data) { return switch (value.data) {
.int_literal => Type.s(32), .int_literal => Type.s(64),
.float_literal => .f64, .float_literal => .f64,
.bool_literal => .boolean, .bool_literal => .boolean,
.string_literal => .string_type, .string_literal => .string_type,