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,
|
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 {
|
||||||
|
|||||||
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
|
// 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];
|
||||||
|
|||||||
@@ -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,
|
||||||
} });
|
} });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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})}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
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