lang: opt-in UFCS — ufcs-marked fns + alias dot-dispatch, generic binding via receiver; one binding builder for plan-side generic returns
This commit is contained in:
@@ -10,9 +10,9 @@
|
||||
Counter :: struct { n: s32; }
|
||||
|
||||
// FREE functions (defined outside the struct), pointer first param.
|
||||
bump :: (c: *Counter) -> s32 { c.n += 1; return c.n; }
|
||||
bump :: ufcs (c: *Counter) -> s32 { c.n += 1; return c.n; }
|
||||
// reached ONLY via UFCS — must still be emitted.
|
||||
reset :: (c: *Counter) { c.n = 0; }
|
||||
reset :: ufcs (c: *Counter) { c.n = 0; }
|
||||
|
||||
main :: () -> s32 {
|
||||
c := Counter.{ n = 10 };
|
||||
|
||||
35
examples/0053-basic-ufcs-opt-in.sx
Normal file
35
examples/0053-basic-ufcs-opt-in.sx
Normal file
@@ -0,0 +1,35 @@
|
||||
// Free-function dot-calls are OPT-IN. Two opt-in spellings:
|
||||
// name :: ufcs (params) { body } — the fn itself is dot-callable
|
||||
// name :: ufcs target; — dot-callable (renaming) alias
|
||||
// A plain fn is callable directly or via `|>` only (see 1166 for the
|
||||
// rejection). Generic ufcs fns dispatch through normal monomorphization,
|
||||
// and the plan-side return type binds from the receiver (structured
|
||||
// params like `[]$T` included).
|
||||
|
||||
#import "modules/std.sx";
|
||||
|
||||
bump :: (x: s64) -> s64 { x + 1 }
|
||||
bump2 :: ufcs (x: s64) -> s64 { x + 2 }
|
||||
bump3 :: ufcs bump;
|
||||
|
||||
Counter :: struct { n: s64; }
|
||||
inc :: ufcs (c: *Counter, by: s64) -> s64 { c.n += by; c.n }
|
||||
|
||||
gfirst :: ufcs (xs: []$T) -> T { xs[0] }
|
||||
|
||||
main :: () {
|
||||
f : s64 = 40;
|
||||
print("marked: {}\n", f.bump2()); // 42
|
||||
print("alias: {}\n", f.bump3()); // 41
|
||||
print("direct: {}\n", bump(f)); // 41 — plain fn, direct
|
||||
print("pipe: {}\n", f |> bump()); // 41 — plain fn, pipe
|
||||
print("marked-direct: {}\n", bump2(f)); // 42 — marked fn callable directly
|
||||
|
||||
c := Counter.{ n = 10 };
|
||||
print("ptr-recv: {}\n", c.inc(5)); // 15 — auto address-of receiver
|
||||
|
||||
arr := .[7, 8, 9];
|
||||
xs : []s64 = arr;
|
||||
print("generic-dot: {}\n", xs.gfirst()); // 7
|
||||
print("generic-direct: {}\n", gfirst(xs)); // 7 — plan types it s64, not a T stub
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
Hasher :: struct { total: s64 = 0; count: s64 = 0; }
|
||||
|
||||
update :: (self: *Hasher, n: s64) {
|
||||
update :: ufcs (self: *Hasher, n: s64) {
|
||||
self.total += n;
|
||||
self.count += 1;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// a.sx authors `bump` adding 1. Imported first → first-wins winner. `from_a`'s
|
||||
// `v.bump()` resolves a.sx's own author (own == winner → existing UFCS path,
|
||||
// byte-for-byte unchanged).
|
||||
bump :: (x: s64) -> s64 { return x + 1; }
|
||||
bump :: ufcs (x: s64) -> s64 { return x + 1; }
|
||||
from_a_ufcs :: () -> s64 { v : s64 = 10; return v.bump(); }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// b.sx authors its OWN `bump` adding 100. `from_b`'s `v.bump()` must dispatch
|
||||
// b.sx's author (+100 → 110), not the first-wins winner from a.sx (+1).
|
||||
bump :: (x: s64) -> s64 { return x + 100; }
|
||||
bump :: ufcs (x: s64) -> s64 { return x + 100; }
|
||||
from_b_ufcs :: () -> s64 { v : s64 = 10; return v.bump(); }
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
// a.sx authors `dup` (+1). One of two distinct flat authors of `dup`.
|
||||
dup :: (x: s64) -> s64 { return x + 1; }
|
||||
dup :: ufcs (x: s64) -> s64 { return x + 1; }
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
// b.sx authors its OWN `dup` (+2) — the second distinct flat author. Main
|
||||
// imports both and authors neither, so `v.dup()` from main is ambiguous.
|
||||
dup :: (x: s64) -> s64 { return x + 2; }
|
||||
dup :: ufcs (x: s64) -> s64 { return x + 2; }
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
// a.sx authors `tag` returning a string; imported first → first-wins winner.
|
||||
// `show_a`'s `v.tag()` is the caller's OWN author (own == winner → existing UFCS
|
||||
// path, byte-for-byte unchanged): typed AND dispatched as a.tag (string).
|
||||
tag :: (x: s64) -> string { return "a-string"; }
|
||||
tag :: ufcs (x: s64) -> string { return "a-string"; }
|
||||
show_a :: () { v : s64 = 10; print("a: v.tag() = {}\n", v.tag()); }
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
// dispatched AND typed as b.tag (s64 = 110), not the first-wins winner from a.sx
|
||||
// (string). `print` types each arg from the call plan, so a mistype here boxes
|
||||
// the s64 as a string pointer → segfault before the fix.
|
||||
tag :: (x: s64) -> s64 { return x + 100; }
|
||||
tag :: ufcs (x: s64) -> s64 { return x + 100; }
|
||||
show_b :: () { v : s64 = 10; print("b: v.tag() = {}\n", v.tag()); }
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
// Typed allocation helpers over the Allocator protocol (std/mem.sx):
|
||||
// create/destroy (one T), alloc/free (slices), clone, resize, and the
|
||||
// bytes-level mem_realloc. Free functions — direct calls and the
|
||||
// fluent pipe spelling (`context.allocator |> create(Session)`) hit
|
||||
// the same generic machinery. Contents are UNINITIALISED by design
|
||||
// bytes-level mem_realloc. Declared `ufcs` — the dot spelling
|
||||
// (`context.allocator.create(Session)`), the pipe spelling, and the
|
||||
// direct call all hit the same generic machinery. Contents are
|
||||
// UNINITIALISED by design
|
||||
// (Zig-aligned): assign before reading. TrackingAllocator balances to
|
||||
// zero across every pair.
|
||||
|
||||
@@ -28,16 +29,22 @@ main :: () {
|
||||
print("pipe-create: {}\n", p.id);
|
||||
a |> destroy(p);
|
||||
|
||||
// create — canonical dot spelling on context.allocator
|
||||
q := context.allocator.create(Session);
|
||||
q.id = 2;
|
||||
print("dot-create: {}\n", q.id);
|
||||
context.allocator.destroy(q);
|
||||
|
||||
// alloc / free — typed slice
|
||||
xs := a |> alloc(s64, 4);
|
||||
xs[0] = 10; xs[1] = 20; xs[2] = 30; xs[3] = 40;
|
||||
print("alloc: {} {} len={}\n", xs[0], xs[3], xs.len);
|
||||
|
||||
// clone — independent copy
|
||||
ys := xs |> clone(a);
|
||||
// clone — independent copy (canonical dot spelling)
|
||||
ys := xs.clone(a);
|
||||
xs[0] = 99;
|
||||
print("clone: {} (orig {})\n", ys[0], xs[0]);
|
||||
a |> free(ys);
|
||||
a.free(ys);
|
||||
|
||||
// resize — grow (copies, old backing freed)
|
||||
zs := xs |> resize(a, 6);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Box :: struct { total: s64 = 0; count: s64 = 0; }
|
||||
|
||||
update :: (self: *Box, n: s64) {
|
||||
update :: ufcs (self: *Box, n: s64) {
|
||||
self.total += n;
|
||||
self.count += 1;
|
||||
}
|
||||
|
||||
11
examples/1166-diagnostics-ufcs-not-opted-in.sx
Normal file
11
examples/1166-diagnostics-ufcs-not-opted-in.sx
Normal file
@@ -0,0 +1,11 @@
|
||||
// A dot-call on a PLAIN free function (no `ufcs` marker, no alias) is
|
||||
// rejected with a tailored help: direct call, pipe, or declare it ufcs.
|
||||
|
||||
#import "modules/std.sx";
|
||||
|
||||
bump :: (x: s64) -> s64 { x + 1 }
|
||||
|
||||
main :: () {
|
||||
f : s64 = 40;
|
||||
print("{}\n", f.bump());
|
||||
}
|
||||
1
examples/expected/0053-basic-ufcs-opt-in.exit
Normal file
1
examples/expected/0053-basic-ufcs-opt-in.exit
Normal file
@@ -0,0 +1 @@
|
||||
0
|
||||
1
examples/expected/0053-basic-ufcs-opt-in.stderr
Normal file
1
examples/expected/0053-basic-ufcs-opt-in.stderr
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
8
examples/expected/0053-basic-ufcs-opt-in.stdout
Normal file
8
examples/expected/0053-basic-ufcs-opt-in.stdout
Normal file
@@ -0,0 +1,8 @@
|
||||
marked: 42
|
||||
alias: 41
|
||||
direct: 41
|
||||
pipe: 41
|
||||
marked-direct: 42
|
||||
ptr-recv: 15
|
||||
generic-dot: 7
|
||||
generic-direct: 7
|
||||
@@ -1,5 +1,6 @@
|
||||
create: 7 42
|
||||
pipe-create: 1
|
||||
dot-create: 2
|
||||
alloc: 10 40 len=4
|
||||
clone: 10 (orig 99)
|
||||
resize: 20 60 len=6
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
10
examples/expected/1166-diagnostics-ufcs-not-opted-in.stderr
Normal file
10
examples/expected/1166-diagnostics-ufcs-not-opted-in.stderr
Normal file
@@ -0,0 +1,10 @@
|
||||
error: 'bump' is not a ufcs function — a plain function does not dispatch via dot-call
|
||||
--> examples/1166-diagnostics-ufcs-not-opted-in.sx:10:19
|
||||
|
|
||||
10 | print("{}\n", f.bump());
|
||||
| ^^^^^^
|
||||
|
||||
help: call it directly (`bump(receiver, ...)`), pipe it (`receiver |> bump(...)`), or declare it `bump :: ufcs (...) { ... }`
|
||||
|
|
||||
10 | print("{}\n", f.bump());
|
||||
| ^^^^^^
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
Reference in New Issue
Block a user