From 4ff828fd1af8cfcbab8c12da8fbb94f59abbdd90 Mon Sep 17 00:00:00 2001 From: agra Date: Thu, 12 Feb 2026 21:37:25 +0200 Subject: [PATCH] sdl phase 2 --- examples/28-sdl-graphics.sx | 206 ++++++++++++++++++------------------ examples/modules/opengl.sx | 16 +-- specs.md | 18 ++++ src/codegen.zig | 111 +++++++++++++++++-- src/types.zig | 62 ++++++++--- 5 files changed, 281 insertions(+), 132 deletions(-) diff --git a/examples/28-sdl-graphics.sx b/examples/28-sdl-graphics.sx index 3e5c45b..fa0c34b 100644 --- a/examples/28-sdl-graphics.sx +++ b/examples/28-sdl-graphics.sx @@ -4,19 +4,27 @@ PI :f32: 3.14159265; -// ---- Matrix math (column-major [16]f32, passed as [*]f32) ---- +vec4 :: (x: f32, y: f32, z: f32, w: f32) -> Vector(4, f32) { + .[x, y, z, w]; +} -mat4_zero :: (m: [*]f32) { +// ---- Matrix44: column-major 4×4 matrix ---- + +Matrix44 :: struct { + data: [16]f32; +} + +mat4_zero :: (m: *Matrix44) { i := 0; - while i < 16 { m[i] = 0.0; i += 1; } + while i < 16 { m.data[i] = 0.0; i += 1; } } -mat4_identity :: (m: [*]f32) { +mat4_identity :: (m: *Matrix44) { mat4_zero(m); - m[0] = 1.0; m[5] = 1.0; m[10] = 1.0; m[15] = 1.0; + m.data[0] = 1.0; m.data[5] = 1.0; m.data[10] = 1.0; m.data[15] = 1.0; } -mat4_multiply :: (out: [*]f32, a: [*]f32, b: [*]f32) { +mat4_multiply :: (out: *Matrix44, a: *Matrix44, b: *Matrix44) { tmp : [16]f32 = ---; j := 0; while j < 4 { @@ -25,7 +33,7 @@ mat4_multiply :: (out: [*]f32, a: [*]f32, b: [*]f32) { sum : f32 = 0.0; k := 0; while k < 4 { - sum = sum + a[k * 4 + i] * b[j * 4 + k]; + sum = sum + a.data[k * 4 + i] * b.data[j * 4 + k]; k += 1; } tmp[j * 4 + i] = sum; @@ -34,64 +42,63 @@ mat4_multiply :: (out: [*]f32, a: [*]f32, b: [*]f32) { j += 1; } i := 0; - while i < 16 { out[i] = tmp[i]; i += 1; } + while i < 16 { out.data[i] = tmp[i]; i += 1; } } -mat4_perspective :: (m: [*]f32, fov: f32, aspect: f32, near: f32, far: f32) { +mat4_perspective :: (m: *Matrix44, fov: f32, aspect: f32, near: f32, far: f32) { mat4_zero(m); half := fov / 2.0; f := cos(half) / sin(half); - m[0] = f / aspect; - m[5] = f; - m[10] = (far + near) / (near - far); - m[11] = -1.0; - m[14] = (2.0 * far * near) / (near - far); + m.data[0] = f / aspect; + m.data[5] = f; + m.data[10] = (far + near) / (near - far); + m.data[11] = -1.0; + m.data[14] = (2.0 * far * near) / (near - far); } -mat4_rotate_y :: (m: [*]f32, angle: f32) { +mat4_rotate_y :: (m: *Matrix44, angle: f32) { mat4_zero(m); c := cos(angle); s := sin(angle); - m[0] = c; - m[2] = 0.0 - s; - m[5] = 1.0; - m[8] = s; - m[10] = c; - m[15] = 1.0; + m.data[0] = c; + m.data[2] = 0.0 - s; + m.data[5] = 1.0; + m.data[8] = s; + m.data[10] = c; + m.data[15] = 1.0; } -mat4_rotate_x :: (m: [*]f32, angle: f32) { +mat4_rotate_x :: (m: *Matrix44, angle: f32) { mat4_zero(m); c := cos(angle); s := sin(angle); - m[0] = 1.0; - m[5] = c; - m[6] = s; - m[9] = 0.0 - s; - m[10] = c; - m[15] = 1.0; + m.data[0] = 1.0; + m.data[5] = c; + m.data[6] = s; + m.data[9] = 0.0 - s; + m.data[10] = c; + m.data[15] = 1.0; } -mat4_translate :: (m: [*]f32, tx: f32, ty: f32, tz: f32) { +mat4_translate :: (m: *Matrix44, tx: f32, ty: f32, tz: f32) { mat4_identity(m); - m[12] = tx; - m[13] = ty; - m[14] = tz; + m.data[12] = tx; + m.data[13] = ty; + m.data[14] = tz; } // ---- Shader helpers ---- compile_shader :: (shader_type: u32, source: [:0]u8) -> u32 { shader : u32 = glCreateShader(shader_type); - src_ptr : *void = xx &source[0]; - glShaderSource(shader, 1, xx &src_ptr, null); + glShaderSource(shader, 1, source, null); glCompileShader(shader); status : s32 = 0; - glGetShaderiv(shader, GL_COMPILE_STATUS, xx &status); + glGetShaderiv(shader, GL_COMPILE_STATUS, status); if status == GL_FALSE { log_buf : [512]u8 = ---; - glGetShaderInfoLog(shader, 512, null, xx &log_buf[0]); + glGetShaderInfoLog(shader, 512, null, log_buf); print("Shader compile error\n"); } shader; @@ -107,10 +114,10 @@ create_program :: (vert_src: [:0]u8, frag_src: [:0]u8) -> u32 { glLinkProgram(prog); status : s32 = 0; - glGetProgramiv(prog, GL_LINK_STATUS, xx &status); + glGetProgramiv(prog, GL_LINK_STATUS, status); if status == GL_FALSE { log_buf : [512]u8 = ---; - glGetProgramInfoLog(prog, 512, null, xx &log_buf[0]); + glGetProgramInfoLog(prog, 512, null, log_buf); print("Program link error\n"); } @@ -189,68 +196,68 @@ GLSL; light_loc : s32 = glGetUniformLocation(program, "uLightDir"); wire_loc : s32 = glGetUniformLocation(program, "uWire"); - // Cube vertices: position(3) + normal(3), 36 vertices = 216 floats - vertices : [216]f32 = .[ + // Cube vertices: pos(vec4 w=1) + normal(vec4 w=0), 36 vertices × 2 vec4s = 72 + vertices : []Vector(4, f32) = .[ // Front face (z = +0.5) - -0.5, -0.5, 0.5, 0.0, 0.0, 1.0, - 0.5, -0.5, 0.5, 0.0, 0.0, 1.0, - 0.5, 0.5, 0.5, 0.0, 0.0, 1.0, - -0.5, -0.5, 0.5, 0.0, 0.0, 1.0, - 0.5, 0.5, 0.5, 0.0, 0.0, 1.0, - -0.5, 0.5, 0.5, 0.0, 0.0, 1.0, + vec4(-0.5, -0.5, 0.5, 1.0), vec4( 0.0, 0.0, 1.0, 0.0), + vec4( 0.5, -0.5, 0.5, 1.0), vec4( 0.0, 0.0, 1.0, 0.0), + vec4( 0.5, 0.5, 0.5, 1.0), vec4( 0.0, 0.0, 1.0, 0.0), + vec4(-0.5, -0.5, 0.5, 1.0), vec4( 0.0, 0.0, 1.0, 0.0), + vec4( 0.5, 0.5, 0.5, 1.0), vec4( 0.0, 0.0, 1.0, 0.0), + vec4(-0.5, 0.5, 0.5, 1.0), vec4( 0.0, 0.0, 1.0, 0.0), // Back face (z = -0.5) - 0.5, -0.5, -0.5, 0.0, 0.0, -1.0, - -0.5, -0.5, -0.5, 0.0, 0.0, -1.0, - -0.5, 0.5, -0.5, 0.0, 0.0, -1.0, - 0.5, -0.5, -0.5, 0.0, 0.0, -1.0, - -0.5, 0.5, -0.5, 0.0, 0.0, -1.0, - 0.5, 0.5, -0.5, 0.0, 0.0, -1.0, + vec4( 0.5, -0.5, -0.5, 1.0), vec4( 0.0, 0.0, -1.0, 0.0), + vec4(-0.5, -0.5, -0.5, 1.0), vec4( 0.0, 0.0, -1.0, 0.0), + vec4(-0.5, 0.5, -0.5, 1.0), vec4( 0.0, 0.0, -1.0, 0.0), + vec4( 0.5, -0.5, -0.5, 1.0), vec4( 0.0, 0.0, -1.0, 0.0), + vec4(-0.5, 0.5, -0.5, 1.0), vec4( 0.0, 0.0, -1.0, 0.0), + vec4( 0.5, 0.5, -0.5, 1.0), vec4( 0.0, 0.0, -1.0, 0.0), // Top face (y = +0.5) - -0.5, 0.5, 0.5, 0.0, 1.0, 0.0, - 0.5, 0.5, 0.5, 0.0, 1.0, 0.0, - 0.5, 0.5, -0.5, 0.0, 1.0, 0.0, - -0.5, 0.5, 0.5, 0.0, 1.0, 0.0, - 0.5, 0.5, -0.5, 0.0, 1.0, 0.0, - -0.5, 0.5, -0.5, 0.0, 1.0, 0.0, + vec4(-0.5, 0.5, 0.5, 1.0), vec4( 0.0, 1.0, 0.0, 0.0), + vec4( 0.5, 0.5, 0.5, 1.0), vec4( 0.0, 1.0, 0.0, 0.0), + vec4( 0.5, 0.5, -0.5, 1.0), vec4( 0.0, 1.0, 0.0, 0.0), + vec4(-0.5, 0.5, 0.5, 1.0), vec4( 0.0, 1.0, 0.0, 0.0), + vec4( 0.5, 0.5, -0.5, 1.0), vec4( 0.0, 1.0, 0.0, 0.0), + vec4(-0.5, 0.5, -0.5, 1.0), vec4( 0.0, 1.0, 0.0, 0.0), // Bottom face (y = -0.5) - -0.5, -0.5, -0.5, 0.0, -1.0, 0.0, - 0.5, -0.5, -0.5, 0.0, -1.0, 0.0, - 0.5, -0.5, 0.5, 0.0, -1.0, 0.0, - -0.5, -0.5, -0.5, 0.0, -1.0, 0.0, - 0.5, -0.5, 0.5, 0.0, -1.0, 0.0, - -0.5, -0.5, 0.5, 0.0, -1.0, 0.0, + vec4(-0.5, -0.5, -0.5, 1.0), vec4( 0.0, -1.0, 0.0, 0.0), + vec4( 0.5, -0.5, -0.5, 1.0), vec4( 0.0, -1.0, 0.0, 0.0), + vec4( 0.5, -0.5, 0.5, 1.0), vec4( 0.0, -1.0, 0.0, 0.0), + vec4(-0.5, -0.5, -0.5, 1.0), vec4( 0.0, -1.0, 0.0, 0.0), + vec4( 0.5, -0.5, 0.5, 1.0), vec4( 0.0, -1.0, 0.0, 0.0), + vec4(-0.5, -0.5, 0.5, 1.0), vec4( 0.0, -1.0, 0.0, 0.0), // Right face (x = +0.5) - 0.5, -0.5, 0.5, 1.0, 0.0, 0.0, - 0.5, -0.5, -0.5, 1.0, 0.0, 0.0, - 0.5, 0.5, -0.5, 1.0, 0.0, 0.0, - 0.5, -0.5, 0.5, 1.0, 0.0, 0.0, - 0.5, 0.5, -0.5, 1.0, 0.0, 0.0, - 0.5, 0.5, 0.5, 1.0, 0.0, 0.0, + vec4( 0.5, -0.5, 0.5, 1.0), vec4( 1.0, 0.0, 0.0, 0.0), + vec4( 0.5, -0.5, -0.5, 1.0), vec4( 1.0, 0.0, 0.0, 0.0), + vec4( 0.5, 0.5, -0.5, 1.0), vec4( 1.0, 0.0, 0.0, 0.0), + vec4( 0.5, -0.5, 0.5, 1.0), vec4( 1.0, 0.0, 0.0, 0.0), + vec4( 0.5, 0.5, -0.5, 1.0), vec4( 1.0, 0.0, 0.0, 0.0), + vec4( 0.5, 0.5, 0.5, 1.0), vec4( 1.0, 0.0, 0.0, 0.0), // Left face (x = -0.5) - -0.5, -0.5, -0.5, -1.0, 0.0, 0.0, - -0.5, -0.5, 0.5, -1.0, 0.0, 0.0, - -0.5, 0.5, 0.5, -1.0, 0.0, 0.0, - -0.5, -0.5, -0.5, -1.0, 0.0, 0.0, - -0.5, 0.5, 0.5, -1.0, 0.0, 0.0, - -0.5, 0.5, -0.5, -1.0, 0.0, 0.0 + vec4(-0.5, -0.5, -0.5, 1.0), vec4(-1.0, 0.0, 0.0, 0.0), + vec4(-0.5, -0.5, 0.5, 1.0), vec4(-1.0, 0.0, 0.0, 0.0), + vec4(-0.5, 0.5, 0.5, 1.0), vec4(-1.0, 0.0, 0.0, 0.0), + vec4(-0.5, -0.5, -0.5, 1.0), vec4(-1.0, 0.0, 0.0, 0.0), + vec4(-0.5, 0.5, 0.5, 1.0), vec4(-1.0, 0.0, 0.0, 0.0), + vec4(-0.5, 0.5, -0.5, 1.0), vec4(-1.0, 0.0, 0.0, 0.0) ]; // Create VAO and VBO vao : u32 = 0; vbo : u32 = 0; - glGenVertexArrays(1, xx &vao); - glGenBuffers(1, xx &vbo); + glGenVertexArrays(1, vao); + glGenBuffers(1, vbo); glBindVertexArray(vao); glBindBuffer(GL_ARRAY_BUFFER, vbo); - glBufferData(GL_ARRAY_BUFFER, 864, xx &vertices[0], GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, 1152, xx vertices, GL_STATIC_DRAW); - // Position attribute (location 0): 3 floats, stride 24 bytes, offset 0 - glVertexAttribPointer(0, 3, GL_FLOAT, 0, 24, xx 0); + // Position attribute (location 0): 3 floats, stride 32 bytes, offset 0 + glVertexAttribPointer(0, 3, GL_FLOAT, 0, 32, xx 0); glEnableVertexAttribArray(0); - // Normal attribute (location 1): 3 floats, stride 24 bytes, offset 12 - glVertexAttribPointer(1, 3, GL_FLOAT, 0, 24, xx 12); + // Normal attribute (location 1): 3 floats, stride 32 bytes, offset 16 + glVertexAttribPointer(1, 3, GL_FLOAT, 0, 32, xx 16); glEnableVertexAttribArray(1); // Set light direction @@ -275,31 +282,28 @@ GLSL; angle := ms * 0.001; // Build matrices - proj : [16]f32 = ---; - mat4_perspective(xx &proj[0], PI / 4.0, 800.0 / 600.0, 0.1, 100.0); + proj : Matrix44 = ---; + mat4_perspective(proj, PI / 4.0, 800.0 / 600.0, 0.1, 100.0); - view : [16]f32 = ---; - mat4_translate(xx &view[0], 0.0, 0.0, -3.0); + view : Matrix44 = ---; + mat4_translate(view, 0.0, 0.0, -3.0); - rot_y : [16]f32 = ---; - mat4_rotate_y(xx &rot_y[0], angle); + rot_y : Matrix44 = ---; + mat4_rotate_y(rot_y, angle); - rot_x : [16]f32 = ---; - mat4_rotate_x(xx &rot_x[0], angle * 0.7); + rot_x : Matrix44 = ---; + mat4_rotate_x(rot_x, angle * 0.7); - // model = rot_y * rot_x - model : [16]f32 = ---; - mat4_multiply(xx &model[0], xx &rot_y[0], xx &rot_x[0]); + model : Matrix44 = ---; + mat4_multiply(model, rot_y, rot_x); - // view_model = view * model - vm : [16]f32 = ---; - mat4_multiply(xx &vm[0], xx &view[0], xx &model[0]); + vm : Matrix44 = ---; + mat4_multiply(vm, view, model); - // mvp = proj * view_model - mvp : [16]f32 = ---; - mat4_multiply(xx &mvp[0], xx &proj[0], xx &vm[0]); + mvp : Matrix44 = ---; + mat4_multiply(mvp, proj, vm); - glUniformMatrix4fv(mvp_loc, 1, 0, xx &mvp[0]); + glUniformMatrix4fv(mvp_loc, 1, 0, mvp.data); glClearColor(0.1, 0.1, 0.15, 1.0); glClear(GL_COLOR_BUFFER_BIT + GL_DEPTH_BUFFER_BIT); diff --git a/examples/modules/opengl.sx b/examples/modules/opengl.sx index 8b80697..06b0878 100644 --- a/examples/modules/opengl.sx +++ b/examples/modules/opengl.sx @@ -34,26 +34,26 @@ glDrawArrays : (u32, s32, s32) -> void = ---; glPolygonMode : (u32, u32) -> void = ---; glLineWidth : (f32) -> void = ---; glCreateShader : (u32) -> u32 = ---; -glShaderSource : (u32, s32, *void, *void) -> void = ---; +glShaderSource : (u32, s32, *[:0]u8, *s32) -> void = ---; glCompileShader : (u32) -> void = ---; -glGetShaderiv : (u32, u32, *void) -> void = ---; -glGetShaderInfoLog : (u32, s32, *void, *void) -> void = ---; +glGetShaderiv : (u32, u32, *s32) -> void = ---; +glGetShaderInfoLog : (u32, s32, *s32, [*]u8) -> void = ---; glCreateProgram : () -> u32 = ---; glAttachShader : (u32, u32) -> void = ---; glLinkProgram : (u32) -> void = ---; -glGetProgramiv : (u32, u32, *void) -> void = ---; -glGetProgramInfoLog : (u32, s32, *void, *void) -> void = ---; +glGetProgramiv : (u32, u32, *s32) -> void = ---; +glGetProgramInfoLog : (u32, s32, *s32, [*]u8) -> void = ---; glUseProgram : (u32) -> void = ---; glDeleteShader : (u32) -> void = ---; -glGenVertexArrays : (s32, *void) -> void = ---; -glGenBuffers : (s32, *void) -> void = ---; +glGenVertexArrays : (s32, *u32) -> void = ---; +glGenBuffers : (s32, *u32) -> void = ---; glBindVertexArray : (u32) -> void = ---; glBindBuffer : (u32, u32) -> void = ---; glBufferData : (u32, s64, *void, u32) -> void = ---; glVertexAttribPointer : (u32, s32, u32, u8, s32, *void) -> void = ---; glEnableVertexAttribArray : (u32) -> void = ---; glGetUniformLocation : (u32, [:0]u8) -> s32 = ---; -glUniformMatrix4fv : (s32, s32, u8, *void) -> void = ---; +glUniformMatrix4fv : (s32, s32, u8, [16]f32) -> void = ---; glUniform3f : (s32, f32, f32, f32) -> void = ---; glDepthFunc : (u32) -> void = ---; glUniform1f : (s32, f32) -> void = ---; diff --git a/specs.md b/specs.md index d6e767c..d9efe47 100644 --- a/specs.md +++ b/specs.md @@ -286,8 +286,26 @@ val := mp[2]; // 30 **Implicit conversions**: - `*T` → `[*]T` (pointer to element → many-pointer) +- `*[N]T` → `[*]T` (pointer to array → many-pointer) +- `[N]T` → `[*]T` at call sites (array decays to many-pointer) +- `[]T` → `[*]T` (slice decays to many-pointer, extracts `.ptr`) +- `T` → `*T` at call sites (implicit address-of) - `null` (`*void`) → any `*T` +**Fat pointer layout**: `[:0]u8`, `string`, and `[]T` are `{ptr, i64}` structs. The raw pointer is always the first field at offset 0. This means `*[:0]u8` works as C's `char**` — a C function dereferences through the outer pointer and reads the raw `char*` from offset 0. + +### C Interop Type Mapping + +| C type | sx type | Notes | +|--------|---------|-------| +| `const char*` (input) | `[:0]u8` | compiler extracts `.ptr` at call site | +| `char*` (output buffer) | `[*]u8` | raw buffer, no length | +| `const char**` | `*[:0]u8` | address of `[:0]u8` — `.ptr` at offset 0 | +| `int*` (single out) | `*s32` | | +| `unsigned*` (single out) | `*u32` | | +| `float*` (buffer) | `[*]f32` | | +| `void*` (generic) | `*void` | only for truly opaque/generic data | + ### Vector Types (SIMD) LLVM SIMD vectors, parameterized by length and element type. ```sx diff --git a/src/codegen.zig b/src/codegen.zig index b955847..194d479 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -1280,6 +1280,9 @@ pub const CodeGen = struct { try param_llvm_types.append(self.allocator, c.LLVMPointerTypeInContext(self.context, 0)); } else if (is_foreign and sx_ty.isStruct()) { try param_llvm_types.append(self.allocator, self.getForeignParamABIType(sx_ty)); + } else if (is_foreign and sx_ty.isArray()) { + // [N]T → pointer in C ABI (C arrays decay to pointers) + try param_llvm_types.append(self.allocator, c.LLVMPointerTypeInContext(self.context, 0)); } else { try param_llvm_types.append(self.allocator, self.typeToLLVM(sx_ty)); } @@ -1329,7 +1332,7 @@ pub const CodeGen = struct { if (first == .f32 or first == .f64) { var all_same = true; for (field_types[1..]) |ft| { - if (!std.meta.eql(ft, first)) { + if (!ft.eql(first)) { all_same = false; break; } @@ -2554,6 +2557,17 @@ pub const CodeGen = struct { return null; } } + // struct.field[i] = val — GEP through struct to array field, then index + if (ie.object.data == .field_access) { + const field_ptr = try self.genAddressOf(ie.object); + const idx = try self.genExpr(ie.index); + const val = try self.genExpr(asgn.value); + const zero = c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0); + var indices = [_]c.LLVMValueRef{ zero, idx }; + const gep_ptr = c.LLVMBuildGEP2(self.builder, self.typeToLLVM(obj_ty), field_ptr, &indices, 2, "field_arridx"); + _ = c.LLVMBuildStore(self.builder, val, gep_ptr); + return null; + } } if (obj_ty.isSlice()) { const slice_info = obj_ty.slice_type; @@ -2891,6 +2905,16 @@ pub const CodeGen = struct { const idx = self.findFieldIndex(info, fa.field) orelse return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fa.field, sname }); return c.LLVMBuildStructGEP2(self.builder, info.llvm_type, entry.ptr, @intCast(idx), "addr_field"); } + // &p.field where p is *Struct — auto-deref through pointer + if (entry.ty.isPointer()) { + const pointee_name = entry.ty.pointer_type.pointee_name; + if (self.struct_types.get(pointee_name)) |info| { + const loaded_ptr = c.LLVMBuildLoad2(self.builder, + c.LLVMPointerTypeInContext(self.context, 0), entry.ptr, "ptr_load"); + const idx = self.findFieldIndex(info, fa.field) orelse return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fa.field, pointee_name }); + return c.LLVMBuildStructGEP2(self.builder, info.llvm_type, loaded_ptr, @intCast(idx), "addr_pfield"); + } + } } } } @@ -3364,6 +3388,9 @@ pub const CodeGen = struct { break :blk entry.ptr; } } + if (node.data == .field_access) { + break :blk try self.genAddressOf(node); + } // Fallback: generate the expression and hope it returns a pointer break :blk try self.genExpr(node); }; @@ -3382,6 +3409,42 @@ pub const CodeGen = struct { } } + // Array to many-pointer coercion: [N]T → [*]T + if (target_ty.isManyPointer()) { + const src_ty = self.inferType(node); + if (src_ty.isArray()) { + const arr_info = src_ty.array_type; + if (std.mem.eql(u8, arr_info.element_name, target_ty.many_pointer_type.element_name)) { + const arr_alloca = blk: { + if (node.data == .identifier) { + if (self.named_values.get(node.data.identifier.name)) |entry| { + break :blk entry.ptr; + } + } + if (node.data == .field_access) { + break :blk try self.genAddressOf(node); + } + break :blk try self.genExpr(node); + }; + const i64_ty = c.LLVMInt64TypeInContext(self.context); + const zero = c.LLVMConstInt(i64_ty, 0, 0); + var indices = [_]c.LLVMValueRef{ zero, zero }; + return c.LLVMBuildGEP2(self.builder, self.typeToLLVM(src_ty), arr_alloca, &indices, 2, "arr_decay"); + } + } + } + + // Slice to many-pointer coercion: []T → [*]T (extract .ptr from fat pointer) + if (target_ty.isManyPointer()) { + const src_ty = self.inferType(node); + if (src_ty.isSlice()) { + if (std.mem.eql(u8, src_ty.slice_type.element_name, target_ty.many_pointer_type.element_name)) { + const slice_val = try self.genExpr(node); + return c.LLVMBuildExtractValue(self.builder, slice_val, 0, "slice_decay"); + } + } + } + // Implicit address-of: passing T where *T is expected → auto & if (target_ty.isPointer()) { const src_ty = self.inferType(node); @@ -3390,6 +3453,8 @@ pub const CodeGen = struct { std.mem.eql(u8, src_ty.struct_type, pointee_name) or (if (self.type_aliases.get(src_ty.struct_type)) |alias| std.mem.eql(u8, alias, pointee_name) else false) or (if (self.type_aliases.get(pointee_name)) |alias| std.mem.eql(u8, alias, src_ty.struct_type) else false) + else if (Type.fromName(pointee_name)) |pointee_ty| + src_ty.eql(pointee_ty) else false; if (src_matches) { @@ -3442,7 +3507,7 @@ pub const CodeGen = struct { /// Convert an LLVM value from src_ty to target_ty, emitting appropriate casts. fn convertValue(self: *CodeGen, val: c.LLVMValueRef, src_ty: Type, target_ty: Type) c.LLVMValueRef { // Same type → return as-is - if (std.meta.eql(src_ty, target_ty)) return val; + if (src_ty.eql(target_ty)) return val; // string <-> []u8: identical LLVM type {ptr, i64}, no conversion needed if ((src_ty == .string_type and target_ty.isSlice() and @@ -3574,6 +3639,16 @@ pub const CodeGen = struct { return c.LLVMBuildIntToPtr(self.builder, val, c.LLVMPointerTypeInContext(self.context, 0), "inttoptr"); } + // Slice/string → pointer: extract .ptr from fat pointer + if ((src_ty.isSlice() or src_ty == .string_type) and (target_ty.isPointer() or target_ty.isManyPointer())) { + return c.LLVMBuildExtractValue(self.builder, val, 0, "slice_to_ptr"); + } + + // *[N]T → [*]T: pointer to array decays to many-pointer (both opaque ptrs, no-op) + if (src_ty.isPointer() and target_ty.isManyPointer()) { + return val; + } + // Pointer → function_type or function_type → pointer: both are opaque pointers, no-op if ((src_ty.isPointer() or src_ty.isManyPointer()) and target_ty.isFunctionType()) { return val; @@ -4170,11 +4245,11 @@ pub const CodeGen = struct { return c.LLVMBuildExtractElement(self.builder, vec_val, idx, "vidx"); } if (obj_ty.isArray()) { - // Array index: load from GEP + const arr_info = obj_ty.array_type; + const elem_ty = Type.fromName(arr_info.element_name) orelse return self.emitErrorFmt("unknown array element type '{s}'", .{arr_info.element_name}); + // Array index via identifier: load from GEP if (ie.object.data == .identifier) { if (self.named_values.get(ie.object.data.identifier.name)) |entry| { - const arr_info = obj_ty.array_type; - const elem_ty = Type.fromName(arr_info.element_name) orelse return self.emitErrorFmt("unknown array element type '{s}'", .{arr_info.element_name}); const idx = try self.genExpr(ie.index); const zero = c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0); var indices = [_]c.LLVMValueRef{ zero, idx }; @@ -4182,6 +4257,15 @@ pub const CodeGen = struct { return c.LLVMBuildLoad2(self.builder, self.typeToLLVM(elem_ty), gep, "arrval"); } } + // Array index via field access: GEP through struct field + if (ie.object.data == .field_access) { + const field_ptr = try self.genAddressOf(ie.object); + const idx = try self.genExpr(ie.index); + const zero = c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0); + var indices = [_]c.LLVMValueRef{ zero, idx }; + const gep = c.LLVMBuildGEP2(self.builder, self.typeToLLVM(obj_ty), field_ptr, &indices, 2, "field_arridx"); + return c.LLVMBuildLoad2(self.builder, self.typeToLLVM(elem_ty), gep, "field_arrval"); + } } if (obj_ty == .string_type) { // String indexing: extract ptr from slice, GEP + load u8 @@ -4730,9 +4814,11 @@ pub const CodeGen = struct { const fn_ptr = c.LLVMBuildLoad2(self.builder, ptr_ty, entry.ptr, "fn_ptr"); // Build LLVM function type from FunctionTypeInfo + const ptr_ty_llvm = c.LLVMPointerTypeInContext(self.context, 0); var param_llvm_types: [64]c.LLVMTypeRef = undefined; for (fti.param_types, 0..) |pt, i| { - param_llvm_types[i] = self.typeToLLVM(pt); + // [N]T and [:0]T params are pointers at the ABI level + param_llvm_types[i] = if (pt.isArray() or pt == .string_type) ptr_ty_llvm else self.typeToLLVM(pt); } const ret_llvm = self.typeToLLVM(fti.return_type.*); const fn_type = c.LLVMFunctionType( @@ -4746,7 +4832,18 @@ pub const CodeGen = struct { var arg_vals = std.ArrayList(c.LLVMValueRef).empty; for (call_node.args, 0..) |arg, i| { if (i < fti.param_types.len) { - try arg_vals.append(self.allocator, try self.genExprAsType(arg, fti.param_types[i])); + const pt = fti.param_types[i]; + // [N]T params: pass pointer via array decay + if (pt.isArray()) { + const decay_target: Type = .{ .many_pointer_type = .{ .element_name = pt.array_type.element_name } }; + try arg_vals.append(self.allocator, try self.genExprAsType(arg, decay_target)); + } else if (pt == .string_type) { + // [:0]u8 params: extract .ptr from fat pointer + const val = try self.genExprAsType(arg, pt); + try arg_vals.append(self.allocator, c.LLVMBuildExtractValue(self.builder, val, 0, "str_ptr")); + } else { + try arg_vals.append(self.allocator, try self.genExprAsType(arg, pt)); + } } else { try arg_vals.append(self.allocator, try self.genExpr(arg)); } diff --git a/src/types.zig b/src/types.zig index 48f6a9d..1782301 100644 --- a/src/types.zig +++ b/src/types.zig @@ -56,6 +56,38 @@ pub const Type = union(enum) { name: []const u8, }; + /// Content-based equality: compares string fields by content, not pointer identity. + pub fn eql(self: Type, other: Type) bool { + const Tag = std.meta.Tag(Type); + const self_tag: Tag = self; + const other_tag: Tag = other; + if (self_tag != other_tag) return false; + return switch (self) { + .signed => |w| w == other.signed, + .unsigned => |w| w == other.unsigned, + .f32, .f64, .void_type, .boolean, .string_type, .any_type => true, + .enum_type => |n| std.mem.eql(u8, n, other.enum_type), + .struct_type => |n| std.mem.eql(u8, n, other.struct_type), + .union_type => |n| std.mem.eql(u8, n, other.union_type), + .array_type => |info| info.length == other.array_type.length and + std.mem.eql(u8, info.element_name, other.array_type.element_name), + .slice_type => |info| std.mem.eql(u8, info.element_name, other.slice_type.element_name), + .pointer_type => |info| std.mem.eql(u8, info.pointee_name, other.pointer_type.pointee_name), + .many_pointer_type => |info| std.mem.eql(u8, info.element_name, other.many_pointer_type.element_name), + .vector_type => |info| info.length == other.vector_type.length and + std.mem.eql(u8, info.element_name, other.vector_type.element_name), + .function_type => |info| { + const o = other.function_type; + if (info.param_types.len != o.param_types.len) return false; + for (info.param_types, o.param_types) |a, b| { + if (!a.eql(b)) return false; + } + return info.return_type.eql(o.return_type.*); + }, + .meta_type => |info| std.mem.eql(u8, info.name, other.meta_type.name), + }; + } + // Convenience constructors pub fn s(width: u8) Type { return .{ .signed = width }; @@ -91,6 +123,17 @@ pub const Type = union(enum) { if (name.len >= 2 and name[0] == '*') { return .{ .pointer_type = .{ .pointee_name = name[1..] } }; } + // Vector: Vector(N,T) + if (name.len >= 10 and std.mem.startsWith(u8, name, "Vector(") and name[name.len - 1] == ')') { + const inner = name[7 .. name.len - 1]; // contents between ( and ) + if (std.mem.indexOfScalar(u8, inner, ',')) |comma| { + const length = std.fmt.parseInt(u32, inner[0..comma], 10) catch return null; + const elem_name = inner[comma + 1 ..]; + if (elem_name.len > 0) { + return .{ .vector_type = .{ .element_name = elem_name, .length = length } }; + } + } + } // Variable-width integers: s1..s64, u1..u64 if (name.len >= 2 and (name[0] == 's' or name[0] == 'u')) { const width = std.fmt.parseInt(u8, name[1..], 10) catch return null; @@ -263,29 +306,16 @@ pub const Type = union(enum) { /// - Float to wider float (f32 → f64) /// Everything else requires `xx`. pub fn isImplicitlyConvertibleTo(self: Type, target: Type) bool { - if (std.meta.eql(self, target)) return true; - // Struct types: compare names by content (not pointer identity) - if (self.isStruct() and target.isStruct()) { - return std.mem.eql(u8, self.struct_type, target.struct_type); - } - // Slice types: compare element names by content (not pointer) - if (self.isSlice() and target.isSlice()) { - return std.mem.eql(u8, self.slice_type.element_name, target.slice_type.element_name); - } + if (self.eql(target)) return true; // string <-> []u8: same layout, bidirectional implicit conversion if (self == .string_type and target.isSlice() and std.mem.eql(u8, target.slice_type.element_name, "u8")) return true; if (self.isSlice() and std.mem.eql(u8, self.slice_type.element_name, "u8") and target == .string_type) return true; - // Pointer types: compare pointee names by content, *void is universal (both directions) + // *void is universal pointer (both directions) if (self.isPointer() and target.isPointer()) { if (std.mem.eql(u8, self.pointer_type.pointee_name, "void")) return true; if (std.mem.eql(u8, target.pointer_type.pointee_name, "void")) return true; - return std.mem.eql(u8, self.pointer_type.pointee_name, target.pointer_type.pointee_name); - } - // Many-pointer types: compare element names by content - if (self.isManyPointer() and target.isManyPointer()) { - return std.mem.eql(u8, self.many_pointer_type.element_name, target.many_pointer_type.element_name); } // *T → [*]T: pointer to element is implicitly convertible to many-pointer // null (*void) → [*]T is also allowed @@ -418,7 +448,7 @@ pub const Type = union(enum) { /// Used for arithmetic type promotion (e.g., s16 + s32 → s32, int + float → float). pub fn widen(a: Type, b: Type) Type { // Same type → return it - if (std.meta.eql(a, b)) return a; + if (a.eql(b)) return a; // Vector + vector of same dimensions → return a if (a.isVector() and b.isVector()) return a;