test: group examples into per-category folders
Move examples/*.sx and their expected/ snapshots into per-category subfolders (examples/<category>/...). Folder = leading filename token, with ffi-objc/ffi-jni kept whole; filenames are unchanged. The corpus runner and LSP sweep now discover each category's expected/ dir, while issues/ stays flat. Example 1058's repo-root-relative companion import is made file-relative. Path strings embedded in 164 snapshots were regenerated (path-only changes). Test-layout docs in CLAUDE.md updated.
This commit is contained in:
17
examples/generics/0200-generics-generic.sx
Normal file
17
examples/generics/0200-generics-generic.sx
Normal file
@@ -0,0 +1,17 @@
|
||||
#import "modules/std.sx";
|
||||
sum :: (a:$T, b:T) -> T {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
main :: () {
|
||||
x:=sum(2,3);
|
||||
|
||||
print("sum: {}\n", x);
|
||||
print("sum: {}\n", sum(40,2));
|
||||
print("sum: {}\n", sum(40,2.5));
|
||||
}
|
||||
|
||||
// ** stdout **
|
||||
// sum: 42
|
||||
// sum: 42.500000
|
||||
//
|
||||
87
examples/generics/0201-generics-generic-struct.sx
Normal file
87
examples/generics/0201-generics-generic-struct.sx
Normal file
@@ -0,0 +1,87 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math";
|
||||
|
||||
Vec :: struct($N: u32, $T:Type) {
|
||||
// <N x T> (LLVM Vector)
|
||||
// Vector is a Builtin Type
|
||||
data: Vector(N,T);
|
||||
}
|
||||
|
||||
Complex :: ($T:Type) -> Type {
|
||||
return struct {
|
||||
value: T;
|
||||
//..inject
|
||||
count: u32;
|
||||
};
|
||||
}
|
||||
|
||||
Vec3 :: Vec(3, f32);
|
||||
|
||||
vec3 :: (x:f32, y:f32, z:f32) -> Vector(3,f32) {
|
||||
.[x, y, z]
|
||||
}
|
||||
|
||||
Foo :: Complex(u32);
|
||||
|
||||
main :: () {
|
||||
v1 := Vec3.{data = .[1,3,2]};
|
||||
print("v1: {}\n", v1);
|
||||
//stdout: Vec(3,f32){data: [1.0, 3.0, 2.0]}
|
||||
//
|
||||
|
||||
v2 := vec3(1,3,2);
|
||||
print("v2: {}\n", v2);
|
||||
//stdout: [1.0, 3.0, 2.0]
|
||||
//
|
||||
|
||||
// [N x T] (LLVM Array)
|
||||
buffer : [5]f32 = .[0, 2, 3.5, 4, 0];
|
||||
print("buff: {}\n", buffer);
|
||||
//stdout: [0.0, 2.0, 3.5, 4.0, 0.0]
|
||||
//
|
||||
|
||||
comp : Foo = .{value = 42, count = 1};
|
||||
print("comp: {}\n", comp);
|
||||
//stdout: Foo{value: 42, count: 1}
|
||||
//
|
||||
|
||||
// Vector arithmetic
|
||||
v3 := vec3(3,2,1);
|
||||
add := v2 + v3;
|
||||
print("add: {}\n", add);
|
||||
|
||||
// Element access
|
||||
v2x := v2.x;
|
||||
print("v2.x: {}\n", v2x);
|
||||
|
||||
// Index access
|
||||
v2i := v2[1];
|
||||
print("v2[1]: {}\n", v2i);
|
||||
|
||||
// Scalar broadcast
|
||||
scaled := v2 * 2.0;
|
||||
print("scaled: {}\n", scaled);
|
||||
|
||||
// Negation
|
||||
neg := -v2;
|
||||
print("neg: {}\n", neg);
|
||||
|
||||
// sqrt
|
||||
s := sqrt(9.0);
|
||||
print("sqrt(9): {}\n", s);
|
||||
|
||||
// inline generic type
|
||||
Sx :: (user: $T) -> Type {
|
||||
return enum {
|
||||
counter: i32;
|
||||
user: T;
|
||||
};
|
||||
}
|
||||
|
||||
sx := Sx(f32).user(0.5);
|
||||
print("{}\n", sx);
|
||||
|
||||
print("{}\n", size_of(f32));
|
||||
print("{}\n", size_of(Sx(f32)));
|
||||
print("{}\n", size_of(Foo));
|
||||
}
|
||||
11
examples/generics/0202-generics-anytype.sx
Normal file
11
examples/generics/0202-generics-anytype.sx
Normal file
@@ -0,0 +1,11 @@
|
||||
#import "modules/std.sx";
|
||||
|
||||
main :: () {
|
||||
i := 0;
|
||||
while i < 10 {
|
||||
i+=1;
|
||||
if i == 2 then continue;
|
||||
if i == 5 then break;
|
||||
}
|
||||
print("{}\n", i);
|
||||
}
|
||||
16
examples/generics/0203-generics-infer-return-type.sx
Normal file
16
examples/generics/0203-generics-infer-return-type.sx
Normal file
@@ -0,0 +1,16 @@
|
||||
// Functions without an explicit return type infer the type from the first
|
||||
// `return <value>;` statement in the body, so `foo :: () { return 42; }` is
|
||||
// usable from a typed context. No explicit-value return → `.void` default.
|
||||
|
||||
foo :: () { return 42; }
|
||||
bar :: () { return foo() * 2; }
|
||||
nested :: () {
|
||||
if true {
|
||||
return foo() + 10;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
main :: () -> i32 {
|
||||
return xx (foo() + bar() + nested());
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
// Generic struct `Animated($T: Lerpable)` monomorphized with a struct type — the
|
||||
// `#inline` protocol constraint participates in method dispatch via `self.from.lerp(...)`.
|
||||
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math";
|
||||
|
||||
Lerpable :: protocol #inline {
|
||||
lerp :: (self: *Self, b: Self, t: f32) -> Self;
|
||||
}
|
||||
|
||||
Size :: struct {
|
||||
width, height: f32;
|
||||
zero :: () -> Size => .{ width = 0.0, height = 0.0 };
|
||||
}
|
||||
|
||||
impl Lerpable for Size {
|
||||
lerp :: (self: Size, b: Size, t: f32) -> Size {
|
||||
Size.{ width = self.width + (b.width - self.width) * t,
|
||||
height = self.height + (b.height - self.height) * t }
|
||||
}
|
||||
}
|
||||
|
||||
Animated :: struct ($T: Lerpable) {
|
||||
current: T;
|
||||
from: T;
|
||||
to: T;
|
||||
elapsed: f32;
|
||||
duration: f32;
|
||||
active: bool;
|
||||
|
||||
make :: (value: T) -> Animated(T) {
|
||||
Animated(T).{
|
||||
current = value, from = value, to = value,
|
||||
elapsed = 0.0, duration = 0.0, active = false
|
||||
}
|
||||
}
|
||||
|
||||
set_immediate :: (self: *Animated(T), value: T) {
|
||||
self.current = value;
|
||||
self.from = value;
|
||||
self.to = value;
|
||||
self.active = false;
|
||||
}
|
||||
|
||||
animate_to :: (self: *Animated(T), target: T, dur: f32) {
|
||||
self.from = self.current;
|
||||
self.to = target;
|
||||
self.elapsed = 0.0;
|
||||
self.duration = dur;
|
||||
self.active = true;
|
||||
}
|
||||
|
||||
tick :: (self: *Animated(T), dt: f32) {
|
||||
if !self.active { return; }
|
||||
self.elapsed += dt;
|
||||
t := clamp(self.elapsed / self.duration, 0.0, 1.0);
|
||||
self.current = self.from.lerp(self.to, t);
|
||||
if t >= 1.0 {
|
||||
self.current = self.to;
|
||||
self.active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main :: () -> void {
|
||||
anim := Animated(Size).make(Size.zero());
|
||||
anim.set_immediate(Size.{ width = 100.0, height = 50.0 });
|
||||
print("after set: {}x{}\n", anim.current.width, anim.current.height);
|
||||
|
||||
anim.animate_to(Size.{ width = 200.0, height = 100.0 }, 1.0);
|
||||
anim.tick(0.5);
|
||||
print("mid anim: {}x{}\n", anim.current.width, anim.current.height);
|
||||
|
||||
anim.tick(0.5);
|
||||
print("end anim: {}x{}\n", anim.current.width, anim.current.height);
|
||||
}
|
||||
28
examples/generics/0205-generics-generic-method-dot-call.sx
Normal file
28
examples/generics/0205-generics-generic-method-dot-call.sx
Normal file
@@ -0,0 +1,28 @@
|
||||
// Dot-call dispatch for generic struct methods.
|
||||
//
|
||||
// Covers three shapes:
|
||||
// 1. non-generic method: h.plain()
|
||||
// 2. generic method, explicit type arg: h.sized(i32)
|
||||
// 3. generic method, inferred from val: h.taking(99)
|
||||
|
||||
#import "modules/std.sx";
|
||||
|
||||
Holder :: struct {
|
||||
n: i64;
|
||||
|
||||
plain :: (self: *Holder) -> i64 { self.n }
|
||||
sized :: (self: *Holder, $T: Type) -> i64 { size_of(T) }
|
||||
taking :: (self: *Holder, $T: Type, v: T) -> T { v }
|
||||
}
|
||||
|
||||
main :: () -> i32 {
|
||||
h : *Holder = xx libc_malloc(size_of(Holder));
|
||||
h.n = 7;
|
||||
|
||||
print("plain: {}\n", h.plain());
|
||||
print("sized i32: {}\n", h.sized(i32));
|
||||
print("sized i64: {}\n", h.sized(i64));
|
||||
print("taking explicit: {}\n", h.taking(i32, 42));
|
||||
print("taking inferred: {}\n", h.taking(99));
|
||||
0
|
||||
}
|
||||
37
examples/generics/0206-generics-generic-into-block.sx
Normal file
37
examples/generics/0206-generics-generic-into-block.sx
Normal file
@@ -0,0 +1,37 @@
|
||||
// FFI plan step 5.2 — generic `Into(Block) for Closure(..$args) ->
|
||||
// $R` impl. One impl in stdlib covers every closure shape; the
|
||||
// compiler monomorphises the impl body per call shape and emits a
|
||||
// dedicated `__invoke` `abi(.c)` trampoline + Block literal
|
||||
// (via `#insert build_block_convert($args, $R);`).
|
||||
//
|
||||
// This test exercises a closure shape (`Closure(i64, i64) -> void`)
|
||||
// that has NO hand-rolled `Into(Block)` impl in
|
||||
// `library/modules/ffi/objc_block.sx`. Before step 5.2 lands,
|
||||
// `xx cl : Block` errors out with the "no Into(Block) for
|
||||
// cl_i64_i64__void" focused diagnostic. After the generic impl
|
||||
// lands, the same call resolves through the pack-shaped impl and
|
||||
// the per-shape trampoline ferries control back to the sx closure.
|
||||
//
|
||||
// The block is invoked directly through `b.invoke` (a typed
|
||||
// `abi(.c)` fn-pointer) — the same shape the Apple Block
|
||||
// runtime calls when a UIKit/Foundation API hands the block back
|
||||
// to its registered invoke.
|
||||
|
||||
#import "modules/std.sx";
|
||||
#import "modules/ffi/objc_block.sx";
|
||||
|
||||
g_a: i64 = 0;
|
||||
g_b: i64 = 0;
|
||||
|
||||
main :: () -> i32 {
|
||||
cl := (a: i64, b: i64) => { g_a = a; g_b = b; };
|
||||
blk : Block = xx cl;
|
||||
|
||||
invoke_fn : (*Block, i64, i64) -> void abi(.c) = xx blk.invoke;
|
||||
invoke_fn(@blk, 10, 20);
|
||||
|
||||
if g_a != 10 { print("FAIL: g_a={}\n", g_a); return 1; }
|
||||
if g_b != 20 { print("FAIL: g_b={}\n", g_b); return 1; }
|
||||
print("generic-into-block ok: a={} b={}\n", g_a, g_b);
|
||||
0
|
||||
}
|
||||
29
examples/generics/0207-generics-value-param-const.sx
Normal file
29
examples/generics/0207-generics-value-param-const.sx
Normal file
@@ -0,0 +1,29 @@
|
||||
// A generic value parameter (`$K: u32`) bound from a named const or a
|
||||
// constant-foldable expression resolves to the SAME monomorphised instantiation
|
||||
// as the literal form: `Vec(N, f32)` (N a module const) and `Vec(M + 1, f32)`
|
||||
// (a const expression) are both `Vec(3, f32)`. The struct-copy assignment is the
|
||||
// proof — it type-checks only because the two spellings name one instantiation.
|
||||
//
|
||||
// Regression (issue 0083): the value-param binder hand-rolled an `else => 0`
|
||||
// switch, so a named-const value arg either fabricated a 0 binding under a wrong
|
||||
// mangled name or was rejected outright as "unknown type 'N'". It now folds
|
||||
// through the shared const-int evaluator (`program_index.evalConstIntExpr`).
|
||||
#import "modules/std.sx";
|
||||
|
||||
N :: 3;
|
||||
M :: 2;
|
||||
|
||||
Vec :: struct ($K: u32, $T: Type) { data: [K]T; }
|
||||
|
||||
main :: () {
|
||||
a : Vec(N, f32) = ---; // named-const value param
|
||||
a.data[0] = 10.0; a.data[1] = 20.0; a.data[2] = 30.0;
|
||||
print("named: len={} a0={} a2={}\n", a.data.len, a.data[0], a.data[2]);
|
||||
|
||||
e : Vec(M + 1, f32) = ---; // const-expr value param (M + 1 == 3)
|
||||
e.data[0] = 1.0; e.data[2] = 9.0;
|
||||
print("expr: len={} e2={}\n", e.data.len, e.data[2]);
|
||||
|
||||
b : Vec(3, f32) = a; // same instantiation → struct copy type-checks
|
||||
print("copy: len={} b2={}\n", b.data.len, b.data[2]);
|
||||
}
|
||||
32
examples/generics/0208-generics-value-param-type-function.sx
Normal file
32
examples/generics/0208-generics-value-param-type-function.sx
Normal file
@@ -0,0 +1,32 @@
|
||||
// A type-RETURNING function with a value parameter (`$K: u32`) used as a TYPE
|
||||
// annotation: `b : Make(N, i64)` where `Make :: ($K, $T) -> Type { return [K]T; }`.
|
||||
// A named-const value arg (`Make(N, i64)`), a const-expression value arg
|
||||
// (`Make(M + 1, i64)`), and the literal form (`Make(3, i64)`) all instantiate to
|
||||
// the SAME type — the array copy `b : Make(3, i64) = a` type-checks only because
|
||||
// the three spellings name one `[3]i64`.
|
||||
//
|
||||
// Regression (issue 0083 / F0.4 attempt 6): the unknown-type checker walked the
|
||||
// value-param position as a type name ("unknown type 'N'"), and the
|
||||
// parameterized-type-annotation path never routed to `instantiateTypeFunction`,
|
||||
// nor did that binder resolve a non-struct/union return shape (`return [K]T`).
|
||||
// The value arg now folds through the shared const-int evaluator and the type
|
||||
// function resolves its general return-type expression with bindings active.
|
||||
#import "modules/std.sx";
|
||||
|
||||
N :: 3;
|
||||
M :: 2;
|
||||
|
||||
Make :: ($K: u32, $T: Type) -> Type { return [K]T; }
|
||||
|
||||
main :: () {
|
||||
a : Make(N, i64) = ---; // named-const value param
|
||||
a[0] = 10; a[1] = 20; a[2] = 30;
|
||||
print("named: len={} a0={} a2={}\n", a.len, a[0], a[2]);
|
||||
|
||||
e : Make(M + 1, i64) = ---; // const-expr value param (M + 1 == 3)
|
||||
e[0] = 1; e[2] = 9;
|
||||
print("expr: len={} e2={}\n", e.len, e[2]);
|
||||
|
||||
b : Make(3, i64) = a; // same instantiation → array copy type-checks
|
||||
print("copy: len={} b2={}\n", b.len, b[2]);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// A generic value parameter (`$K: u32`) binds a literal (`Vec(3, i64)`) and an
|
||||
// integral-float named const (`Vec(L, i64)` with `L : f64 : 4.0`) to the same
|
||||
// integer a plain `4` would — the value-param arg folds through the shared
|
||||
// const-int evaluator, so the integral-float rule (F0.4 attempt 8, Agra ruling)
|
||||
// reaches value params too. The folded value is the array length `[K]i64`.
|
||||
//
|
||||
// The bind is range-checked against the declared `u32` (an out-of-range arg is a
|
||||
// clean compile error — see 1134); a valid in-range value binds normally.
|
||||
#import "modules/std.sx";
|
||||
|
||||
Vec :: struct ($K: u32, $T: Type) { data: [K]T; }
|
||||
|
||||
L : f64 : 4.0;
|
||||
|
||||
main :: () {
|
||||
a : Vec(3, i64) = ---; // literal value param
|
||||
b : Vec(L, i64) = ---; // integral-float named-const value param → 4
|
||||
print("a.len={} b.len={}\n", a.data.len, b.data.len); // 3 and 4
|
||||
}
|
||||
31
examples/generics/0210-generics-resolver-legacy-paths.sx
Normal file
31
examples/generics/0210-generics-resolver-legacy-paths.sx
Normal file
@@ -0,0 +1,31 @@
|
||||
// Resolver E1 lock: the bare-type-leaf cutover to the source-aware
|
||||
// `selectNominalLeaf` must NOT touch the NON-leaf type heads. A generic-struct
|
||||
// instantiation (`Box(i32)`), a `Vector(N, T)` builtin, and a type-returning
|
||||
// function (`Make(3, i64)`) are all resolved by `resolveTypeWithBindings`
|
||||
// ABOVE the bare-name leaf switch (`resolveParameterizedWithBindings` /
|
||||
// `resolveTypeCallWithBindings` / the `Vector` builtin path), so they stay on
|
||||
// the legacy resolution and never reach `selectNominalLeaf`. Parameterized
|
||||
// protocols share the same `resolveParameterizedWithBindings` pre-leaf path
|
||||
// (covered by 0204/0206). This example pins that all three still resolve
|
||||
// identically after the cutover.
|
||||
#import "modules/std.sx";
|
||||
|
||||
Box :: struct($T: Type) {
|
||||
value: T;
|
||||
}
|
||||
|
||||
Make :: ($K: u32, $T: Type) -> Type { return [K]T; }
|
||||
|
||||
N :: 3;
|
||||
|
||||
main :: () {
|
||||
b : Box(i32) = .{ value = 42 };
|
||||
print("box: {}\n", b.value);
|
||||
|
||||
v : Vector(4, f32) = .[1, 2, 3, 4];
|
||||
print("vec: {} {}\n", v.x, v.w);
|
||||
|
||||
a : Make(N, i64) = ---;
|
||||
a[0] = 10; a[2] = 30;
|
||||
print("typefn: len={} a0={} a2={}\n", a.len, a[0], a[2]);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// Companion of 0211: the re-export facade — own alias decls over another
|
||||
// module's members. Flat importers of THIS file see the aliases bare.
|
||||
#import "modules/std.sx";
|
||||
|
||||
r :: #import "0211-generics-struct-alias-head-rich.sx";
|
||||
|
||||
helper :: r.helper;
|
||||
Thing :: r.Thing;
|
||||
Box :: r.Box;
|
||||
15
examples/generics/0211-generics-struct-alias-head-rich.sx
Normal file
15
examples/generics/0211-generics-struct-alias-head-rich.sx
Normal file
@@ -0,0 +1,15 @@
|
||||
// Companion of 0211: the authoring module — a plain fn, a plain struct,
|
||||
// and a generic struct, all re-exported by -facade.sx via alias decls.
|
||||
#import "modules/std.sx";
|
||||
|
||||
helper :: () -> i64 { 7 }
|
||||
|
||||
Thing :: struct {
|
||||
v: i64;
|
||||
init :: () -> Thing { Thing.{ v = 42 } }
|
||||
}
|
||||
|
||||
Box :: struct ($T: Type) {
|
||||
item: T;
|
||||
get :: (b: *Box(T)) -> T { b.item }
|
||||
}
|
||||
43
examples/generics/0211-generics-struct-alias-head.sx
Normal file
43
examples/generics/0211-generics-struct-alias-head.sx
Normal file
@@ -0,0 +1,43 @@
|
||||
// Generic-struct head aliases: `BoxAlias :: Box;` binds the alias to the
|
||||
// SAME template — instantiation, methods, annotations, and alias chains all
|
||||
// resolve through it. Cross-module, a facade's `Box :: r.Box;` re-export is
|
||||
// the facade's OWN declaration, so it carries one flat-import level exactly
|
||||
// like a plain-struct alias (companion files: -rich.sx authors the decls,
|
||||
// -facade.sx re-exports them through a namespace alias).
|
||||
// Regression (issue 0120): the alias head used to lower silently to an
|
||||
// unresolved type and panic in the LLVM backend at instantiation.
|
||||
#import "modules/std.sx";
|
||||
#import "0211-generics-struct-alias-head-facade.sx";
|
||||
|
||||
LocalBox :: struct ($T: Type) {
|
||||
item: T;
|
||||
get :: (b: *LocalBox(T)) -> T { b.item }
|
||||
}
|
||||
|
||||
LocalAlias :: LocalBox;
|
||||
ChainAlias :: LocalAlias;
|
||||
|
||||
main :: () {
|
||||
// Same-file alias: instantiation + field + method.
|
||||
b := LocalAlias(i64).{ item = 3 };
|
||||
print("field: {}\n", b.item);
|
||||
print("method: {}\n", b.get());
|
||||
|
||||
// Alias chain terminates at the template.
|
||||
c := ChainAlias(i64).{ item = 11 };
|
||||
print("chain: {}\n", c.item);
|
||||
|
||||
// Alias as a type annotation head.
|
||||
a : LocalAlias(string) = .{ item = "ann" };
|
||||
print("annot: {}\n", a.item);
|
||||
|
||||
// Cross-module re-exports carried one flat hop from the facade:
|
||||
// plain fn, plain struct (static method), and the generic head.
|
||||
print("helper: {}\n", helper());
|
||||
t := Thing.init();
|
||||
print("thing: {}\n", t.v);
|
||||
f := Box(i64).{ item = 7 };
|
||||
print("facade: {}\n", f.get());
|
||||
x : Box(string) = .{ item = "qq" };
|
||||
print("facade-annot: {}\n", x.item);
|
||||
}
|
||||
41
examples/generics/0212-generics-array-arg-slice-param.sx
Normal file
41
examples/generics/0212-generics-array-arg-slice-param.sx
Normal file
@@ -0,0 +1,41 @@
|
||||
// An ARRAY argument at a generic slice param (`xs: []$T`) binds T from
|
||||
// the array's element type — the same array→slice promotion concrete
|
||||
// slice params perform — for scalar and struct elements, with the
|
||||
// return-position `T` resolving to the element type. The slice
|
||||
// spelling keeps working unchanged.
|
||||
//
|
||||
// Regression (issue 0126): the binding extractor only accepted slice
|
||||
// args, so `first(a)` left T unbound and the monomorphized body
|
||||
// reached LLVM emission with the `.unresolved` sentinel (panic).
|
||||
|
||||
#import "modules/std.sx";
|
||||
|
||||
P :: struct { x: i64; y: i64; }
|
||||
|
||||
first :: (xs: []$T) -> T {
|
||||
return xs[0];
|
||||
}
|
||||
|
||||
last :: (xs: []$T) -> T {
|
||||
return xs[xs.len - 1];
|
||||
}
|
||||
|
||||
main :: () -> i32 {
|
||||
a : [3]i64 = ---;
|
||||
a[0] = 7; a[1] = 8; a[2] = 9;
|
||||
print("{}\n", first(a));
|
||||
print("{}\n", last(a));
|
||||
|
||||
bs : [4]u8 = ---;
|
||||
bs[0] = 5; bs[1] = 6; bs[2] = 7; bs[3] = 8;
|
||||
print("{}\n", last(bs));
|
||||
|
||||
ps : [2]P = ---;
|
||||
ps[0] = .{ x = 1, y = 2 };
|
||||
ps[1] = .{ x = 3, y = 4 };
|
||||
print("{}\n", first(ps).y);
|
||||
|
||||
s : []i64 = a;
|
||||
print("{}\n", first(s));
|
||||
return 0;
|
||||
}
|
||||
13
examples/generics/0213-generics-namespaced-call-result.sx
Normal file
13
examples/generics/0213-generics-namespaced-call-result.sx
Normal file
@@ -0,0 +1,13 @@
|
||||
// A NAMESPACED call to a generic free function types its result from the
|
||||
// call's inferred bindings — not the unbound `T` stub (issue 0127:
|
||||
// `m.pick(3, 9)` boxed as `T{}` while the flat spelling printed `9`).
|
||||
#import "modules/std.sx";
|
||||
m :: #import "0213-generics-namespaced-call-result/m.sx";
|
||||
|
||||
main :: () {
|
||||
print("{}\n", m.pick(3, 9)); // i64 binding
|
||||
print("{}\n", m.pick(1.5, 0.25)); // f64 binding
|
||||
v := m.double(21);
|
||||
w : i64 = v + 0; // the concrete type flows onward
|
||||
print("{}\n", w);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#import "modules/std.sx";
|
||||
|
||||
pick :: (a: $T, b: T) -> T {
|
||||
if a > b then a else b
|
||||
}
|
||||
|
||||
double :: (x: $T) -> T {
|
||||
x + x
|
||||
}
|
||||
26
examples/generics/0214-generics-ufcs-closure-return-pack.sx
Normal file
26
examples/generics/0214-generics-ufcs-closure-return-pack.sx
Normal file
@@ -0,0 +1,26 @@
|
||||
// Generic inference where `$R` comes from a worker closure's RETURN type
|
||||
// through a variadic `..$args` pack — both the DIRECT spelling
|
||||
// `mymk(bx, worker, 40, 2)` and the UFCS dot-call `bx.mymk(worker, 40, 2)`
|
||||
// resolve `$R = i64` identically and build `Wrap($R)` correctly.
|
||||
// Regression (issue 0151): the UFCS path used to splice the receiver as
|
||||
// arg 0 without running the direct path's pack/closure-return binding, so
|
||||
// `$R` stayed `.unresolved` and SIGTRAPped at LLVM emission.
|
||||
#import "modules/std.sx";
|
||||
|
||||
Box :: struct { n: i64; }
|
||||
Wrap :: struct ($R: Type) { value: R; }
|
||||
|
||||
mymk :: ufcs (b: Box, worker: Closure(..$args) -> $R, ..$args) -> Wrap($R) {
|
||||
f : Wrap($R) = ---;
|
||||
f.value = worker(..args);
|
||||
return f;
|
||||
}
|
||||
|
||||
main :: () -> i32 {
|
||||
bx : Box = .{ n = 1 };
|
||||
direct := mymk(bx, (a: i64, b: i64) -> i64 => a + b, 40, 2);
|
||||
ufcs := bx.mymk((a: i64, b: i64) -> i64 => a + b, 40, 2);
|
||||
print("direct={}\n", direct.value);
|
||||
print("ufcs={}\n", ufcs.value);
|
||||
return 0;
|
||||
}
|
||||
29
examples/generics/0215-generics-infer-through-pointer.sx
Normal file
29
examples/generics/0215-generics-infer-through-pointer.sx
Normal file
@@ -0,0 +1,29 @@
|
||||
// Generic `$T` inferred through a generic-struct argument head — both
|
||||
// by-value (`Box($T)`) and pointer-wrapped (`*Box($T)`), the latter also
|
||||
// via a UFCS dot-call (auto-address-of receiver). Multi-param heads
|
||||
// (`Pair($A, $B)`) and nested heads (`Box(Box($T))`) bind positionally.
|
||||
// Regression (issue 0151, widened): `extractTypeParam` had no
|
||||
// `parameterized_type_expr` arm, so `$T` never bound from a generic-struct
|
||||
// param — the call failed with "cannot infer generic type parameter 'T'".
|
||||
#import "modules/std.sx";
|
||||
|
||||
Box :: struct ($T: Type) { v: T; }
|
||||
Pair :: struct ($A: Type, $B: Type) { a: A; b: B; }
|
||||
|
||||
unbox :: (b: *Box($T)) -> $T { return b.v; } // infer through `*`
|
||||
byval :: (b: Box($T)) -> $T { return b.v; } // infer through head
|
||||
second :: (p: Pair($A, $B)) -> $B { return p.b; } // 2nd of two params
|
||||
nested :: (b: Box(Box($T))) -> $T { return b.v.v; } // nested head
|
||||
get :: ufcs (b: *Box($T)) -> $T { return b.v; } // UFCS auto-ref
|
||||
|
||||
main :: () -> i32 {
|
||||
b : Box(i64) = .{ v = 42 };
|
||||
p : Pair(i64, f64) = .{ a = 1, b = 2.5 };
|
||||
nb : Box(Box(i64)) = .{ v = .{ v = 9 } };
|
||||
print("unbox={}\n", unbox(@b));
|
||||
print("byval={}\n", byval(b));
|
||||
print("second={}\n", second(p));
|
||||
print("nested={}\n", nested(nb));
|
||||
print("ufcs={}\n", b.get());
|
||||
return 0;
|
||||
}
|
||||
1
examples/generics/expected/0200-generics-generic.exit
Normal file
1
examples/generics/expected/0200-generics-generic.exit
Normal file
@@ -0,0 +1 @@
|
||||
0
|
||||
16033
examples/generics/expected/0200-generics-generic.ir
Normal file
16033
examples/generics/expected/0200-generics-generic.ir
Normal file
File diff suppressed because one or more lines are too long
1
examples/generics/expected/0200-generics-generic.stderr
Normal file
1
examples/generics/expected/0200-generics-generic.stderr
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
3
examples/generics/expected/0200-generics-generic.stdout
Normal file
3
examples/generics/expected/0200-generics-generic.stdout
Normal file
@@ -0,0 +1,3 @@
|
||||
sum: 5
|
||||
sum: 42
|
||||
sum: 42.500000
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
17783
examples/generics/expected/0201-generics-generic-struct.ir
Normal file
17783
examples/generics/expected/0201-generics-generic-struct.ir
Normal file
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
v1: Vec3{data: [1.000000, 3.000000, 2.000000]}
|
||||
v2: [1.000000, 3.000000, 2.000000]
|
||||
buff: [0.000000, 2.000000, 3.500000, 4.000000, 0.000000]
|
||||
comp: Foo{value: 42, count: 1}
|
||||
add: [4.000000, 5.000000, 3.000000]
|
||||
v2.x: 1.000000
|
||||
v2[1]: 3.000000
|
||||
scaled: [2.000000, 6.000000, 4.000000]
|
||||
neg: [-1.000000, -3.000000, -2.000000]
|
||||
sqrt(9): 3.000000
|
||||
.user(0.500000)
|
||||
4
|
||||
16
|
||||
8
|
||||
1
examples/generics/expected/0202-generics-anytype.exit
Normal file
1
examples/generics/expected/0202-generics-anytype.exit
Normal file
@@ -0,0 +1 @@
|
||||
0
|
||||
1
examples/generics/expected/0202-generics-anytype.stderr
Normal file
1
examples/generics/expected/0202-generics-anytype.stderr
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
1
examples/generics/expected/0202-generics-anytype.stdout
Normal file
1
examples/generics/expected/0202-generics-anytype.stdout
Normal file
@@ -0,0 +1 @@
|
||||
5
|
||||
@@ -0,0 +1 @@
|
||||
178
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
after set: 100.000000x50.000000
|
||||
mid anim: 150.000000x75.000000
|
||||
end anim: 200.000000x100.000000
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
plain: 7
|
||||
sized i32: 4
|
||||
sized i64: 8
|
||||
taking explicit: 42
|
||||
taking inferred: 99
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
generic-into-block ok: a=10 b=20
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
named: len=3 a0=10.000000 a2=30.000000
|
||||
expr: len=3 e2=9.000000
|
||||
copy: len=3 b2=30.000000
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
named: len=3 a0=10 a2=30
|
||||
expr: len=3 e2=9
|
||||
copy: len=3 b2=30
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
a.len=3 b.len=4
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
box: 42
|
||||
vec: 1.000000 4.000000
|
||||
typefn: len=3 a0=10 a2=30
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
field: 3
|
||||
method: 3
|
||||
chain: 11
|
||||
annot: ann
|
||||
helper: 7
|
||||
thing: 42
|
||||
facade: 7
|
||||
facade-annot: qq
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
7
|
||||
9
|
||||
8
|
||||
2
|
||||
7
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
9
|
||||
1.500000
|
||||
42
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
direct=42
|
||||
ufcs=42
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
unbox=42
|
||||
byval=42
|
||||
second=2.500000
|
||||
nested=9
|
||||
ufcs=42
|
||||
Reference in New Issue
Block a user