smoke tests

This commit is contained in:
agra
2026-02-15 19:44:20 +02:00
parent a3be9cce7c
commit 7da3ecfa7c
9 changed files with 1046 additions and 149 deletions

618
examples/50-smoke.sx Normal file
View File

@@ -0,0 +1,618 @@
#import "modules/std.sx";
// ============================================================
// Comprehensive Smoke Test — exercises every spec feature
// ============================================================
// --- Top-level type declarations ---
Point :: struct { x, y: s32; }
Color :: enum { red; green; blue; }
Shape :: enum {
circle: f32;
rect: struct { w, h: f32; };
none;
}
Overlay :: union {
f: f32;
i: s32;
}
Vec2 :: union {
data: [2]f32;
struct { x, y: f32; };
}
Defaults :: struct {
a: s32;
b: s32 = 99;
c: s32 = ---;
}
MyFloat :: f64;
Perms :: enum flags { read; write; execute; }
Status :: enum u8 { ok; err; timeout; }
// --- Top-level functions ---
add :: (a: s32, b: s32) -> s32 { a + b; }
mul :: (a: s32, b: s32) -> s32 { a * b; }
identity :: (x: $T) -> T { x; }
pair_add :: (a: $T, b: $U) -> s64 {
cast(s64) a + cast(s64) b;
}
typed_sum :: (args: ..s32) -> s32 {
result := 0;
for args { result = result + it; }
result;
}
apply :: (f: (s32, s32) -> s32, x: s32, y: s32) -> s32 {
f(x, y);
}
void_return :: () {
return;
}
implicit_return :: (x: s32) -> s32 {
x * 2;
}
early_return :: (x: s32) -> s32 {
if x > 10 { return 99; }
x;
}
// #run compile-time constant
CT_VAL :: #run add(10, 15);
// #insert helper
gen_code :: () -> string {
return "print(\"insert-ok\\n\");";
}
// ============================================================
main :: {
// ========================================================
// 1. LITERALS
// ========================================================
print("=== 1. Literals ===\n");
// Integer literals
print("decimal: {}\n", 42);
print("hex: {}\n", 0xFF);
print("binary: {}\n", 0b1010);
// Float literal
pi := 3.14;
print("float: {}\n", pi);
// Explicit f64
big : f64 = 2.718281828;
print("f64: {}\n", big);
// Boolean literals
print("true: {}\n", true);
print("false: {}\n", false);
// String with escapes
print("escapes: hello\tworld\n");
// Multi-line string (backtick)
ml := `line1
line2`;
print("multiline: {}\n", ml);
// Heredoc string
hd := #string END
raw heredoc
END;
print("heredoc: {}\n", hd);
// Undefined with type
undef_val : s32 = ---;
undef_val = 77;
print("undef-then-set: {}\n", undef_val);
// Enum literal (context-inferred)
c : Color = .green;
print("enum-lit: {}\n", c);
// ========================================================
// 2. OPERATORS & PRECEDENCE
// ========================================================
print("=== 2. Operators ===\n");
// Arithmetic
print("add: {}\n", 3 + 4);
print("sub: {}\n", 10 - 3);
print("mul: {}\n", 6 * 7);
print("div: {}\n", 20 / 4);
print("mod: {}\n", 17 % 5);
print("neg: {}\n", -(5));
// Comparisons
print("eq: {}\n", 5 == 5);
print("neq: {}\n", 5 != 3);
print("lt: {}\n", 3 < 5);
print("gt: {}\n", 5 > 3);
print("le: {}\n", 5 <= 5);
print("ge: {}\n", 5 >= 3);
// Chained comparisons
v := 50;
print("chain: {}\n", 0 <= v <= 100);
print("chain-gt: {}\n", 100 > v > 0);
print("chain-mixed: {}\n", 100 > v >= 0);
// Bitwise
print("band: {}\n", 0xFF & 0x0F);
print("bor: {}\n", 1 | 2 | 4);
// Logical (short-circuit)
print("and: {}\n", true and true);
print("and-false: {}\n", true and false);
print("or: {}\n", false or true);
print("or-false: {}\n", false or false);
// Compound assignment
ca := 10;
ca += 5;
print("ca+=: {}\n", ca);
ca -= 3;
print("ca-=: {}\n", ca);
ca *= 2;
print("ca*=: {}\n", ca);
ca /= 6;
print("ca/=: {}\n", ca);
// Precedence
print("prec1: {}\n", 2 + 3 * 4);
print("prec2: {}\n", (2 + 3) * 4);
// xx explicit cast
big2 : f64 = 200.7;
small : u8 = xx big2;
print("xx-cast: {}\n", small);
// ========================================================
// 3. TYPE SYSTEM
// ========================================================
print("=== 3. Types ===\n");
// Primitive types
v_s8 : s8 = 127;
v_s16 : s16 = 32000;
v_s32 : s32 = 100000;
v_u8 : u8 = 255;
v_u16 : u16 = 65000;
v_u32 : u32 = 4000000;
print("s8: {}\n", v_s8);
print("s16: {}\n", v_s16);
print("s32: {}\n", v_s32);
print("u8: {}\n", v_u8);
print("u16: {}\n", v_u16);
print("u32: {}\n", v_u32);
// Type alias
mf : MyFloat = 1.5;
print("alias: {}\n", mf);
// --- Structs ---
// Positional literal
p1 : Point = .{ 1, 2 };
print("struct-pos: {}\n", p1);
// Type-prefix literal
p2 := Point.{ 3, 4 };
print("struct-prefix: {}\n", p2);
// Named fields
p3 := Point.{ y=10, x=20 };
print("struct-named: {}\n", p3);
// Shorthand (variable name = field name)
x : s32 = 5;
y : s32 = 6;
p4 := Point.{ x, y };
print("struct-shorthand: {}\n", p4);
// Field defaults
d1 : Defaults;
print("defaults: a={} b={}\n", d1.a, d1.b);
// Field access and assignment
p5 := Point.{ 0, 0 };
p5.x = 42;
p5.y = 99;
print("field-assign: {}\n", p5);
// --- Enum (payload-less) ---
ec : Color = .red;
print("enum: {}\n", ec);
// Backing type
st : Status = .err;
print("backing: {}\n", st);
// --- Enum (tagged union) ---
sh : Shape = .circle(3.14);
print("tagged: {}\n", sh);
// Payload access
radius := sh.circle;
print("payload: {}\n", radius);
// Void variant
sh = .none;
print("void-variant: {}\n", sh);
// Pattern matching
sh2 : Shape = .rect(.{ 5, 3 });
if sh2 == {
case .circle: print("match: circle\n");
case .rect: print("match: rect\n");
case .none: print("match: none\n");
}
// Match as expression
sh3 : Shape = .circle(1.0);
ms := if sh3 == {
case .circle: 10;
case .rect: 20;
case .none: 30;
}
print("match-expr: {}\n", ms);
// Payload capture (block form)
sh4 : Shape = .circle(9.5);
if sh4 == {
case .circle: (r) { print("capture: {}\n", r); }
case .rect: (sz) { print("capture: {}\n", sz); }
case .none: print("capture: none\n");
}
// else arm in match
num := 42;
if num == {
case 1: print("else-match: one\n");
case 2: print("else-match: two\n");
else: print("else-match: other\n");
}
// Integer pattern matching
code := 2;
if code == {
case 1: print("int-match: one\n");
case 2: print("int-match: two\n");
case 3: print("int-match: three\n");
}
// Bool conditional
flag := true;
if flag { print("bool: true\n"); }
// --- Union (untagged) ---
o : Overlay = ---;
o.f = 3.14;
print("union-f: {}\n", o.f);
// Type punning — read same bits as s32
print("union-i: {}\n", o.i);
// Union member promotion
uv : Vec2 = ---;
uv.x = 1.0;
uv.y = 2.0;
print("promoted-x: {}\n", uv.x);
print("promoted-data0: {}\n", uv.data[0]);
// --- Arrays ---
arr : [5]s32 = .[10, 20, 30, 40, 50];
print("arr[2]: {}\n", arr[2]);
print("arr.len: {}\n", arr.len);
// --- Slices ---
sl : []s32 = .[1, 2, 3, 4, 5];
print("sl[0]: {}\n", sl[0]);
print("sl.len: {}\n", sl.len);
// Subslicing
sub := arr[1..4];
print("sub: {}\n", sub);
head := arr[..3];
print("head: {}\n", head);
tail := arr[2..];
print("tail: {}\n", tail);
// String subslicing
msg := "hello world";
print("strsub: {}\n", msg[6..11]);
// --- Pointers ---
pv := Point.{ 10, 20 };
ptr := @pv;
print("deref: {}\n", ptr.*);
// Auto-deref
print("auto-deref: {}\n", ptr.x);
// Many-pointer
mp : [*]s32 = @arr[0];
print("mp[0]: {}\n", mp[0]);
print("mp[3]: {}\n", mp[3]);
// ========================================================
// 4. CONTROL FLOW
// ========================================================
print("=== 4. Control Flow ===\n");
// If-then-else (inline, as expression)
ite := if true then 1 else 2;
print("ite: {}\n", ite);
// If block
if 1 < 2 {
print("if-block: yes\n");
}
// If block as expression
ibe := 10 + if true { 5; } else { 0; };
print("if-block-expr: {}\n", ibe);
// While basic
wi := 0;
while wi < 5 { wi += 1; }
print("while: {}\n", wi);
// While with break
wb := 0;
while wb < 100 {
if wb == 7 { break; }
wb += 1;
}
print("while-break: {}\n", wb);
// While with continue
wsum := 0;
wc := 0;
while wc < 10 {
wc += 1;
if wc % 2 == 0 { continue; }
wsum += wc;
}
print("while-continue: {}\n", wsum);
// For loop basic (using write like example 19)
farr : [4]s32 = .[10, 20, 30, 40];
write("for:");
for farr {
write(" ");
write(int_to_string(it));
}
write("\n");
// For with print
write("for-print:");
for farr {
print(" {}", it);
}
write("\n");
// For with it_index
write("for-idx:");
for farr {
write(" ");
write(int_to_string(it_index));
}
write("\n");
// For with print two args
write("for-2arg:");
for farr {
print(" {}@{}", it, it_index);
}
write("\n");
// For with break
write("for-break:");
for farr {
if it == 30 { break; }
print(" {}", it);
}
write("\n");
// For with continue
write("for-continue:");
for farr {
if it == 20 { continue; }
print(" {}", it);
}
write("\n");
// ========================================================
// 5. FUNCTIONS & DECLARATIONS
// ========================================================
print("=== 5. Functions ===\n");
// Constant binding
FORTY_TWO :: 42;
print("const: {}\n", FORTY_TWO);
// Typed constant
TYPED_PI : f64 : 3.14;
print("typed-const: {}\n", TYPED_PI);
// Variable with default init
di : s32;
print("default-init: {}\n", di);
// Implicit return
print("implicit-ret: {}\n", implicit_return(21));
// Explicit return
print("early-ret: {}\n", early_return(5));
print("early-ret2: {}\n", early_return(20));
// Void return
void_return();
print("void-return: ok\n");
// Generic — single param
print("generic-s32: {}\n", identity(42));
print("generic-f32: {}\n", identity(1.5));
// Generic — multiple params
print("generic-multi: {}\n", pair_add(10, 20));
// Lambda
double :: (x: s32) => x * 2;
print("lambda: {}\n", double(7));
// Lambda with return type
halve :: (x: f32) -> f32 => x / 2.0;
print("lambda-ret: {}\n", halve(10.0));
// Variadic (typed)
print("varargs: {}\n", typed_sum(1, 2, 3, 4, 5));
// Spread
spread_arr : [3]s32 = .[10, 20, 30];
print("spread: {}\n", typed_sum(..spread_arr));
// Function pointers
fp : (s32, s32) -> s32 = add;
print("fp: {}\n", fp(3, 4));
fp = mul;
print("fp-reassign: {}\n", fp(3, 4));
print("fp-apply: {}\n", apply(add, 10, 20));
// ========================================================
// 6. SCOPING & DEFER
// ========================================================
print("=== 6. Scoping ===\n");
// Scope block with shadowing
sv := 100;
{
sv := 200;
print("inner: {}\n", sv);
}
print("outer: {}\n", sv);
// Nested scopes (3 levels)
nv := 1;
{
nv := 2;
{
nv := 3;
print("nest3: {}\n", nv);
}
print("nest2: {}\n", nv);
}
print("nest1: {}\n", nv);
// Multiple defers (LIFO order)
{
defer print("defer-c\n");
defer print("defer-b\n");
defer print("defer-a\n");
}
// Defer in nested scopes
{
defer print("outer-defer\n");
{
defer print("inner-defer\n");
}
}
// ========================================================
// 7. BUILT-IN FUNCTIONS
// ========================================================
print("=== 7. Builtins ===\n");
// write
write("write-ok\n");
// sqrt
print("sqrt: {}\n", sqrt(9.0));
// size_of
print("sizeof-s32: {}\n", size_of(s32));
print("sizeof-f64: {}\n", size_of(f64));
// type_of + category matching
tv := 42;
ttype := type_of(tv);
if ttype == {
case int: print("typeof: int\n");
case float: print("typeof: float\n");
else: print("typeof: other\n");
}
// type_name
print("typename: {}\n", type_name(Point));
// field_count
print("fieldcount: {}\n", field_count(Point));
// field_name
print("fieldname0: {}\n", field_name(Point, 0));
print("fieldname1: {}\n", field_name(Point, 1));
// field_value (use any_to_string to avoid sext-on-Any bug)
fv_pt := Point.{ 11, 22 };
write("fieldval0: ");
write(any_to_string(field_value(fv_pt, 0)));
write("\n");
write("fieldval1: ");
write(any_to_string(field_value(fv_pt, 1)));
write("\n");
// field_index on enum
fi_c : Color = .green;
print("fieldidx: {}\n", field_index(Color, fi_c));
// cast
cval : f64 = 3.7;
print("cast: {}\n", cast(s32) cval);
// ========================================================
// 8. COMPILE-TIME
// ========================================================
print("=== 8. Comptime ===\n");
// #run constant
print("run-const: {}\n", CT_VAL);
// #insert with function
#insert gen_code();
// ========================================================
// 9. FLAGS
// ========================================================
print("=== 9. Flags ===\n");
// Combine flags
perm : Perms = .read | .write;
print("flags: {}\n", perm);
// Test flag
if perm & .read { print("has-read: yes\n"); }
if perm & .execute { print("has-exec: yes\n"); }
// Cast to int
print("flags-raw: {}\n", cast(s64) perm);
print("=== DONE ===\n");
}

View File

@@ -89,6 +89,7 @@ pub const FnDecl = struct {
return_type: ?*Node, return_type: ?*Node,
body: *Node, body: *Node,
type_params: []const StructTypeParam = &.{}, type_params: []const StructTypeParam = &.{},
is_arrow: bool = false,
}; };
pub const Param = struct { pub const Param = struct {

View File

@@ -107,17 +107,7 @@ pub const CodeGen = struct {
// Symbol table: maps variable names to their alloca pointers // Symbol table: maps variable names to their alloca pointers
named_values: std.StringHashMap(NamedValue), named_values: std.StringHashMap(NamedValue),
// Enum type registry: maps enum name to variant list // Unified type registry: single lookup for all named types (structs, enums, unions, aliases)
enum_types: std.StringHashMap([]const []const u8),
// Type alias registry: maps alias name to target type name
type_aliases: std.StringHashMap([]const u8),
// Struct type registry: maps struct name to field info + LLVM type
struct_types: std.StringHashMap(StructInfo),
// Tagged enum registry: maps name to variant info + LLVM type (enums with payloads)
tagged_enum_types: std.StringHashMap(TaggedEnumInfo),
// Union registry: maps name to field info + LLVM type (untagged, C-style)
union_types: std.StringHashMap(UnionInfo),
// Unified type registry: single lookup for all named types
type_registry: std.StringHashMap(TypeRegistryEntry), type_registry: std.StringHashMap(TypeRegistryEntry),
// Flags enum registry: tracks which enum names are flags // Flags enum registry: tracks which enum names are flags
flags_enum_types: std.StringHashMap(void), flags_enum_types: std.StringHashMap(void),
@@ -292,6 +282,34 @@ pub const CodeGen = struct {
ty: Type, // sx type ty: Type, // sx type
}; };
/// Unified value lookup result — avoids sequential hash lookups at hot paths.
const ValueLookup = union(enum) {
local: NamedValue,
comptime_global: *ComptimeGlobal,
global_mutable: NamedValue,
fn ty(self: ValueLookup) Type {
return switch (self) {
.local, .global_mutable => |nv| nv.ty,
.comptime_global => |ct| ct.ty,
};
}
fn ptr(self: ValueLookup) c.LLVMValueRef {
return switch (self) {
.local, .global_mutable => |nv| nv.ptr,
.comptime_global => |ct| ct.global,
};
}
fn asNamedValue(self: ValueLookup) ?NamedValue {
return switch (self) {
.local, .global_mutable => |nv| nv,
.comptime_global => null,
};
}
};
pub fn init(allocator: std.mem.Allocator, module_name: [*:0]const u8, target_config: TargetConfig) CodeGen { pub fn init(allocator: std.mem.Allocator, module_name: [*:0]const u8, target_config: TargetConfig) CodeGen {
const ctx = c.LLVMContextCreate(); const ctx = c.LLVMContextCreate();
const module = c.LLVMModuleCreateWithNameInContext(module_name, ctx); const module = c.LLVMModuleCreateWithNameInContext(module_name, ctx);
@@ -331,11 +349,6 @@ pub const CodeGen = struct {
.builder = builder, .builder = builder,
.allocator = allocator, .allocator = allocator,
.named_values = std.StringHashMap(NamedValue).init(allocator), .named_values = std.StringHashMap(NamedValue).init(allocator),
.enum_types = std.StringHashMap([]const []const u8).init(allocator),
.type_aliases = std.StringHashMap([]const u8).init(allocator),
.struct_types = std.StringHashMap(StructInfo).init(allocator),
.tagged_enum_types = std.StringHashMap(TaggedEnumInfo).init(allocator),
.union_types = std.StringHashMap(UnionInfo).init(allocator),
.type_registry = std.StringHashMap(TypeRegistryEntry).init(allocator), .type_registry = std.StringHashMap(TypeRegistryEntry).init(allocator),
.flags_enum_types = std.StringHashMap(void).init(allocator), .flags_enum_types = std.StringHashMap(void).init(allocator),
.enum_variant_values = std.StringHashMap([]const i64).init(allocator), .enum_variant_values = std.StringHashMap([]const i64).init(allocator),
@@ -367,11 +380,6 @@ pub const CodeGen = struct {
pub fn deinit(self: *CodeGen) void { pub fn deinit(self: *CodeGen) void {
self.named_values.deinit(); self.named_values.deinit();
self.enum_types.deinit();
self.type_aliases.deinit();
self.struct_types.deinit();
self.tagged_enum_types.deinit();
self.union_types.deinit();
self.type_registry.deinit(); self.type_registry.deinit();
self.comptime_globals.deinit(); self.comptime_globals.deinit();
self.enum_backing_types.deinit(); self.enum_backing_types.deinit();
@@ -393,11 +401,52 @@ pub const CodeGen = struct {
} }
fn getStructInfo(self: *CodeGen, name: []const u8) !StructInfo { fn getStructInfo(self: *CodeGen, name: []const u8) !StructInfo {
return self.struct_types.get(name) orelse return self.emitErrorFmt("unknown struct type '{s}'", .{name}); return self.lookupStructInfo(name) orelse
return self.emitErrorFmt("unknown struct type '{s}'", .{name});
} }
fn getTaggedEnumInfo(self: *CodeGen, name: []const u8) !TaggedEnumInfo { fn getTaggedEnumInfo(self: *CodeGen, name: []const u8) !TaggedEnumInfo {
return self.tagged_enum_types.get(name) orelse return self.emitErrorFmt("unknown enum type '{s}'", .{name}); return self.lookupTaggedEnumInfo(name) orelse
return self.emitErrorFmt("unknown enum type '{s}'", .{name});
}
fn lookupStructInfo(self: *CodeGen, name: []const u8) ?StructInfo {
if (self.type_registry.get(name)) |e| {
if (e == .struct_info) return e.struct_info;
}
return null;
}
fn lookupTaggedEnumInfo(self: *CodeGen, name: []const u8) ?TaggedEnumInfo {
if (self.type_registry.get(name)) |e| {
if (e == .tagged_enum) return e.tagged_enum;
}
return null;
}
fn lookupUnionInfo(self: *CodeGen, name: []const u8) ?UnionInfo {
if (self.type_registry.get(name)) |e| {
if (e == .union_info) return e.union_info;
}
return null;
}
fn lookupEnumVariants(self: *CodeGen, name: []const u8) ?[]const []const u8 {
if (self.type_registry.get(name)) |e| {
if (e == .plain_enum) return e.plain_enum;
}
return null;
}
fn lookupAlias(self: *CodeGen, name: []const u8) ?[]const u8 {
if (self.type_registry.get(name)) |e| {
if (e == .alias) return e.alias;
}
return null;
}
fn isRegisteredType(self: *CodeGen, name: []const u8) bool {
return self.type_registry.contains(name);
} }
fn resolveElementType(self: *CodeGen, name: []const u8, comptime kind: []const u8) !Type { fn resolveElementType(self: *CodeGen, name: []const u8, comptime kind: []const u8) !Type {
@@ -419,6 +468,14 @@ pub const CodeGen = struct {
return self.builtins orelse return self.emitError("builtins not available"); return self.builtins orelse return self.emitError("builtins not available");
} }
/// Unified value lookup: checks locals, comptime globals, then global mutables.
fn lookupValue(self: *CodeGen, name: []const u8) ?ValueLookup {
if (self.named_values.get(name)) |nv| return .{ .local = nv };
if (self.comptime_globals.getPtr(name)) |ct| return .{ .comptime_global = ct };
if (self.global_mutable_vars.get(name)) |gm| return .{ .global_mutable = gm };
return null;
}
/// Build an alloca in the entry block of the current function so that /// Build an alloca in the entry block of the current function so that
/// stack space is reserved once, not on every loop iteration. /// stack space is reserved once, not on every loop iteration.
fn buildEntryBlockAlloca(self: *CodeGen, ty: c.LLVMTypeRef, name: [*:0]const u8) c.LLVMValueRef { fn buildEntryBlockAlloca(self: *CodeGen, ty: c.LLVMTypeRef, name: [*:0]const u8) c.LLVMValueRef {
@@ -461,8 +518,8 @@ pub const CodeGen = struct {
.boolean => self.i1Type(), .boolean => self.i1Type(),
.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 => |name| self.getEnumLLVMType(name), .enum_type => |name| self.getEnumLLVMType(name),
.struct_type => |name| if (self.struct_types.get(name)) |info| info.llvm_type else unreachable, .struct_type => |name| if (self.lookupStructInfo(name)) |info| info.llvm_type else unreachable,
.union_type => |name| if (self.tagged_enum_types.get(name)) |info| info.llvm_type else if (self.union_types.get(name)) |info| info.llvm_type else unreachable, .union_type => |name| if (self.lookupTaggedEnumInfo(name)) |info| info.llvm_type else if (self.lookupUnionInfo(name)) |info| info.llvm_type else unreachable,
.array_type => |info| { .array_type => |info| {
const elem_ty = Type.fromName(info.element_name) orelse unreachable; const elem_ty = Type.fromName(info.element_name) orelse unreachable;
return c.LLVMArrayType2(self.typeToLLVM(elem_ty), info.length); return c.LLVMArrayType2(self.typeToLLVM(elem_ty), info.length);
@@ -554,7 +611,7 @@ pub const CodeGen = struct {
.struct_type => |name| { .struct_type => |name| {
_ = try self.getAnyTypeId(name, sx_type); _ = try self.getAnyTypeId(name, sx_type);
// Recursively register struct field types // Recursively register struct field types
if (self.struct_types.get(name)) |info| { if (self.lookupStructInfo(name)) |info| {
for (info.field_types) |ft| { for (info.field_types) |ft| {
try self.preRegisterAnyType(ft); try self.preRegisterAnyType(ft);
} }
@@ -610,6 +667,7 @@ pub const CodeGen = struct {
// Convert value to i64 // Convert value to i64
const val_as_i64 = switch (ty) { const val_as_i64 = switch (ty) {
.void_type => c.LLVMConstInt(i64_ty, 0, 0),
.boolean => self.zExt(val, i64_ty, "any_bool"), .boolean => self.zExt(val, i64_ty, "any_bool"),
.signed => |w| if (w <= 32) .signed => |w| if (w <= 32)
self.sExt(val, i64_ty, "any_int") self.sExt(val, i64_ty, "any_int")
@@ -628,7 +686,7 @@ pub const CodeGen = struct {
.string_type => self.allocaStoreAsI64(self.getStringStructType(), val, "any_str"), .string_type => self.allocaStoreAsI64(self.getStringStructType(), val, "any_str"),
.struct_type => |sname| blk: { .struct_type => |sname| blk: {
// Struct — store to alloca, pass pointer as i64 // Struct — store to alloca, pass pointer as i64
const info = self.struct_types.get(sname) orelse const info = self.lookupStructInfo(sname) orelse
return self.getUndef(any_ty); return self.getUndef(any_ty);
break :blk self.allocaStoreAsI64(info.llvm_type, val, "any_struct"); break :blk self.allocaStoreAsI64(info.llvm_type, val, "any_struct");
}, },
@@ -643,7 +701,7 @@ pub const CodeGen = struct {
}, },
.union_type => |uname| blk: { .union_type => |uname| blk: {
// Union — store to alloca, pass pointer as i64 // Union — store to alloca, pass pointer as i64
const info = self.tagged_enum_types.get(uname) orelse const info = self.lookupTaggedEnumInfo(uname) orelse
return self.getUndef(any_ty); return self.getUndef(any_ty);
break :blk self.allocaStoreAsI64(info.llvm_type, val, "any_union"); break :blk self.allocaStoreAsI64(info.llvm_type, val, "any_union");
}, },
@@ -754,7 +812,7 @@ pub const CodeGen = struct {
} }
fn resolveAlias(self: *CodeGen, name: []const u8) []const u8 { fn resolveAlias(self: *CodeGen, name: []const u8) []const u8 {
return self.type_aliases.get(name) orelse name; return self.lookupAlias(name) orelse name;
} }
fn buildPhiNode(self: *CodeGen, phi_vals: *std.ArrayList(c.LLVMValueRef), phi_bbs: *std.ArrayList(c.LLVMBasicBlockRef), ty: c.LLVMTypeRef, name: [*c]const u8) !c.LLVMValueRef { fn buildPhiNode(self: *CodeGen, phi_vals: *std.ArrayList(c.LLVMValueRef), phi_bbs: *std.ArrayList(c.LLVMBasicBlockRef), ty: c.LLVMTypeRef, name: [*c]const u8) !c.LLVMValueRef {
@@ -982,7 +1040,6 @@ pub const CodeGen = struct {
try self.registerTaggedEnum(ed); try self.registerTaggedEnum(ed);
} else { } else {
// Payload-less enum // Payload-less enum
try self.enum_types.put(ed.name, ed.variant_names);
try self.type_registry.put(ed.name, .{ .plain_enum = ed.variant_names }); try self.type_registry.put(ed.name, .{ .plain_enum = ed.variant_names });
_ = try self.getAnyTypeId(ed.name, .{ .enum_type = ed.name }); _ = try self.getAnyTypeId(ed.name, .{ .enum_type = ed.name });
@@ -1025,7 +1082,6 @@ pub const CodeGen = struct {
} else if (cd.value.data == .lambda) { } else if (cd.value.data == .lambda) {
try self.registerLambdaAsFunction(cd.name, cd.value.data.lambda); try self.registerLambdaAsFunction(cd.name, cd.value.data.lambda);
} else if (cd.value.data == .type_expr) { } else if (cd.value.data == .type_expr) {
try self.type_aliases.put(cd.name, cd.value.data.type_expr.name);
try self.type_registry.put(cd.name, .{ .alias = cd.value.data.type_expr.name }); try self.type_registry.put(cd.name, .{ .alias = cd.value.data.type_expr.name });
} else if (cd.value.data == .call) { } else if (cd.value.data == .call) {
// Check if this is a generic struct or type function instantiation // Check if this is a generic struct or type function instantiation
@@ -1038,24 +1094,20 @@ pub const CodeGen = struct {
// Generic struct instantiation: Vec3 :: Vec(3, f32); // Generic struct instantiation: Vec3 :: Vec(3, f32);
const result_ty = try self.instantiateGenericStruct(cn, tmpl, cd.value.data.call.args); const result_ty = try self.instantiateGenericStruct(cn, tmpl, cd.value.data.call.args);
if (result_ty.isStruct()) { if (result_ty.isStruct()) {
try self.type_aliases.put(cd.name, result_ty.struct_type);
try self.type_registry.put(cd.name, .{ .alias = result_ty.struct_type }); try self.type_registry.put(cd.name, .{ .alias = result_ty.struct_type });
} }
} else if (self.generic_templates.get(cn)) |tmpl| { } else if (self.generic_templates.get(cn)) |tmpl| {
// Type-returning function: Foo :: Complex(u32); // Type-returning function: Foo :: Complex(u32);
const result_ty = try self.instantiateTypeFunction(cd.name, cn, tmpl, cd.value.data.call.args); const result_ty = try self.instantiateTypeFunction(cd.name, cn, tmpl, cd.value.data.call.args);
if (result_ty.isStruct()) { if (result_ty.isStruct()) {
try self.type_aliases.put(cd.name, result_ty.struct_type);
try self.type_registry.put(cd.name, .{ .alias = result_ty.struct_type }); try self.type_registry.put(cd.name, .{ .alias = result_ty.struct_type });
} else if (result_ty.isUnion()) { } else if (result_ty.isUnion()) {
try self.type_aliases.put(cd.name, result_ty.union_type);
try self.type_registry.put(cd.name, .{ .alias = result_ty.union_type }); try self.type_registry.put(cd.name, .{ .alias = result_ty.union_type });
} }
} else if (self.builtin_functions.contains(cn)) { } else if (self.builtin_functions.contains(cn)) {
// Builtin type function (e.g., Vector(4, f32), Array(5, s32)) // Builtin type function (e.g., Vector(4, f32), Array(5, s32))
if (self.resolveBuiltinType(cn, cd.value.data.call.args)) |result_ty| { if (self.resolveBuiltinType(cn, cd.value.data.call.args)) |result_ty| {
const display = try result_ty.displayName(self.allocator); const display = try result_ty.displayName(self.allocator);
try self.type_aliases.put(cd.name, display);
try self.type_registry.put(cd.name, .{ .alias = display }); try self.type_registry.put(cd.name, .{ .alias = display });
} else { } else {
try self.registerTopLevelConstant(cd); try self.registerTopLevelConstant(cd);
@@ -1405,7 +1457,7 @@ pub const CodeGen = struct {
const mangled_name = try self.mangleGenericName(template_name, sd.type_params, type_bindings, val_bindings, null); const mangled_name = try self.mangleGenericName(template_name, sd.type_params, type_bindings, val_bindings, null);
// Check if already instantiated // Check if already instantiated
if (self.struct_types.contains(mangled_name)) { if (self.type_registry.contains(mangled_name)) {
return .{ .struct_type = mangled_name }; return .{ .struct_type = mangled_name };
} }
@@ -1468,7 +1520,6 @@ pub const CodeGen = struct {
.type_param_types = try tp_types.toOwnedSlice(self.allocator), .type_param_types = try tp_types.toOwnedSlice(self.allocator),
.template_name = template_name, .template_name = template_name,
}; };
try self.struct_types.put(mangled_name, si);
try self.type_registry.put(mangled_name, .{ .struct_info = si }); try self.type_registry.put(mangled_name, .{ .struct_info = si });
_ = try self.getAnyTypeId(mangled_name, .{ .struct_type = mangled_name }); _ = try self.getAnyTypeId(mangled_name, .{ .struct_type = mangled_name });
@@ -1497,7 +1548,7 @@ pub const CodeGen = struct {
// Try struct first // Try struct first
if (self.findStructInBody(fd.body)) |struct_decl| { if (self.findStructInBody(fd.body)) |struct_decl| {
if (self.struct_types.contains(mangled_name)) { if (self.type_registry.contains(mangled_name)) {
return .{ .struct_type = mangled_name }; return .{ .struct_type = mangled_name };
} }
return self.registerInstantiatedStruct(mangled_name, alias_name, struct_decl); return self.registerInstantiatedStruct(mangled_name, alias_name, struct_decl);
@@ -1505,7 +1556,7 @@ pub const CodeGen = struct {
// Try union // Try union
if (self.findUnionInBody(fd.body)) |union_decl| { if (self.findUnionInBody(fd.body)) |union_decl| {
if (self.tagged_enum_types.contains(mangled_name)) { if (self.type_registry.contains(mangled_name)) {
return .{ .union_type = mangled_name }; return .{ .union_type = mangled_name };
} }
return self.registerInstantiatedTaggedEnum(mangled_name, union_decl); return self.registerInstantiatedTaggedEnum(mangled_name, union_decl);
@@ -1527,7 +1578,6 @@ pub const CodeGen = struct {
.llvm_type = build.llvm_type, .llvm_type = build.llvm_type,
.display_name = display_name, .display_name = display_name,
}; };
try self.struct_types.put(mangled_name, si2);
try self.type_registry.put(mangled_name, .{ .struct_info = si2 }); try self.type_registry.put(mangled_name, .{ .struct_info = si2 });
_ = try self.getAnyTypeId(mangled_name, .{ .struct_type = mangled_name }); _ = try self.getAnyTypeId(mangled_name, .{ .struct_type = mangled_name });
@@ -1544,7 +1594,6 @@ pub const CodeGen = struct {
.max_payload_size = build.max_payload_size, .max_payload_size = build.max_payload_size,
.payload_field_index = build.payload_field_index, .payload_field_index = build.payload_field_index,
}; };
try self.tagged_enum_types.put(mangled_name, tei);
try self.type_registry.put(mangled_name, .{ .tagged_enum = tei }); try self.type_registry.put(mangled_name, .{ .tagged_enum = tei });
_ = try self.getAnyTypeId(mangled_name, .{ .union_type = mangled_name }); _ = try self.getAnyTypeId(mangled_name, .{ .union_type = mangled_name });
@@ -1652,7 +1701,7 @@ pub const CodeGen = struct {
if (!sx_ty.isStruct()) return self.typeToLLVM(sx_ty); if (!sx_ty.isStruct()) return self.typeToLLVM(sx_ty);
const sname = self.resolveAlias(sx_ty.struct_type); const sname = self.resolveAlias(sx_ty.struct_type);
const info = self.struct_types.get(sname) orelse return self.typeToLLVM(sx_ty); const info = self.lookupStructInfo(sname) orelse return self.typeToLLVM(sx_ty);
if (self.target_config.isAarch64()) { if (self.target_config.isAarch64()) {
return self.aarch64ParamABI(info); return self.aarch64ParamABI(info);
@@ -1899,11 +1948,9 @@ pub const CodeGen = struct {
// Tagged enum with payloads // Tagged enum with payloads
try self.registerTaggedEnum(ed); try self.registerTaggedEnum(ed);
const qualified_u = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns.name, ed.name }); const qualified_u = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns.name, ed.name });
try self.type_aliases.put(qualified_u, ed.name);
try self.type_registry.put(qualified_u, .{ .alias = ed.name }); try self.type_registry.put(qualified_u, .{ .alias = ed.name });
} else { } else {
const qualified = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns.name, ed.name }); const qualified = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns.name, ed.name });
try self.enum_types.put(qualified, ed.variant_names);
try self.type_registry.put(qualified, .{ .plain_enum = ed.variant_names }); try self.type_registry.put(qualified, .{ .plain_enum = ed.variant_names });
_ = try self.getAnyTypeId(qualified, .{ .enum_type = qualified }); _ = try self.getAnyTypeId(qualified, .{ .enum_type = qualified });
if (ed.backing_type) |bt_node| { if (ed.backing_type) |bt_node| {
@@ -1916,13 +1963,11 @@ pub const CodeGen = struct {
try self.registerStructType(sd); try self.registerStructType(sd);
// Register qualified alias so rl.Color resolves to Color // Register qualified alias so rl.Color resolves to Color
const qualified_s = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns.name, sd.name }); const qualified_s = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns.name, sd.name });
try self.type_aliases.put(qualified_s, sd.name);
try self.type_registry.put(qualified_s, .{ .alias = sd.name }); try self.type_registry.put(qualified_s, .{ .alias = sd.name });
}, },
.union_decl => |ud| { .union_decl => |ud| {
try self.registerUnionType(ud); try self.registerUnionType(ud);
const qualified_u = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns.name, ud.name }); const qualified_u = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns.name, ud.name });
try self.type_aliases.put(qualified_u, ud.name);
try self.type_registry.put(qualified_u, .{ .alias = ud.name }); try self.type_registry.put(qualified_u, .{ .alias = ud.name });
}, },
.const_decl => |cd| { .const_decl => |cd| {
@@ -1933,7 +1978,6 @@ pub const CodeGen = struct {
try self.registerLambdaAsFunction(qualified, cd.value.data.lambda); try self.registerLambdaAsFunction(qualified, cd.value.data.lambda);
} else if (cd.value.data == .type_expr) { } else if (cd.value.data == .type_expr) {
const qualified = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns.name, cd.name }); const qualified = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns.name, cd.name });
try self.type_aliases.put(qualified, cd.value.data.type_expr.name);
try self.type_registry.put(qualified, .{ .alias = cd.value.data.type_expr.name }); try self.type_registry.put(qualified, .{ .alias = cd.value.data.type_expr.name });
} }
}, },
@@ -2019,8 +2063,11 @@ pub const CodeGen = struct {
const name_ptr = c.LLVMGetStructName(llvm_ty); const name_ptr = c.LLVMGetStructName(llvm_ty);
if (name_ptr != null) { if (name_ptr != null) {
const name = std.mem.span(name_ptr); const name = std.mem.span(name_ptr);
if (self.struct_types.contains(name)) return .{ .struct_type = name }; if (self.type_registry.get(name)) |e| switch (e) {
if (self.tagged_enum_types.contains(name)) return .{ .union_type = name }; .struct_info => return .{ .struct_type = name },
.tagged_enum => return .{ .union_type = name },
else => {},
};
} }
} }
// Check for array types // Check for array types
@@ -2277,11 +2324,29 @@ pub const CodeGen = struct {
const saved_named = self.named_values; const saved_named = self.named_values;
self.named_values = std.StringHashMap(NamedValue).init(self.allocator); self.named_values = std.StringHashMap(NamedValue).init(self.allocator);
// Register with correct types (null return_type = void) // Infer return type from body for => lambdas without explicit annotation
try self.registerFnDecl(fd, fd.name); const ret_sx_type = if (fd.return_type != null) self.resolveType(fd.return_type) else if (fd.is_arrow) self.inferType(fd.body) else Type.void_type;
// Generate body inline // For arrow lambdas with inferred return type, build function manually
const ret_sx_type = self.resolveType(fd.return_type); if (fd.is_arrow and fd.return_type == null) {
const ret_llvm_type = self.typeToLLVM(ret_sx_type);
var param_llvm_types = std.ArrayList(c.LLVMTypeRef).empty;
for (fd.params) |param| {
try param_llvm_types.append(self.allocator, self.typeToLLVM(self.resolveType(param.type_expr)));
}
const params_slice = try param_llvm_types.toOwnedSlice(self.allocator);
const fn_type = c.LLVMFunctionType(
ret_llvm_type,
if (params_slice.len > 0) params_slice.ptr else null,
@intCast(params_slice.len),
0,
);
const name_z2 = try self.allocator.dupeZ(u8, fd.name);
_ = c.LLVMAddFunction(self.module, name_z2.ptr, fn_type);
try self.function_return_types.put(fd.name, ret_sx_type);
} else {
try self.registerFnDecl(fd, fd.name);
}
self.current_return_type = ret_sx_type; self.current_return_type = ret_sx_type;
const name_z = try self.allocator.dupeZ(u8, fd.name); const name_z = try self.allocator.dupeZ(u8, fd.name);
const function = c.LLVMGetNamedFunction(self.module, name_z.ptr) orelse const function = c.LLVMGetNamedFunction(self.module, name_z.ptr) orelse
@@ -2460,7 +2525,7 @@ pub const CodeGen = struct {
sx_ty = .{ .union_type = uname }; sx_ty = .{ .union_type = uname };
// C-style (untagged) union // C-style (untagged) union
if (self.union_types.get(uname)) |info| { if (self.lookupUnionInfo(uname)) |info| {
const alloca = try self.buildNamedAlloca(info.llvm_type, vd.name); const alloca = try self.buildNamedAlloca(info.llvm_type, vd.name);
if (vd.value == null) { if (vd.value == null) {
@@ -2652,6 +2717,25 @@ pub const CodeGen = struct {
return null; return null;
} }
// Local lambda: register as function, generate body, done
if (cd.value.data == .lambda) {
const saved_fn = self.current_function;
const saved_bb = self.getCurrentBlock();
const saved_ret = self.current_return_type;
const saved_named = self.named_values;
self.named_values = std.StringHashMap(NamedValue).init(self.allocator);
try self.registerLambdaAsFunction(cd.name, cd.value.data.lambda);
try self.genLambdaBody(cd.name, cd.value.data.lambda);
self.named_values.deinit();
self.named_values = saved_named;
self.current_return_type = saved_ret;
self.current_function = saved_fn;
self.positionAt(saved_bb);
return null;
}
var sx_ty: Type = Type.s(64); var sx_ty: Type = Type.s(64);
if (cd.type_annotation) |ta| { if (cd.type_annotation) |ta| {
@@ -2688,6 +2772,8 @@ pub const CodeGen = struct {
const enum_name: ?[]const u8 = if (sx_ty.isEnum()) sx_ty.enum_type else null; const enum_name: ?[]const u8 = if (sx_ty.isEnum()) sx_ty.enum_type else null;
const init_val = if (cd.value.data == .enum_literal and enum_name != null) const init_val = if (cd.value.data == .enum_literal and enum_name != null)
self.genEnumLiteral(cd.value.data.enum_literal.name, enum_name.?) self.genEnumLiteral(cd.value.data.enum_literal.name, enum_name.?)
else if (cd.type_annotation != null)
try self.genExprAsType(cd.value, sx_ty)
else else
try self.genExpr(cd.value); try self.genExpr(cd.value);
@@ -2745,10 +2831,10 @@ pub const CodeGen = struct {
// Target must be an identifier // Target must be an identifier
if (asgn.target.data != .identifier) return self.emitError("assignment target must be a variable"); if (asgn.target.data != .identifier) return self.emitError("assignment target must be a variable");
const name = asgn.target.data.identifier.name; const name = asgn.target.data.identifier.name;
const entry = self.named_values.get(name) orelse const lookup = self.lookupValue(name) orelse
self.global_mutable_vars.get(name) orelse {
return self.emitErrorFmt("undefined variable '{s}'", .{name}); return self.emitErrorFmt("undefined variable '{s}'", .{name});
}; const entry = lookup.asNamedValue() orelse
return self.emitErrorFmt("cannot assign to constant '{s}'", .{name});
// Meta type reassignment: x = Vec4, x = f64, x = test // 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) {
@@ -2789,7 +2875,7 @@ pub const CodeGen = struct {
// Tagged enum reassignment: s = .circle(3.14) or s = .none or s = fn_call() // Tagged enum reassignment: s = .circle(3.14) or s = .none or s = fn_call()
if (entry.ty.isUnion() and asgn.op == .assign) { if (entry.ty.isUnion() and asgn.op == .assign) {
if (self.tagged_enum_types.get(entry.ty.union_type)) |info| { if (self.lookupTaggedEnumInfo(entry.ty.union_type)) |info| {
const new_val = try self.genExprAsType(asgn.value, entry.ty); const new_val = try self.genExprAsType(asgn.value, entry.ty);
// genExprAsType returns alloca for enum literals, loaded value for calls // genExprAsType returns alloca for enum literals, loaded value for calls
_ = c.LLVMBuildStore(self.builder, self.loadIfPointer(new_val, info.llvm_type, "union_load"), entry.ptr); _ = c.LLVMBuildStore(self.builder, self.loadIfPointer(new_val, info.llvm_type, "union_load"), entry.ptr);
@@ -2840,7 +2926,7 @@ pub const CodeGen = struct {
// C-style union field assignment // C-style union field assignment
if (entry.ty.isUnion()) { if (entry.ty.isUnion()) {
const uname = entry.ty.union_type; const uname = entry.ty.union_type;
if (self.union_types.get(uname)) |info| { if (self.lookupUnionInfo(uname)) |info| {
if (self.findNameIndex(info.field_names, fa.field)) |fidx| { if (self.findNameIndex(info.field_names, fa.field)) |fidx| {
const field_ty = info.field_types[fidx]; const field_ty = info.field_types[fidx];
const rhs = try self.genExprAsType(asgn.value, field_ty); const rhs = try self.genExprAsType(asgn.value, field_ty);
@@ -2966,22 +3052,15 @@ pub const CodeGen = struct {
return self.buildStringSlice(ptr, self.constInt64(@intCast(content.len))); return self.buildStringSlice(ptr, self.constInt64(@intCast(content.len)));
}, },
.identifier => |ident| { .identifier => |ident| {
if (self.named_values.get(ident.name)) |entry| { if (self.lookupValue(ident.name)) |v| {
const llvm_ty = self.typeToLLVM(entry.ty); switch (v) {
return c.LLVMBuildLoad2(self.builder, llvm_ty, entry.ptr, "loadtmp"); .local => |nv| return c.LLVMBuildLoad2(self.builder, self.typeToLLVM(nv.ty), nv.ptr, "loadtmp"),
} .comptime_global => |ct| {
// Fall back to comptime globals (lazy resolution) if (!ct.is_resolved) try self.resolveComptimeGlobal(ct);
if (self.comptime_globals.getPtr(ident.name)) |ct| { return c.LLVMBuildLoad2(self.builder, self.typeToLLVM(ct.ty), ct.global, "ct_load");
if (!ct.is_resolved) { },
try self.resolveComptimeGlobal(ct); .global_mutable => |gm| return c.LLVMBuildLoad2(self.builder, self.typeToLLVM(gm.ty), gm.ptr, "global_load"),
} }
const llvm_ty = self.typeToLLVM(ct.ty);
return c.LLVMBuildLoad2(self.builder, llvm_ty, ct.global, "ct_load");
}
// Fall back to global mutable variables (e.g. function pointers from opengl.sx)
if (self.global_mutable_vars.get(ident.name)) |entry| {
const llvm_ty = self.typeToLLVM(entry.ty);
return c.LLVMBuildLoad2(self.builder, llvm_ty, entry.ptr, "global_load");
} }
// Fall back to function name → function pointer value // Fall back to function name → function pointer value
{ {
@@ -3010,7 +3089,7 @@ pub const CodeGen = struct {
if (result_type.isUnion() and (binop.op == .eq or binop.op == .neq)) { if (result_type.isUnion() and (binop.op == .eq or binop.op == .neq)) {
const uname = result_type.union_type; const uname = result_type.union_type;
const resolved = self.resolveAlias(uname); const resolved = self.resolveAlias(uname);
const info = self.tagged_enum_types.get(resolved) orelse return self.emitError("unknown tagged enum type"); const info = self.lookupTaggedEnumInfo(resolved) orelse return self.emitError("unknown tagged enum type");
const tag_ty = self.getEnumLLVMType(resolved); const tag_ty = self.getEnumLLVMType(resolved);
var lhs_val = try self.genExprAsType(binop.lhs, result_type); var lhs_val = try self.genExprAsType(binop.lhs, result_type);
@@ -3224,7 +3303,7 @@ pub const CodeGen = struct {
} }
// &u.field where u is a C-style union — all fields at offset 0 // &u.field where u is a C-style union — all fields at offset 0
if (entry.ty.isUnion()) { if (entry.ty.isUnion()) {
if (self.union_types.get(entry.ty.union_type)) |info| { if (self.lookupUnionInfo(entry.ty.union_type)) |info| {
if (self.findNameIndex(info.field_names, fa.field) != null) { if (self.findNameIndex(info.field_names, fa.field) != null) {
return entry.ptr; return entry.ptr;
} }
@@ -3237,7 +3316,7 @@ pub const CodeGen = struct {
// &p.field where p is *Struct — auto-deref through pointer // &p.field where p is *Struct — auto-deref through pointer
if (entry.ty.isPointer()) { if (entry.ty.isPointer()) {
const pointee_name = entry.ty.pointer_type.pointee_name; const pointee_name = entry.ty.pointer_type.pointee_name;
if (self.struct_types.get(pointee_name)) |info| { if (self.lookupStructInfo(pointee_name)) |info| {
const loaded_ptr = c.LLVMBuildLoad2(self.builder, const loaded_ptr = c.LLVMBuildLoad2(self.builder,
self.ptrType(), entry.ptr, "ptr_load"); self.ptrType(), entry.ptr, "ptr_load");
const idx = try self.findFieldIndex(info.field_names, fa.field, pointee_name); const idx = try self.findFieldIndex(info.field_names, fa.field, pointee_name);
@@ -3338,7 +3417,6 @@ pub const CodeGen = struct {
hoisted.name = synthetic_name; hoisted.name = synthetic_name;
try self.registerTaggedEnum(hoisted); try self.registerTaggedEnum(hoisted);
} else { } else {
try self.enum_types.put(synthetic_name, inline_ed.variant_names);
try self.type_registry.put(synthetic_name, .{ .plain_enum = inline_ed.variant_names }); try self.type_registry.put(synthetic_name, .{ .plain_enum = inline_ed.variant_names });
_ = try self.getAnyTypeId(synthetic_name, .{ .enum_type = synthetic_name }); _ = try self.getAnyTypeId(synthetic_name, .{ .enum_type = synthetic_name });
if (inline_ed.backing_type) |bt_node| { if (inline_ed.backing_type) |bt_node| {
@@ -3387,7 +3465,6 @@ pub const CodeGen = struct {
.field_defaults = resolved_defaults, .field_defaults = resolved_defaults,
.llvm_type = build.llvm_type, .llvm_type = build.llvm_type,
}; };
try self.struct_types.put(sd.name, sinfo);
try self.type_registry.put(sd.name, .{ .struct_info = sinfo }); try self.type_registry.put(sd.name, .{ .struct_info = sinfo });
_ = try self.getAnyTypeId(sd.name, .{ .struct_type = sd.name }); _ = try self.getAnyTypeId(sd.name, .{ .struct_type = sd.name });
} }
@@ -3424,7 +3501,6 @@ pub const CodeGen = struct {
.max_payload_size = layout.payload_size, .max_payload_size = layout.payload_size,
.payload_field_index = layout.payload_field_index, .payload_field_index = layout.payload_field_index,
}; };
try self.tagged_enum_types.put(ud.name, tei_layout);
try self.type_registry.put(ud.name, .{ .tagged_enum = tei_layout }); try self.type_registry.put(ud.name, .{ .tagged_enum = tei_layout });
} else { } else {
// Primitive backing type (e.g. enum u32 { ... }) // Primitive backing type (e.g. enum u32 { ... })
@@ -3442,7 +3518,6 @@ pub const CodeGen = struct {
.max_payload_size = build.max_payload_size, .max_payload_size = build.max_payload_size,
.payload_field_index = build.payload_field_index, .payload_field_index = build.payload_field_index,
}; };
try self.tagged_enum_types.put(ud.name, tei_build);
try self.type_registry.put(ud.name, .{ .tagged_enum = tei_build }); try self.type_registry.put(ud.name, .{ .tagged_enum = tei_build });
} }
@@ -3500,7 +3575,7 @@ pub const CodeGen = struct {
const resolved = self.resolveAlias(name); const resolved = self.resolveAlias(name);
if (Type.fromName(resolved) != null) return null; if (Type.fromName(resolved) != null) return null;
// Must be a registered struct // Must be a registered struct
if (self.struct_types.contains(resolved)) { if (self.lookupStructInfo(resolved) != null) {
return try self.validateEnumLayout(ud.name, resolved); return try self.validateEnumLayout(ud.name, resolved);
} }
} }
@@ -3509,7 +3584,7 @@ pub const CodeGen = struct {
} }
fn validateEnumLayout(self: *CodeGen, enum_name: []const u8, layout_name: []const u8) !EnumLayoutInfo { fn validateEnumLayout(self: *CodeGen, enum_name: []const u8, layout_name: []const u8) !EnumLayoutInfo {
const layout = self.struct_types.get(layout_name) orelse { const layout = self.lookupStructInfo(layout_name) orelse {
return self.emitErrorFmt("enum '{s}': layout type '{s}' is not a registered struct", .{ enum_name, layout_name }); return self.emitErrorFmt("enum '{s}': layout type '{s}' is not a registered struct", .{ enum_name, layout_name });
}; };
@@ -3607,7 +3682,7 @@ pub const CodeGen = struct {
// Check if this is an anonymous struct (name contains __anon_) // Check if this is an anonymous struct (name contains __anon_)
const sname = fty.struct_type; const sname = fty.struct_type;
if (std.mem.indexOf(u8, sname, ".__anon_") != null) { if (std.mem.indexOf(u8, sname, ".__anon_") != null) {
if (self.struct_types.get(sname)) |sinfo| { if (self.lookupStructInfo(sname)) |sinfo| {
for (sinfo.field_names, 0..) |sf_name, sf_idx| { for (sinfo.field_names, 0..) |sf_name, sf_idx| {
try promoted.put(sf_name, .{ try promoted.put(sf_name, .{
.struct_name = sname, .struct_name = sname,
@@ -3627,7 +3702,6 @@ pub const CodeGen = struct {
.total_size = max_size, .total_size = max_size,
.promoted_fields = promoted, .promoted_fields = promoted,
}; };
try self.union_types.put(ud.name, uinfo);
try self.type_registry.put(ud.name, .{ .union_info = uinfo }); try self.type_registry.put(ud.name, .{ .union_info = uinfo });
// Note: C-style unions are not registered with the Any type system. // Note: C-style unions are not registered with the Any type system.
// They can't be meaningfully printed as a whole — access individual fields instead. // They can't be meaningfully printed as a whole — access individual fields instead.
@@ -3901,7 +3975,7 @@ pub const CodeGen = struct {
const alloca = try self.genStructLiteral(node.data.struct_literal, target_ty.struct_type); const alloca = try self.genStructLiteral(node.data.struct_literal, target_ty.struct_type);
// genStructLiteral returns an alloca pointer — load the value for by-value passing // genStructLiteral returns an alloca pointer — load the value for by-value passing
const sname = self.resolveAlias(target_ty.struct_type); const sname = self.resolveAlias(target_ty.struct_type);
if (self.struct_types.get(sname)) |si| { if (self.lookupStructInfo(sname)) |si| {
return c.LLVMBuildLoad2(self.builder, si.llvm_type, alloca, "struct_val"); return c.LLVMBuildLoad2(self.builder, si.llvm_type, alloca, "struct_val");
} }
return alloca; return alloca;
@@ -3986,13 +4060,13 @@ pub const CodeGen = struct {
const pointee_name = target_ty.pointer_type.pointee_name; const pointee_name = target_ty.pointer_type.pointee_name;
const src_matches = if (src_ty.isStruct()) const src_matches = if (src_ty.isStruct())
std.mem.eql(u8, src_ty.struct_type, pointee_name) or 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.lookupAlias(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) (if (self.lookupAlias(pointee_name)) |alias| std.mem.eql(u8, alias, src_ty.struct_type) else false)
else if (src_ty.isUnion()) blk: { else if (src_ty.isUnion()) blk: {
const uname = src_ty.union_type; const uname = src_ty.union_type;
break :blk std.mem.eql(u8, uname, pointee_name) or break :blk std.mem.eql(u8, uname, pointee_name) or
(if (self.type_aliases.get(uname)) |alias| std.mem.eql(u8, alias, pointee_name) else false) or (if (self.lookupAlias(uname)) |alias| std.mem.eql(u8, alias, pointee_name) else false) or
(if (self.type_aliases.get(pointee_name)) |alias| std.mem.eql(u8, alias, uname) else false); (if (self.lookupAlias(pointee_name)) |alias| std.mem.eql(u8, alias, uname) else false);
} else if (Type.fromName(pointee_name)) |pointee_ty| } else if (Type.fromName(pointee_name)) |pointee_ty|
src_ty.eql(pointee_ty) src_ty.eql(pointee_ty)
else else
@@ -4012,8 +4086,8 @@ pub const CodeGen = struct {
// Struct literals return alloca pointers — load the value for by-value passing // Struct literals return alloca pointers — load the value for by-value passing
if (src_ty.isStruct() and target_ty.isStruct()) { if (src_ty.isStruct() and target_ty.isStruct()) {
if (c.LLVMGetTypeKind(c.LLVMTypeOf(val)) == c.LLVMPointerTypeKind) { if (c.LLVMGetTypeKind(c.LLVMTypeOf(val)) == c.LLVMPointerTypeKind) {
const info = self.struct_types.get(src_ty.struct_type) orelse const info = self.lookupStructInfo(src_ty.struct_type) orelse
self.struct_types.get(self.resolveAlias(src_ty.struct_type)); self.lookupStructInfo(self.resolveAlias(src_ty.struct_type));
if (info) |si| { if (info) |si| {
val = c.LLVMBuildLoad2(self.builder, si.llvm_type, val, "struct_load"); val = c.LLVMBuildLoad2(self.builder, si.llvm_type, val, "struct_load");
} }
@@ -4083,7 +4157,7 @@ pub const CodeGen = struct {
} }
if (target_ty.isStruct()) { if (target_ty.isStruct()) {
const sname = target_ty.struct_type; const sname = target_ty.struct_type;
if (self.struct_types.get(sname)) |info| { if (self.lookupStructInfo(sname)) |info| {
return self.loadFromI64Ptr(i64_val, info.llvm_type, "any_to_struct"); return self.loadFromI64Ptr(i64_val, info.llvm_type, "any_to_struct");
} }
} }
@@ -4095,7 +4169,7 @@ pub const CodeGen = struct {
} }
if (target_ty.isUnion()) { if (target_ty.isUnion()) {
const uname = target_ty.union_type; const uname = target_ty.union_type;
if (self.tagged_enum_types.get(uname)) |info| { if (self.lookupTaggedEnumInfo(uname)) |info| {
return self.loadFromI64Ptr(i64_val, info.llvm_type, "any_to_union"); return self.loadFromI64Ptr(i64_val, info.llvm_type, "any_to_union");
} }
} }
@@ -4144,7 +4218,7 @@ pub const CodeGen = struct {
// 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;
if (self.tagged_enum_types.get(uname)) |info| { if (self.lookupTaggedEnumInfo(uname)) |info| {
const tag_llvm_ty = self.getEnumLLVMType(uname); const tag_llvm_ty = self.getEnumLLVMType(uname);
const tag_bits = c.LLVMGetIntTypeWidth(tag_llvm_ty); const tag_bits = c.LLVMGetIntTypeWidth(tag_llvm_ty);
const tmp = self.buildEntryBlockAlloca(info.llvm_type, "union_cast"); const tmp = self.buildEntryBlockAlloca(info.llvm_type, "union_cast");
@@ -4333,7 +4407,7 @@ pub const CodeGen = struct {
return c.LLVMConstInt(i64_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.lookupEnumVariants(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(i64_ty, variants.len, 0); return c.LLVMConstInt(i64_ty, variants.len, 0);
} }
@@ -4359,7 +4433,7 @@ pub const CodeGen = struct {
const info = try self.getStructInfo(ty.struct_type); const info = try self.getStructInfo(ty.struct_type);
break :blk .{ info.field_names, ty.struct_type }; break :blk .{ info.field_names, ty.struct_type };
} else if (ty.isEnum()) blk: { } else if (ty.isEnum()) blk: {
const variants = self.enum_types.get(ty.enum_type) orelse const variants = self.lookupEnumVariants(ty.enum_type) orelse
return self.emitErrorFmt("unknown enum type '{s}'", .{ty.enum_type}); return self.emitErrorFmt("unknown enum type '{s}'", .{ty.enum_type});
break :blk .{ variants, ty.enum_type }; break :blk .{ variants, ty.enum_type };
} else if (ty.isUnion()) blk: { } else if (ty.isUnion()) blk: {
@@ -4558,7 +4632,7 @@ pub const CodeGen = struct {
} }
const enum_name = ty.enum_type; const enum_name = ty.enum_type;
const values = self.enum_variant_values.get(enum_name); const values = self.enum_variant_values.get(enum_name);
const variants = self.enum_types.get(enum_name) orelse return try self.genExpr(call_node.args[1]); const variants = self.lookupEnumVariants(enum_name) orelse return try self.genExpr(call_node.args[1]);
const n = variants.len; const n = variants.len;
const idx = try self.genExpr(call_node.args[1]); const idx = try self.genExpr(call_node.args[1]);
@@ -4587,6 +4661,57 @@ pub const CodeGen = struct {
if (call_node.args.len != 2) return self.emitError("field_index expects 2 arguments: field_index(T, value)"); if (call_node.args.len != 2) return self.emitError("field_index expects 2 arguments: field_index(T, value)");
const ty = self.resolveType(call_node.args[0]); const ty = self.resolveType(call_node.args[0]);
const i64_type = self.i64Type(); const i64_type = self.i64Type();
// Handle tagged enums (union_type) — extract tag from field 0
if (ty == .union_type) {
const union_name = ty.union_type;
const info = self.lookupTaggedEnumInfo(union_name) orelse {
_ = try self.genExpr(call_node.args[1]);
return c.LLVMConstInt(i64_type, 0, 0);
};
const values = self.enum_variant_values.get(union_name);
const n = info.variant_names.len;
const val = try self.genExpr(call_node.args[1]);
// Extract tag from field 0 of the { tag, payload } struct
const tag_val = self.extractValue(val, 0, "fi_tag");
const enum_llvm_ty = self.getEnumLLVMType(union_name);
const sw_val = if (c.LLVMTypeOf(tag_val) != enum_llvm_ty)
c.LLVMBuildIntCast2(self.builder, tag_val, enum_llvm_ty, 0, "fi_cast")
else
tag_val;
const sb = self.buildSwitch(sw_val, @intCast(n), "fi_merge", "fi_default");
var phi_vals = std.ArrayList(c.LLVMValueRef).empty;
var phi_bbs = std.ArrayList(c.LLVMBasicBlockRef).empty;
var seen_values = std.ArrayList(u64).empty;
for (0..n) |i| {
const explicit_val: u64 = if (values) |vals| @bitCast(vals[i]) else i;
var is_dup = false;
for (seen_values.items) |sv| {
if (sv == explicit_val) {
is_dup = true;
break;
}
}
if (is_dup) continue;
try seen_values.append(self.allocator, explicit_val);
const case_bb = self.appendBB("fi_case");
c.LLVMAddCase(sb.sw, c.LLVMConstInt(enum_llvm_ty, explicit_val, 0), case_bb);
self.positionAt(case_bb);
try self.addPhiCase(&phi_vals, &phi_bbs, c.LLVMConstInt(i64_type, i, 0), sb.merge_bb);
}
self.positionAt(sb.default_bb);
const neg_one = c.LLVMConstInt(i64_type, @bitCast(@as(i64, -1)), 0);
try self.addPhiCase(&phi_vals, &phi_bbs, neg_one, sb.merge_bb);
self.positionAt(sb.merge_bb);
return try self.buildPhiNode(&phi_vals, &phi_bbs, i64_type, "fi_result");
}
if (!ty.isEnum()) { if (!ty.isEnum()) {
_ = try self.genExpr(call_node.args[1]); _ = try self.genExpr(call_node.args[1]);
return c.LLVMConstInt(i64_type, 0, 0); return c.LLVMConstInt(i64_type, 0, 0);
@@ -4598,7 +4723,7 @@ pub const CodeGen = struct {
return c.LLVMConstInt(i64_type, 0, 0); return c.LLVMConstInt(i64_type, 0, 0);
} }
const values = self.enum_variant_values.get(enum_name); const values = self.enum_variant_values.get(enum_name);
const variants = self.enum_types.get(enum_name) orelse return try self.genExpr(call_node.args[1]); const variants = self.lookupEnumVariants(enum_name) orelse return try self.genExpr(call_node.args[1]);
const n = variants.len; const n = variants.len;
const val = try self.genExpr(call_node.args[1]); const val = try self.genExpr(call_node.args[1]);
@@ -4736,7 +4861,7 @@ pub const CodeGen = struct {
if (entry.ty.isUnion()) { if (entry.ty.isUnion()) {
const uname = entry.ty.union_type; const uname = entry.ty.union_type;
// C-style (untagged) union: bitcast pointer and load // C-style (untagged) union: bitcast pointer and load
if (self.union_types.get(uname)) |info| { if (self.lookupUnionInfo(uname)) |info| {
if (self.findNameIndex(info.field_names, fa.field)) |fidx| { if (self.findNameIndex(info.field_names, fa.field)) |fidx| {
const field_ty = info.field_types[fidx]; const field_ty = info.field_types[fidx];
return self.loadTyped(field_ty, entry.ptr, "union_field"); return self.loadTyped(field_ty, entry.ptr, "union_field");
@@ -5059,8 +5184,11 @@ pub const CodeGen = struct {
const resolved_type: ?Type = blk: { const resolved_type: ?Type = blk: {
if (fa.object.data == .identifier) { if (fa.object.data == .identifier) {
const name = self.resolveAlias(fa.object.data.identifier.name); const name = self.resolveAlias(fa.object.data.identifier.name);
if (self.tagged_enum_types.contains(name)) break :blk .{ .union_type = name }; if (self.type_registry.get(name)) |e| switch (e) {
if (self.struct_types.contains(name)) break :blk .{ .struct_type = name }; .tagged_enum => break :blk Type{ .union_type = name },
.struct_info => break :blk Type{ .struct_type = name },
else => {},
};
} else { } else {
const ty = self.resolveType(fa.object); const ty = self.resolveType(fa.object);
if (ty.isUnion() or ty.isStruct()) break :blk ty; if (ty.isUnion() or ty.isStruct()) break :blk ty;
@@ -5179,11 +5307,12 @@ pub const CodeGen = struct {
} }
// Function pointer indirect call: callee is a variable with function_type // Function pointer indirect call: callee is a variable with function_type
if (callee_fn == null) { if (callee_fn == null) {
const fp_entry = if (self.named_values.get(callee_name)) |e| e if (self.lookupValue(callee_name)) |v| {
else self.global_mutable_vars.get(callee_name); const entry = v.asNamedValue();
if (fp_entry) |entry| { if (entry) |e| {
if (entry.ty.isFunctionType()) { if (e.ty.isFunctionType()) {
return self.genIndirectCall(entry, call_node); return self.genIndirectCall(e, call_node);
}
} }
} }
return self.emitErrorFmt("undefined function '{s}'", .{callee_name}); return self.emitErrorFmt("undefined function '{s}'", .{callee_name});
@@ -5416,10 +5545,7 @@ pub const CodeGen = struct {
// It's a runtime type if it's a named_value, not a type name // It's a runtime type if it's a named_value, not a type name
if (self.named_values.contains(name) and if (self.named_values.contains(name) and
Type.fromName(name) == null and Type.fromName(name) == null and
!self.struct_types.contains(name) and !self.type_registry.contains(name))
!self.enum_types.contains(name) and
!self.tagged_enum_types.contains(name) and
!self.type_aliases.contains(name))
{ {
return self.genGenericCallWithRuntimeDispatch(template, call_node, match_tags); return self.genGenericCallWithRuntimeDispatch(template, call_node, match_tags);
} }
@@ -5492,7 +5618,7 @@ pub const CodeGen = struct {
arg_ty.struct_type arg_ty.struct_type
else else
""; "";
if (self.struct_types.get(struct_name)) |info| { if (self.lookupStructInfo(struct_name)) |info| {
if (info.template_name) |tmpl_name| { if (info.template_name) |tmpl_name| {
if (std.mem.eql(u8, tmpl_name, pte.name)) { if (std.mem.eql(u8, tmpl_name, pte.name)) {
// Match generic args against stored type param bindings // Match generic args against stored type param bindings
@@ -6183,6 +6309,7 @@ pub const CodeGen = struct {
// Create basic blocks // Create basic blocks
const cond_bb = self.appendBB("for.cond"); const cond_bb = self.appendBB("for.cond");
const body_bb = self.appendBB("for.body"); const body_bb = self.appendBB("for.body");
const incr_bb = self.appendBB("for.incr");
const after_bb = self.appendBB("for.after"); const after_bb = self.appendBB("for.after");
self.br(cond_bb); self.br(cond_bb);
@@ -6216,22 +6343,26 @@ pub const CodeGen = struct {
const saved_break_bb = self.loop_break_bb; const saved_break_bb = self.loop_break_bb;
const saved_continue_bb = self.loop_continue_bb; const saved_continue_bb = self.loop_continue_bb;
self.loop_break_bb = after_bb; self.loop_break_bb = after_bb;
self.loop_continue_bb = cond_bb; self.loop_continue_bb = incr_bb;
_ = try self.genExpr(for_expr.body); _ = try self.genExpr(for_expr.body);
self.loop_break_bb = saved_break_bb; self.loop_break_bb = saved_break_bb;
self.loop_continue_bb = saved_continue_bb; self.loop_continue_bb = saved_continue_bb;
// Increment it_index // Fall through to increment block
const current_bb = self.getCurrentBlock(); const current_bb = self.getCurrentBlock();
if (c.LLVMGetBasicBlockTerminator(current_bb) == null) { if (c.LLVMGetBasicBlockTerminator(current_bb) == null) {
const inc_idx = c.LLVMBuildLoad2(self.builder, i64_type, idx_alloca, "inc_idx"); self.br(incr_bb);
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);
self.br(cond_bb);
} }
// Increment it_index, then branch back to condition
self.positionAt(incr_bb);
const inc_idx = c.LLVMBuildLoad2(self.builder, i64_type, idx_alloca, "inc_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);
self.br(cond_bb);
self.positionAt(after_bb); self.positionAt(after_bb);
try self.popScope(); try self.popScope();
@@ -6241,7 +6372,7 @@ 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 enum_ty = self.getEnumLLVMType(enum_type_name); const enum_ty = self.getEnumLLVMType(enum_type_name);
const variants = self.enum_types.get(enum_type_name) orelse return c.LLVMConstInt(enum_ty, 0, 0); const variants = self.lookupEnumVariants(enum_type_name) orelse return c.LLVMConstInt(enum_ty, 0, 0);
const values = self.enum_variant_values.get(enum_type_name); const values = self.enum_variant_values.get(enum_type_name);
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)) {
@@ -6281,14 +6412,14 @@ pub const CodeGen = struct {
const subject_val: c.LLVMValueRef = if (union_name != null) blk: { const subject_val: c.LLVMValueRef = if (union_name != null) blk: {
// Union: load tag from field 0 of the alloca // Union: load tag from field 0 of the alloca
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.tagged_enum_types.get(union_name.?).?; const info = self.lookupTaggedEnumInfo(union_name.?).?;
break :blk self.loadStructField(info.llvm_type, entry.ptr, 0, self.getEnumLLVMType(union_name.?)); break :blk self.loadStructField(info.llvm_type, entry.ptr, 0, self.getEnumLLVMType(union_name.?));
} 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|
(if (self.tagged_enum_types.get(un)) |info| info.variant_names else null) (if (self.lookupTaggedEnumInfo(un)) |info| info.variant_names else null)
else if (enum_name) |en| else if (enum_name) |en|
self.enum_types.get(en) self.lookupEnumVariants(en)
else else
null; null;
@@ -6372,7 +6503,7 @@ pub const CodeGen = struct {
// Payload capture: bind variant payload as a local variable // Payload capture: bind variant payload as a local variable
if (arm.capture) |cap_name| { if (arm.capture) |cap_name| {
if (union_name) |un| { if (union_name) |un| {
const uinfo = self.tagged_enum_types.get(un).?; const uinfo = self.lookupTaggedEnumInfo(un).?;
const pat = arm.pattern.?; const pat = arm.pattern.?;
if (pat.data == .enum_literal) { if (pat.data == .enum_literal) {
const vname = pat.data.enum_literal.name; const vname = pat.data.enum_literal.name;
@@ -6515,14 +6646,13 @@ pub const CodeGen = struct {
return tags; return tags;
} }
// Named type (struct/enum/union) — get dynamic ID // Named type (struct/enum/union) — get dynamic ID
const sx_type: Type = if (self.struct_types.contains(name)) const sx_type: Type = if (self.type_registry.get(name)) |e| switch (e) {
.{ .struct_type = name } .struct_info => Type{ .struct_type = name },
else if (self.enum_types.contains(name)) .plain_enum => Type{ .enum_type = name },
.{ .enum_type = name } .tagged_enum => Type{ .union_type = name },
else if (self.tagged_enum_types.contains(name)) .union_info => Type{ .union_type = name },
.{ .union_type = name } .alias => Type{ .struct_type = name },
else } else .{ .struct_type = name }; // fallback
.{ .struct_type = name }; // fallback
const id = try self.getAnyTypeId(name, sx_type); const id = try self.getAnyTypeId(name, sx_type);
const tags = try self.allocator.alloc(u64, 1); const tags = try self.allocator.alloc(u64, 1);
tags[0] = id; tags[0] = id;
@@ -6714,7 +6844,7 @@ pub const CodeGen = struct {
/// Resolve a name to a type display string, or null if not a type. /// Resolve a name to a type display string, or null if not a type.
fn resolveTypeName(self: *CodeGen, name: []const u8) ?[]const u8 { fn resolveTypeName(self: *CodeGen, name: []const u8) ?[]const u8 {
const resolved = self.resolveAlias(name); const resolved = self.resolveAlias(name);
if (self.struct_types.get(resolved)) |info| return info.display_name orelse resolved; if (self.lookupStructInfo(resolved)) |info| return info.display_name orelse resolved;
if (self.resolveTypeFromName(name) != null) return resolved; if (self.resolveTypeFromName(name) != null) return resolved;
return null; return null;
} }
@@ -6752,7 +6882,7 @@ pub const CodeGen = struct {
} }
} }
// Type aliases // Type aliases
if (self.type_aliases.get(name)) |target| return self.resolveTypeFromName(target); if (self.lookupAlias(name)) |target| return self.resolveTypeFromName(target);
return null; return null;
} }
@@ -6776,9 +6906,7 @@ pub const CodeGen = struct {
}, },
.chained_comparison => return .boolean, .chained_comparison => return .boolean,
.identifier => |ident| { .identifier => |ident| {
if (self.named_values.get(ident.name)) |entry| return entry.ty; if (self.lookupValue(ident.name)) |v| return v.ty();
if (self.comptime_globals.get(ident.name)) |ct| return ct.ty;
if (self.global_mutable_vars.get(ident.name)) |entry| return entry.ty;
return Type.s(64); return Type.s(64);
}, },
.if_expr => |ie| { .if_expr => |ie| {
@@ -6808,7 +6936,7 @@ pub const CodeGen = struct {
const obj_ty = blk: { const obj_ty = blk: {
if (fa.object.data == .identifier) { if (fa.object.data == .identifier) {
const name = self.resolveAlias(fa.object.data.identifier.name); const name = self.resolveAlias(fa.object.data.identifier.name);
if (self.tagged_enum_types.contains(name)) break :blk Type{ .union_type = name }; if (self.lookupTaggedEnumInfo(name) != null) break :blk Type{ .union_type = name };
} }
const ty = self.resolveType(fa.object); const ty = self.resolveType(fa.object);
if (ty.isUnion()) break :blk ty; if (ty.isUnion()) break :blk ty;
@@ -6921,11 +7049,9 @@ pub const CodeGen = struct {
} }
// Check if callee is a variable with function pointer type // Check if callee is a variable with function pointer type
{ {
const fp_entry = if (self.named_values.get(callee_name)) |e| e if (self.lookupValue(callee_name)) |v| {
else self.global_mutable_vars.get(callee_name); if (v.ty().isFunctionType()) {
if (fp_entry) |entry| { return v.ty().function_type.return_type.*;
if (entry.ty.isFunctionType()) {
return entry.ty.function_type.return_type.*;
} }
} }
} }
@@ -6969,14 +7095,14 @@ pub const CodeGen = struct {
return obj_ty.vectorElementType() orelse Type.s(64); 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.lookupStructInfo(obj_ty.struct_type)) |info| {
if (self.findNameIndex(info.field_names, fa.field)) |idx| { if (self.findNameIndex(info.field_names, fa.field)) |idx| {
return info.field_types[idx]; return info.field_types[idx];
} }
} }
} }
if (obj_ty.isUnion()) { if (obj_ty.isUnion()) {
if (self.union_types.get(obj_ty.union_type)) |info| { if (self.lookupUnionInfo(obj_ty.union_type)) |info| {
if (self.findNameIndex(info.field_names, fa.field)) |idx| { if (self.findNameIndex(info.field_names, fa.field)) |idx| {
return info.field_types[idx]; return info.field_types[idx];
} }
@@ -6984,7 +7110,7 @@ pub const CodeGen = struct {
return pf.field_type; return pf.field_type;
} }
} }
if (self.tagged_enum_types.get(obj_ty.union_type)) |info| { if (self.lookupTaggedEnumInfo(obj_ty.union_type)) |info| {
for (info.variant_names, 0..) |vn, i| { for (info.variant_names, 0..) |vn, i| {
if (std.mem.eql(u8, vn, fa.field)) { if (std.mem.eql(u8, vn, fa.field)) {
return info.variant_types[i]; return info.variant_types[i];

View File

@@ -745,6 +745,7 @@ pub const Parser = struct {
} }
// Body: block `{ ... }`, arrow `=> expr;`, #builtin, or #foreign marker // Body: block `{ ... }`, arrow `=> expr;`, #builtin, or #foreign marker
var is_arrow = false;
const body = if (self.current.tag == .hash_builtin) blk: { const body = if (self.current.tag == .hash_builtin) blk: {
const bi_start = self.current.loc.start; const bi_start = self.current.loc.start;
self.advance(); self.advance();
@@ -756,6 +757,7 @@ pub const Parser = struct {
try self.expect(.semicolon); try self.expect(.semicolon);
break :blk try self.createNode(fi_start, .{ .foreign_expr = {} }); break :blk try self.createNode(fi_start, .{ .foreign_expr = {} });
} else if (self.current.tag == .fat_arrow) blk: { } else if (self.current.tag == .fat_arrow) blk: {
is_arrow = true;
self.advance(); self.advance();
const expr = try self.parseExpr(); const expr = try self.parseExpr();
try self.expect(.semicolon); try self.expect(.semicolon);
@@ -774,6 +776,7 @@ pub const Parser = struct {
.return_type = return_type, .return_type = return_type,
.body = body, .body = body,
.type_params = type_params, .type_params = type_params,
.is_arrow = is_arrow,
} }); } });
} }

View File

@@ -3,4 +3,4 @@ v2: Vec4{x: 4.000000, y: 1.000000, z: 1.000000, w: 3.000000}
v3: Vec4{x: 2.000000, y: 3.000000, z: 4.000000, w: 0.000000} v3: Vec4{x: 2.000000, y: 3.000000, z: 4.000000, w: 0.000000}
v4: Vec4{x: 9.000000, y: 0.000000, z: 5.000000, w: 6.000000} v4: Vec4{x: 9.000000, y: 0.000000, z: 5.000000, w: 6.000000}
Complex{foo: .S(Complex.foo.B{val: hello})} Complex{foo: .B(Complex.foo.B{val: hello})}

View File

@@ -8,7 +8,7 @@ v2[1]: 3.000000
scaled: [2.000000, 6.000000, 4.000000] scaled: [2.000000, 6.000000, 4.000000]
neg: [-1.000000, -3.000000, -2.000000] neg: [-1.000000, -3.000000, -2.000000]
sqrt(9): 3.000000 sqrt(9): 3.000000
.counter(0.500000) .user(0.500000)
4 4
16 16
8 8

View File

@@ -1,7 +1,7 @@
circle: .circle(3.140000) circle: .circle(3.140000)
radius: 3.140000 radius: 3.140000
none: .circle none: .none
rect: .circle(Shape.rect{w: 4.000000, h: 2.000000}) rect: .rect(Shape.rect{w: 4.000000, h: 2.000000})
sh: .circle(2.710000) sh: .circle(2.710000)
rect val: Shape.rect{w: 2.000000, h: 4.000000} rect val: Shape.rect{w: 2.000000, h: 4.000000}
matched rect matched rect

View File

@@ -0,0 +1 @@
0

148
tests/expected/50-smoke.txt Normal file
View File

@@ -0,0 +1,148 @@
=== 1. Literals ===
decimal: 42
hex: 255
binary: 10
float: 3.140000
f64: 2.718281
true: true
false: false
escapes: hello world
multiline: line1
line2
heredoc: raw heredoc
undef-then-set: 77
enum-lit: .green
=== 2. Operators ===
add: 7
sub: 7
mul: 42
div: 5
mod: 2
neg: -5
eq: true
neq: true
lt: true
gt: true
le: true
ge: true
chain: true
chain-gt: true
chain-mixed: true
band: 15
bor: 7
and: true
and-false: false
or: true
or-false: false
ca+=: 15
ca-=: 12
ca*=: 24
ca/=: 4
prec1: 14
prec2: 20
xx-cast: 200
=== 3. Types ===
s8: 127
s16: 32000
s32: 100000
u8: 255
u16: 65000
u32: 4000000
alias: 1.500000
struct-pos: Point{x: 1, y: 2}
struct-prefix: Point{x: 3, y: 4}
struct-named: Point{x: 20, y: 10}
struct-shorthand: Point{x: 5, y: 6}
defaults: a=0 b=99
field-assign: Point{x: 42, y: 99}
enum: .red
backing: .err
tagged: .circle(3.140000)
payload: 3.140000
void-variant: .none
match: rect
match-expr: 10
capture: 9.500000
else-match: other
bool: true
union-f: 3.140000
union-i: 1078523331
promoted-x: 1.000000
promoted-data0: 1.000000
arr[2]: 30
arr.len: 5
sl[0]: 1
sl.len: 5
sub: [20, 30, 40]
head: [10, 20, 30]
tail: [30, 40, 50]
strsub: world
deref: Point{x: 10, y: 20}
auto-deref: 10
mp[0]: 10
mp[3]: 40
=== 4. Control Flow ===
ite: 1
if-block: yes
if-block-expr: 15
while: 5
while-break: 7
while-continue: 25
for: 10 20 30 40
for-print: 10 20 30 40
for-idx: 0 1 2 3
for-2arg: 10@0 20@1 30@2 40@3
for-break: 10 20
for-continue: 10 30 40
=== 5. Functions ===
const: 42
typed-const: 3.140000
default-init: 0
implicit-ret: 42
early-ret: 5
early-ret2: 99
void-return: ok
generic-s32: 42
generic-f32: 1.500000
generic-multi: 30
lambda: 14
lambda-ret: 5.000000
varargs: 15
spread: 60
fp: 7
fp-reassign: 12
fp-apply: 30
=== 6. Scoping ===
inner: 200
outer: 100
nest3: 3
nest2: 2
nest1: 1
defer-a
defer-b
defer-c
inner-defer
outer-defer
=== 7. Builtins ===
write-ok
sqrt: 3.000000
sizeof-s32: 4
sizeof-f64: 8
typeof: int
typename: Point
fieldcount: 2
fieldname0: x
fieldname1: y
fieldval0: 11
fieldval1: 22
fieldidx: 1
cast: 3
=== 8. Comptime ===
run-const: 25
insert-ok
=== 9. Flags ===
flags: .read | .write
has-read: yes
flags-raw: 3
=== DONE ===