smoke tests
This commit is contained in:
618
examples/50-smoke.sx
Normal file
618
examples/50-smoke.sx
Normal 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");
|
||||
}
|
||||
@@ -89,6 +89,7 @@ pub const FnDecl = struct {
|
||||
return_type: ?*Node,
|
||||
body: *Node,
|
||||
type_params: []const StructTypeParam = &.{},
|
||||
is_arrow: bool = false,
|
||||
};
|
||||
|
||||
pub const Param = struct {
|
||||
|
||||
416
src/codegen.zig
416
src/codegen.zig
@@ -107,17 +107,7 @@ pub const CodeGen = struct {
|
||||
|
||||
// Symbol table: maps variable names to their alloca pointers
|
||||
named_values: std.StringHashMap(NamedValue),
|
||||
// Enum type registry: maps enum name to variant list
|
||||
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
|
||||
// Unified type registry: single lookup for all named types (structs, enums, unions, aliases)
|
||||
type_registry: std.StringHashMap(TypeRegistryEntry),
|
||||
// Flags enum registry: tracks which enum names are flags
|
||||
flags_enum_types: std.StringHashMap(void),
|
||||
@@ -292,6 +282,34 @@ pub const CodeGen = struct {
|
||||
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 {
|
||||
const ctx = c.LLVMContextCreate();
|
||||
const module = c.LLVMModuleCreateWithNameInContext(module_name, ctx);
|
||||
@@ -331,11 +349,6 @@ pub const CodeGen = struct {
|
||||
.builder = builder,
|
||||
.allocator = 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),
|
||||
.flags_enum_types = std.StringHashMap(void).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 {
|
||||
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.comptime_globals.deinit();
|
||||
self.enum_backing_types.deinit();
|
||||
@@ -393,11 +401,52 @@ pub const CodeGen = struct {
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
@@ -419,6 +468,14 @@ pub const CodeGen = struct {
|
||||
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
|
||||
/// stack space is reserved once, not on every loop iteration.
|
||||
fn buildEntryBlockAlloca(self: *CodeGen, ty: c.LLVMTypeRef, name: [*:0]const u8) c.LLVMValueRef {
|
||||
@@ -461,8 +518,8 @@ pub const CodeGen = struct {
|
||||
.boolean => self.i1Type(),
|
||||
.string_type, .slice_type => self.getStringStructType(), // slices use same {ptr, i32} layout
|
||||
.enum_type => |name| self.getEnumLLVMType(name),
|
||||
.struct_type => |name| if (self.struct_types.get(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,
|
||||
.struct_type => |name| if (self.lookupStructInfo(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| {
|
||||
const elem_ty = Type.fromName(info.element_name) orelse unreachable;
|
||||
return c.LLVMArrayType2(self.typeToLLVM(elem_ty), info.length);
|
||||
@@ -554,7 +611,7 @@ pub const CodeGen = struct {
|
||||
.struct_type => |name| {
|
||||
_ = try self.getAnyTypeId(name, sx_type);
|
||||
// Recursively register struct field types
|
||||
if (self.struct_types.get(name)) |info| {
|
||||
if (self.lookupStructInfo(name)) |info| {
|
||||
for (info.field_types) |ft| {
|
||||
try self.preRegisterAnyType(ft);
|
||||
}
|
||||
@@ -610,6 +667,7 @@ pub const CodeGen = struct {
|
||||
|
||||
// Convert value to i64
|
||||
const val_as_i64 = switch (ty) {
|
||||
.void_type => c.LLVMConstInt(i64_ty, 0, 0),
|
||||
.boolean => self.zExt(val, i64_ty, "any_bool"),
|
||||
.signed => |w| if (w <= 32)
|
||||
self.sExt(val, i64_ty, "any_int")
|
||||
@@ -628,7 +686,7 @@ pub const CodeGen = struct {
|
||||
.string_type => self.allocaStoreAsI64(self.getStringStructType(), val, "any_str"),
|
||||
.struct_type => |sname| blk: {
|
||||
// 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);
|
||||
break :blk self.allocaStoreAsI64(info.llvm_type, val, "any_struct");
|
||||
},
|
||||
@@ -643,7 +701,7 @@ pub const CodeGen = struct {
|
||||
},
|
||||
.union_type => |uname| blk: {
|
||||
// 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);
|
||||
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 {
|
||||
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 {
|
||||
@@ -982,7 +1040,6 @@ pub const CodeGen = struct {
|
||||
try self.registerTaggedEnum(ed);
|
||||
} else {
|
||||
// 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.getAnyTypeId(ed.name, .{ .enum_type = ed.name });
|
||||
|
||||
@@ -1025,7 +1082,6 @@ pub const CodeGen = struct {
|
||||
} else if (cd.value.data == .lambda) {
|
||||
try self.registerLambdaAsFunction(cd.name, cd.value.data.lambda);
|
||||
} 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 });
|
||||
} else if (cd.value.data == .call) {
|
||||
// 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);
|
||||
const result_ty = try self.instantiateGenericStruct(cn, tmpl, cd.value.data.call.args);
|
||||
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 });
|
||||
}
|
||||
} else if (self.generic_templates.get(cn)) |tmpl| {
|
||||
// Type-returning function: Foo :: Complex(u32);
|
||||
const result_ty = try self.instantiateTypeFunction(cd.name, cn, tmpl, cd.value.data.call.args);
|
||||
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 });
|
||||
} 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 });
|
||||
}
|
||||
} else if (self.builtin_functions.contains(cn)) {
|
||||
// Builtin type function (e.g., Vector(4, f32), Array(5, s32))
|
||||
if (self.resolveBuiltinType(cn, cd.value.data.call.args)) |result_ty| {
|
||||
const display = try result_ty.displayName(self.allocator);
|
||||
try self.type_aliases.put(cd.name, display);
|
||||
try self.type_registry.put(cd.name, .{ .alias = display });
|
||||
} else {
|
||||
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);
|
||||
|
||||
// Check if already instantiated
|
||||
if (self.struct_types.contains(mangled_name)) {
|
||||
if (self.type_registry.contains(mangled_name)) {
|
||||
return .{ .struct_type = mangled_name };
|
||||
}
|
||||
|
||||
@@ -1468,7 +1520,6 @@ pub const CodeGen = struct {
|
||||
.type_param_types = try tp_types.toOwnedSlice(self.allocator),
|
||||
.template_name = template_name,
|
||||
};
|
||||
try self.struct_types.put(mangled_name, si);
|
||||
try self.type_registry.put(mangled_name, .{ .struct_info = si });
|
||||
_ = try self.getAnyTypeId(mangled_name, .{ .struct_type = mangled_name });
|
||||
|
||||
@@ -1497,7 +1548,7 @@ pub const CodeGen = struct {
|
||||
|
||||
// Try struct first
|
||||
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 self.registerInstantiatedStruct(mangled_name, alias_name, struct_decl);
|
||||
@@ -1505,7 +1556,7 @@ pub const CodeGen = struct {
|
||||
|
||||
// Try union
|
||||
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 self.registerInstantiatedTaggedEnum(mangled_name, union_decl);
|
||||
@@ -1527,7 +1578,6 @@ pub const CodeGen = struct {
|
||||
.llvm_type = build.llvm_type,
|
||||
.display_name = display_name,
|
||||
};
|
||||
try self.struct_types.put(mangled_name, si2);
|
||||
try self.type_registry.put(mangled_name, .{ .struct_info = si2 });
|
||||
_ = try self.getAnyTypeId(mangled_name, .{ .struct_type = mangled_name });
|
||||
|
||||
@@ -1544,7 +1594,6 @@ pub const CodeGen = struct {
|
||||
.max_payload_size = build.max_payload_size,
|
||||
.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.getAnyTypeId(mangled_name, .{ .union_type = mangled_name });
|
||||
|
||||
@@ -1652,7 +1701,7 @@ pub const CodeGen = struct {
|
||||
if (!sx_ty.isStruct()) return self.typeToLLVM(sx_ty);
|
||||
|
||||
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()) {
|
||||
return self.aarch64ParamABI(info);
|
||||
@@ -1899,11 +1948,9 @@ pub const CodeGen = struct {
|
||||
// Tagged enum with payloads
|
||||
try self.registerTaggedEnum(ed);
|
||||
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 });
|
||||
} else {
|
||||
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.getAnyTypeId(qualified, .{ .enum_type = qualified });
|
||||
if (ed.backing_type) |bt_node| {
|
||||
@@ -1916,13 +1963,11 @@ pub const CodeGen = struct {
|
||||
try self.registerStructType(sd);
|
||||
// Register qualified alias so rl.Color resolves to Color
|
||||
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 });
|
||||
},
|
||||
.union_decl => |ud| {
|
||||
try self.registerUnionType(ud);
|
||||
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 });
|
||||
},
|
||||
.const_decl => |cd| {
|
||||
@@ -1933,7 +1978,6 @@ pub const CodeGen = struct {
|
||||
try self.registerLambdaAsFunction(qualified, cd.value.data.lambda);
|
||||
} else if (cd.value.data == .type_expr) {
|
||||
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 });
|
||||
}
|
||||
},
|
||||
@@ -2019,8 +2063,11 @@ pub const CodeGen = struct {
|
||||
const name_ptr = c.LLVMGetStructName(llvm_ty);
|
||||
if (name_ptr != null) {
|
||||
const name = std.mem.span(name_ptr);
|
||||
if (self.struct_types.contains(name)) return .{ .struct_type = name };
|
||||
if (self.tagged_enum_types.contains(name)) return .{ .union_type = name };
|
||||
if (self.type_registry.get(name)) |e| switch (e) {
|
||||
.struct_info => return .{ .struct_type = name },
|
||||
.tagged_enum => return .{ .union_type = name },
|
||||
else => {},
|
||||
};
|
||||
}
|
||||
}
|
||||
// Check for array types
|
||||
@@ -2277,11 +2324,29 @@ pub const CodeGen = struct {
|
||||
const saved_named = self.named_values;
|
||||
self.named_values = std.StringHashMap(NamedValue).init(self.allocator);
|
||||
|
||||
// Register with correct types (null return_type = void)
|
||||
try self.registerFnDecl(fd, fd.name);
|
||||
// Infer return type from body for => lambdas without explicit annotation
|
||||
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
|
||||
const ret_sx_type = self.resolveType(fd.return_type);
|
||||
// For arrow lambdas with inferred return type, build function manually
|
||||
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;
|
||||
const name_z = try self.allocator.dupeZ(u8, fd.name);
|
||||
const function = c.LLVMGetNamedFunction(self.module, name_z.ptr) orelse
|
||||
@@ -2460,7 +2525,7 @@ pub const CodeGen = struct {
|
||||
sx_ty = .{ .union_type = uname };
|
||||
|
||||
// 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);
|
||||
|
||||
if (vd.value == null) {
|
||||
@@ -2652,6 +2717,25 @@ pub const CodeGen = struct {
|
||||
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);
|
||||
|
||||
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 init_val = if (cd.value.data == .enum_literal and enum_name != null)
|
||||
self.genEnumLiteral(cd.value.data.enum_literal.name, enum_name.?)
|
||||
else if (cd.type_annotation != null)
|
||||
try self.genExprAsType(cd.value, sx_ty)
|
||||
else
|
||||
try self.genExpr(cd.value);
|
||||
|
||||
@@ -2745,10 +2831,10 @@ pub const CodeGen = struct {
|
||||
// Target must be an identifier
|
||||
if (asgn.target.data != .identifier) return self.emitError("assignment target must be a variable");
|
||||
const name = asgn.target.data.identifier.name;
|
||||
const entry = self.named_values.get(name) orelse
|
||||
self.global_mutable_vars.get(name) orelse {
|
||||
const lookup = self.lookupValue(name) orelse
|
||||
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
|
||||
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()
|
||||
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);
|
||||
// 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);
|
||||
@@ -2840,7 +2926,7 @@ pub const CodeGen = struct {
|
||||
// C-style union field assignment
|
||||
if (entry.ty.isUnion()) {
|
||||
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| {
|
||||
const field_ty = info.field_types[fidx];
|
||||
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)));
|
||||
},
|
||||
.identifier => |ident| {
|
||||
if (self.named_values.get(ident.name)) |entry| {
|
||||
const llvm_ty = self.typeToLLVM(entry.ty);
|
||||
return c.LLVMBuildLoad2(self.builder, llvm_ty, entry.ptr, "loadtmp");
|
||||
}
|
||||
// Fall back to comptime globals (lazy resolution)
|
||||
if (self.comptime_globals.getPtr(ident.name)) |ct| {
|
||||
if (!ct.is_resolved) {
|
||||
try self.resolveComptimeGlobal(ct);
|
||||
if (self.lookupValue(ident.name)) |v| {
|
||||
switch (v) {
|
||||
.local => |nv| return c.LLVMBuildLoad2(self.builder, self.typeToLLVM(nv.ty), nv.ptr, "loadtmp"),
|
||||
.comptime_global => |ct| {
|
||||
if (!ct.is_resolved) try self.resolveComptimeGlobal(ct);
|
||||
return c.LLVMBuildLoad2(self.builder, self.typeToLLVM(ct.ty), ct.global, "ct_load");
|
||||
},
|
||||
.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
|
||||
{
|
||||
@@ -3010,7 +3089,7 @@ pub const CodeGen = struct {
|
||||
if (result_type.isUnion() and (binop.op == .eq or binop.op == .neq)) {
|
||||
const uname = result_type.union_type;
|
||||
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);
|
||||
|
||||
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
|
||||
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) {
|
||||
return entry.ptr;
|
||||
}
|
||||
@@ -3237,7 +3316,7 @@ pub const CodeGen = struct {
|
||||
// &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| {
|
||||
if (self.lookupStructInfo(pointee_name)) |info| {
|
||||
const loaded_ptr = c.LLVMBuildLoad2(self.builder,
|
||||
self.ptrType(), entry.ptr, "ptr_load");
|
||||
const idx = try self.findFieldIndex(info.field_names, fa.field, pointee_name);
|
||||
@@ -3338,7 +3417,6 @@ pub const CodeGen = struct {
|
||||
hoisted.name = synthetic_name;
|
||||
try self.registerTaggedEnum(hoisted);
|
||||
} 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.getAnyTypeId(synthetic_name, .{ .enum_type = synthetic_name });
|
||||
if (inline_ed.backing_type) |bt_node| {
|
||||
@@ -3387,7 +3465,6 @@ pub const CodeGen = struct {
|
||||
.field_defaults = resolved_defaults,
|
||||
.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.getAnyTypeId(sd.name, .{ .struct_type = sd.name });
|
||||
}
|
||||
@@ -3424,7 +3501,6 @@ pub const CodeGen = struct {
|
||||
.max_payload_size = layout.payload_size,
|
||||
.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 });
|
||||
} else {
|
||||
// Primitive backing type (e.g. enum u32 { ... })
|
||||
@@ -3442,7 +3518,6 @@ pub const CodeGen = struct {
|
||||
.max_payload_size = build.max_payload_size,
|
||||
.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 });
|
||||
}
|
||||
|
||||
@@ -3500,7 +3575,7 @@ pub const CodeGen = struct {
|
||||
const resolved = self.resolveAlias(name);
|
||||
if (Type.fromName(resolved) != null) return null;
|
||||
// Must be a registered struct
|
||||
if (self.struct_types.contains(resolved)) {
|
||||
if (self.lookupStructInfo(resolved) != null) {
|
||||
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 {
|
||||
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 });
|
||||
};
|
||||
|
||||
@@ -3607,7 +3682,7 @@ pub const CodeGen = struct {
|
||||
// Check if this is an anonymous struct (name contains __anon_)
|
||||
const sname = fty.struct_type;
|
||||
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| {
|
||||
try promoted.put(sf_name, .{
|
||||
.struct_name = sname,
|
||||
@@ -3627,7 +3702,6 @@ pub const CodeGen = struct {
|
||||
.total_size = max_size,
|
||||
.promoted_fields = promoted,
|
||||
};
|
||||
try self.union_types.put(ud.name, uinfo);
|
||||
try self.type_registry.put(ud.name, .{ .union_info = uinfo });
|
||||
// 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.
|
||||
@@ -3901,7 +3975,7 @@ pub const CodeGen = struct {
|
||||
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
|
||||
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 alloca;
|
||||
@@ -3986,13 +4060,13 @@ pub const CodeGen = struct {
|
||||
const pointee_name = target_ty.pointer_type.pointee_name;
|
||||
const src_matches = if (src_ty.isStruct())
|
||||
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)
|
||||
(if (self.lookupAlias(src_ty.struct_type)) |alias| std.mem.eql(u8, alias, pointee_name) else false) or
|
||||
(if (self.lookupAlias(pointee_name)) |alias| std.mem.eql(u8, alias, src_ty.struct_type) else false)
|
||||
else if (src_ty.isUnion()) blk: {
|
||||
const uname = src_ty.union_type;
|
||||
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.type_aliases.get(pointee_name)) |alias| std.mem.eql(u8, alias, uname) else false);
|
||||
(if (self.lookupAlias(uname)) |alias| std.mem.eql(u8, alias, pointee_name) else false) or
|
||||
(if (self.lookupAlias(pointee_name)) |alias| std.mem.eql(u8, alias, uname) else false);
|
||||
} else if (Type.fromName(pointee_name)) |pointee_ty|
|
||||
src_ty.eql(pointee_ty)
|
||||
else
|
||||
@@ -4012,8 +4086,8 @@ pub const CodeGen = struct {
|
||||
// Struct literals return alloca pointers — load the value for by-value passing
|
||||
if (src_ty.isStruct() and target_ty.isStruct()) {
|
||||
if (c.LLVMGetTypeKind(c.LLVMTypeOf(val)) == c.LLVMPointerTypeKind) {
|
||||
const info = self.struct_types.get(src_ty.struct_type) orelse
|
||||
self.struct_types.get(self.resolveAlias(src_ty.struct_type));
|
||||
const info = self.lookupStructInfo(src_ty.struct_type) orelse
|
||||
self.lookupStructInfo(self.resolveAlias(src_ty.struct_type));
|
||||
if (info) |si| {
|
||||
val = c.LLVMBuildLoad2(self.builder, si.llvm_type, val, "struct_load");
|
||||
}
|
||||
@@ -4083,7 +4157,7 @@ pub const CodeGen = struct {
|
||||
}
|
||||
if (target_ty.isStruct()) {
|
||||
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");
|
||||
}
|
||||
}
|
||||
@@ -4095,7 +4169,7 @@ pub const CodeGen = struct {
|
||||
}
|
||||
if (target_ty.isUnion()) {
|
||||
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");
|
||||
}
|
||||
}
|
||||
@@ -4144,7 +4218,7 @@ pub const CodeGen = struct {
|
||||
// Union → int: extract the tag field (index 0)
|
||||
if (src_ty.isUnion() and target_ty.isInt()) {
|
||||
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_bits = c.LLVMGetIntTypeWidth(tag_llvm_ty);
|
||||
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);
|
||||
}
|
||||
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 c.LLVMConstInt(i64_ty, variants.len, 0);
|
||||
}
|
||||
@@ -4359,7 +4433,7 @@ pub const CodeGen = struct {
|
||||
const info = try self.getStructInfo(ty.struct_type);
|
||||
break :blk .{ info.field_names, ty.struct_type };
|
||||
} 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});
|
||||
break :blk .{ variants, ty.enum_type };
|
||||
} else if (ty.isUnion()) blk: {
|
||||
@@ -4558,7 +4632,7 @@ pub const CodeGen = struct {
|
||||
}
|
||||
const enum_name = ty.enum_type;
|
||||
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 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)");
|
||||
const ty = self.resolveType(call_node.args[0]);
|
||||
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()) {
|
||||
_ = try self.genExpr(call_node.args[1]);
|
||||
return c.LLVMConstInt(i64_type, 0, 0);
|
||||
@@ -4598,7 +4723,7 @@ pub const CodeGen = struct {
|
||||
return c.LLVMConstInt(i64_type, 0, 0);
|
||||
}
|
||||
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 val = try self.genExpr(call_node.args[1]);
|
||||
@@ -4736,7 +4861,7 @@ pub const CodeGen = struct {
|
||||
if (entry.ty.isUnion()) {
|
||||
const uname = entry.ty.union_type;
|
||||
// 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| {
|
||||
const field_ty = info.field_types[fidx];
|
||||
return self.loadTyped(field_ty, entry.ptr, "union_field");
|
||||
@@ -5059,8 +5184,11 @@ pub const CodeGen = struct {
|
||||
const resolved_type: ?Type = blk: {
|
||||
if (fa.object.data == .identifier) {
|
||||
const name = self.resolveAlias(fa.object.data.identifier.name);
|
||||
if (self.tagged_enum_types.contains(name)) break :blk .{ .union_type = name };
|
||||
if (self.struct_types.contains(name)) break :blk .{ .struct_type = name };
|
||||
if (self.type_registry.get(name)) |e| switch (e) {
|
||||
.tagged_enum => break :blk Type{ .union_type = name },
|
||||
.struct_info => break :blk Type{ .struct_type = name },
|
||||
else => {},
|
||||
};
|
||||
} else {
|
||||
const ty = self.resolveType(fa.object);
|
||||
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
|
||||
if (callee_fn == null) {
|
||||
const fp_entry = if (self.named_values.get(callee_name)) |e| e
|
||||
else self.global_mutable_vars.get(callee_name);
|
||||
if (fp_entry) |entry| {
|
||||
if (entry.ty.isFunctionType()) {
|
||||
return self.genIndirectCall(entry, call_node);
|
||||
if (self.lookupValue(callee_name)) |v| {
|
||||
const entry = v.asNamedValue();
|
||||
if (entry) |e| {
|
||||
if (e.ty.isFunctionType()) {
|
||||
return self.genIndirectCall(e, call_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
if (self.named_values.contains(name) and
|
||||
Type.fromName(name) == null and
|
||||
!self.struct_types.contains(name) and
|
||||
!self.enum_types.contains(name) and
|
||||
!self.tagged_enum_types.contains(name) and
|
||||
!self.type_aliases.contains(name))
|
||||
!self.type_registry.contains(name))
|
||||
{
|
||||
return self.genGenericCallWithRuntimeDispatch(template, call_node, match_tags);
|
||||
}
|
||||
@@ -5492,7 +5618,7 @@ pub const CodeGen = struct {
|
||||
arg_ty.struct_type
|
||||
else
|
||||
"";
|
||||
if (self.struct_types.get(struct_name)) |info| {
|
||||
if (self.lookupStructInfo(struct_name)) |info| {
|
||||
if (info.template_name) |tmpl_name| {
|
||||
if (std.mem.eql(u8, tmpl_name, pte.name)) {
|
||||
// Match generic args against stored type param bindings
|
||||
@@ -6183,6 +6309,7 @@ pub const CodeGen = struct {
|
||||
// Create basic blocks
|
||||
const cond_bb = self.appendBB("for.cond");
|
||||
const body_bb = self.appendBB("for.body");
|
||||
const incr_bb = self.appendBB("for.incr");
|
||||
const after_bb = self.appendBB("for.after");
|
||||
|
||||
self.br(cond_bb);
|
||||
@@ -6216,22 +6343,26 @@ pub const CodeGen = struct {
|
||||
const saved_break_bb = self.loop_break_bb;
|
||||
const saved_continue_bb = self.loop_continue_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);
|
||||
|
||||
self.loop_break_bb = saved_break_bb;
|
||||
self.loop_continue_bb = saved_continue_bb;
|
||||
|
||||
// Increment it_index
|
||||
// Fall through to increment block
|
||||
const current_bb = self.getCurrentBlock();
|
||||
if (c.LLVMGetBasicBlockTerminator(current_bb) == null) {
|
||||
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.br(incr_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);
|
||||
|
||||
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 {
|
||||
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);
|
||||
for (variants, 0..) |v, i| {
|
||||
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: {
|
||||
// Union: load tag from field 0 of the alloca
|
||||
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.?));
|
||||
} else try self.genExpr(match.subject);
|
||||
|
||||
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|
|
||||
self.enum_types.get(en)
|
||||
self.lookupEnumVariants(en)
|
||||
else
|
||||
null;
|
||||
|
||||
@@ -6372,7 +6503,7 @@ pub const CodeGen = struct {
|
||||
// Payload capture: bind variant payload as a local variable
|
||||
if (arm.capture) |cap_name| {
|
||||
if (union_name) |un| {
|
||||
const uinfo = self.tagged_enum_types.get(un).?;
|
||||
const uinfo = self.lookupTaggedEnumInfo(un).?;
|
||||
const pat = arm.pattern.?;
|
||||
if (pat.data == .enum_literal) {
|
||||
const vname = pat.data.enum_literal.name;
|
||||
@@ -6515,14 +6646,13 @@ pub const CodeGen = struct {
|
||||
return tags;
|
||||
}
|
||||
// Named type (struct/enum/union) — get dynamic ID
|
||||
const sx_type: Type = if (self.struct_types.contains(name))
|
||||
.{ .struct_type = name }
|
||||
else if (self.enum_types.contains(name))
|
||||
.{ .enum_type = name }
|
||||
else if (self.tagged_enum_types.contains(name))
|
||||
.{ .union_type = name }
|
||||
else
|
||||
.{ .struct_type = name }; // fallback
|
||||
const sx_type: Type = if (self.type_registry.get(name)) |e| switch (e) {
|
||||
.struct_info => Type{ .struct_type = name },
|
||||
.plain_enum => Type{ .enum_type = name },
|
||||
.tagged_enum => Type{ .union_type = name },
|
||||
.union_info => Type{ .union_type = name },
|
||||
.alias => Type{ .struct_type = name },
|
||||
} else .{ .struct_type = name }; // fallback
|
||||
const id = try self.getAnyTypeId(name, sx_type);
|
||||
const tags = try self.allocator.alloc(u64, 1);
|
||||
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.
|
||||
fn resolveTypeName(self: *CodeGen, name: []const u8) ?[]const u8 {
|
||||
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;
|
||||
return null;
|
||||
}
|
||||
@@ -6752,7 +6882,7 @@ pub const CodeGen = struct {
|
||||
}
|
||||
}
|
||||
// Type aliases
|
||||
if (self.type_aliases.get(name)) |target| return self.resolveTypeFromName(target);
|
||||
if (self.lookupAlias(name)) |target| return self.resolveTypeFromName(target);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -6776,9 +6906,7 @@ pub const CodeGen = struct {
|
||||
},
|
||||
.chained_comparison => return .boolean,
|
||||
.identifier => |ident| {
|
||||
if (self.named_values.get(ident.name)) |entry| return entry.ty;
|
||||
if (self.comptime_globals.get(ident.name)) |ct| return ct.ty;
|
||||
if (self.global_mutable_vars.get(ident.name)) |entry| return entry.ty;
|
||||
if (self.lookupValue(ident.name)) |v| return v.ty();
|
||||
return Type.s(64);
|
||||
},
|
||||
.if_expr => |ie| {
|
||||
@@ -6808,7 +6936,7 @@ pub const CodeGen = struct {
|
||||
const obj_ty = blk: {
|
||||
if (fa.object.data == .identifier) {
|
||||
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);
|
||||
if (ty.isUnion()) break :blk ty;
|
||||
@@ -6921,11 +7049,9 @@ pub const CodeGen = struct {
|
||||
}
|
||||
// Check if callee is a variable with function pointer type
|
||||
{
|
||||
const fp_entry = if (self.named_values.get(callee_name)) |e| e
|
||||
else self.global_mutable_vars.get(callee_name);
|
||||
if (fp_entry) |entry| {
|
||||
if (entry.ty.isFunctionType()) {
|
||||
return entry.ty.function_type.return_type.*;
|
||||
if (self.lookupValue(callee_name)) |v| {
|
||||
if (v.ty().isFunctionType()) {
|
||||
return v.ty().function_type.return_type.*;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6969,14 +7095,14 @@ pub const CodeGen = struct {
|
||||
return obj_ty.vectorElementType() orelse Type.s(64);
|
||||
}
|
||||
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| {
|
||||
return info.field_types[idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
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| {
|
||||
return info.field_types[idx];
|
||||
}
|
||||
@@ -6984,7 +7110,7 @@ pub const CodeGen = struct {
|
||||
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| {
|
||||
if (std.mem.eql(u8, vn, fa.field)) {
|
||||
return info.variant_types[i];
|
||||
|
||||
@@ -745,6 +745,7 @@ pub const Parser = struct {
|
||||
}
|
||||
|
||||
// Body: block `{ ... }`, arrow `=> expr;`, #builtin, or #foreign marker
|
||||
var is_arrow = false;
|
||||
const body = if (self.current.tag == .hash_builtin) blk: {
|
||||
const bi_start = self.current.loc.start;
|
||||
self.advance();
|
||||
@@ -756,6 +757,7 @@ pub const Parser = struct {
|
||||
try self.expect(.semicolon);
|
||||
break :blk try self.createNode(fi_start, .{ .foreign_expr = {} });
|
||||
} else if (self.current.tag == .fat_arrow) blk: {
|
||||
is_arrow = true;
|
||||
self.advance();
|
||||
const expr = try self.parseExpr();
|
||||
try self.expect(.semicolon);
|
||||
@@ -774,6 +776,7 @@ pub const Parser = struct {
|
||||
.return_type = return_type,
|
||||
.body = body,
|
||||
.type_params = type_params,
|
||||
.is_arrow = is_arrow,
|
||||
} });
|
||||
}
|
||||
|
||||
|
||||
@@ -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}
|
||||
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})}
|
||||
|
||||
@@ -8,7 +8,7 @@ v2[1]: 3.000000
|
||||
scaled: [2.000000, 6.000000, 4.000000]
|
||||
neg: [-1.000000, -3.000000, -2.000000]
|
||||
sqrt(9): 3.000000
|
||||
.counter(0.500000)
|
||||
.user(0.500000)
|
||||
4
|
||||
16
|
||||
8
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
circle: .circle(3.140000)
|
||||
radius: 3.140000
|
||||
none: .circle
|
||||
rect: .circle(Shape.rect{w: 4.000000, h: 2.000000})
|
||||
none: .none
|
||||
rect: .rect(Shape.rect{w: 4.000000, h: 2.000000})
|
||||
sh: .circle(2.710000)
|
||||
rect val: Shape.rect{w: 2.000000, h: 4.000000}
|
||||
matched rect
|
||||
|
||||
1
tests/expected/50-smoke.exit
Normal file
1
tests/expected/50-smoke.exit
Normal file
@@ -0,0 +1 @@
|
||||
0
|
||||
148
tests/expected/50-smoke.txt
Normal file
148
tests/expected/50-smoke.txt
Normal 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 ===
|
||||
Reference in New Issue
Block a user