diff --git a/examples/0022-basic-for-range.sx b/examples/0022-basic-for-range.sx index b9e64830..555300cf 100644 --- a/examples/0022-basic-for-range.sx +++ b/examples/0022-basic-for-range.sx @@ -7,7 +7,7 @@ #import "modules/std.sx"; Show :: protocol { - show :: () -> string; + show :: (self: *Self) -> string; } A :: struct { x: i64; } B :: struct { s: string; } diff --git a/examples/0026-basic-operators.sx b/examples/0026-basic-operators.sx index 9fb4842a..0f123f8b 100644 --- a/examples/0026-basic-operators.sx +++ b/examples/0026-basic-operators.sx @@ -10,11 +10,11 @@ mul :: (a: i32, b: i32) -> i32 { a * b } // P4 edge: Chained default→default calls Chained :: protocol { - base :: (msg: string) -> i32; - wrap :: (msg: string) -> i32 { + base :: (self: *Self, msg: string) -> i32; + wrap :: (self: *Self, msg: string) -> i32 { self.base(msg) + 1 } - double_wrap :: (msg: string) -> i32 { + double_wrap :: (self: *Self, msg: string) -> i32 { self.wrap(msg) + self.wrap(msg) } } diff --git a/examples/0129-types-tuple-operators.sx b/examples/0129-types-tuple-operators.sx index 348c9d64..739a058f 100644 --- a/examples/0129-types-tuple-operators.sx +++ b/examples/0129-types-tuple-operators.sx @@ -28,11 +28,11 @@ apply :: (f: (i32, i32) -> i32, x: i32, y: i32) -> i32 { // P4 edge: Chained default→default calls Chained :: protocol { - base :: (msg: string) -> i32; - wrap :: (msg: string) -> i32 { + base :: (self: *Self, msg: string) -> i32; + wrap :: (self: *Self, msg: string) -> i32 { self.base(msg) + 1 } - double_wrap :: (msg: string) -> i32 { + double_wrap :: (self: *Self, msg: string) -> i32 { self.wrap(msg) + self.wrap(msg) } } diff --git a/examples/0131-types-init-blocks.sx b/examples/0131-types-init-blocks.sx index a0f2bce3..68e80d98 100644 --- a/examples/0131-types-init-blocks.sx +++ b/examples/0131-types-init-blocks.sx @@ -9,12 +9,12 @@ Point :: struct { x, y: i32; } add :: (a: i32, b: i32) -> i32 { a + b } Counter :: protocol { - inc :: (); - get :: () -> i32; + inc :: (self: *Self); + get :: (self: *Self) -> i32; } Summable :: protocol { - sum :: () -> i32; + sum :: (self: *Self) -> i32; } SimpleCounter :: struct { val: i32; } diff --git a/examples/0158-types-reserved-name-member-exempt.sx b/examples/0158-types-reserved-name-member-exempt.sx index c085d38a..83bcf3c5 100644 --- a/examples/0158-types-reserved-name-member-exempt.sx +++ b/examples/0158-types-reserved-name-member-exempt.sx @@ -28,7 +28,7 @@ Tag :: union { // Protocol method SIGNATURE spelled with a reserved type name — bare is legal. Speaker :: protocol { - i2 :: () -> i64; + i2 :: (self: *Self) -> i64; } Dog :: struct { n: i64; } diff --git a/examples/0204-generics-generic-protocol-constraint.sx b/examples/0204-generics-generic-protocol-constraint.sx index eaee7bad..63c01644 100644 --- a/examples/0204-generics-generic-protocol-constraint.sx +++ b/examples/0204-generics-generic-protocol-constraint.sx @@ -5,7 +5,7 @@ #import "modules/math"; Lerpable :: protocol #inline { - lerp :: (b: Self, t: f32) -> Self; + lerp :: (self: *Self, b: Self, t: f32) -> Self; } Size :: struct { diff --git a/examples/0303-closures-closure-returning-protocol.sx b/examples/0303-closures-closure-returning-protocol.sx index e39b2819..2d096208 100644 --- a/examples/0303-closures-closure-returning-protocol.sx +++ b/examples/0303-closures-closure-returning-protocol.sx @@ -4,7 +4,7 @@ #import "modules/std.sx"; MyProtocol :: protocol { - get_value :: () -> i64; + get_value :: (self: *Self) -> i64; } MyImpl :: struct { value: i64; } diff --git a/examples/0400-protocols-impl-for-builtin.sx b/examples/0400-protocols-impl-for-builtin.sx index fe7899f2..5317320f 100644 --- a/examples/0400-protocols-impl-for-builtin.sx +++ b/examples/0400-protocols-impl-for-builtin.sx @@ -3,7 +3,7 @@ // `#inline` erasure. Lerpable :: protocol #inline { - lerp :: (b: Self, t: f32) -> Self; + lerp :: (self: *Self, b: Self, t: f32) -> Self; } impl Lerpable for f32 { diff --git a/examples/0401-protocols-protocol-in-wrapper-struct.sx b/examples/0401-protocols-protocol-in-wrapper-struct.sx index fb0c5987..7fe31cce 100644 --- a/examples/0401-protocols-protocol-in-wrapper-struct.sx +++ b/examples/0401-protocols-protocol-in-wrapper-struct.sx @@ -5,7 +5,7 @@ #import "modules/std.sx"; Sizable :: protocol { - size :: () -> i64; + size :: (self: *Self) -> i64; } Widget :: struct { value: i64; } diff --git a/examples/0402-protocols-protocol-list-from-fn.sx b/examples/0402-protocols-protocol-list-from-fn.sx index a6f916c1..ebd34028 100644 --- a/examples/0402-protocols-protocol-list-from-fn.sx +++ b/examples/0402-protocols-protocol-list-from-fn.sx @@ -5,7 +5,7 @@ #import "modules/std.sx"; Sizable :: protocol { - size :: () -> i64; + size :: (self: *Self) -> i64; } Leaf :: struct { value: i64; } diff --git a/examples/0403-protocols-protocol-dispatch-via-fn-arg.sx b/examples/0403-protocols-protocol-dispatch-via-fn-arg.sx index 3816e464..64c89ace 100644 --- a/examples/0403-protocols-protocol-dispatch-via-fn-arg.sx +++ b/examples/0403-protocols-protocol-dispatch-via-fn-arg.sx @@ -5,7 +5,7 @@ #import "modules/std.sx"; Sizable :: protocol { - size :: () -> i64; + size :: (self: *Self) -> i64; } Leaf :: struct { value: i64; } diff --git a/examples/0404-protocols-dot-shorthand-protocol-field.sx b/examples/0404-protocols-dot-shorthand-protocol-field.sx index 2832aff1..c6d38b7c 100644 --- a/examples/0404-protocols-dot-shorthand-protocol-field.sx +++ b/examples/0404-protocols-dot-shorthand-protocol-field.sx @@ -5,10 +5,10 @@ #import "modules/std.sx"; Drawable :: protocol { - draw :: () -> i32; - name :: () -> string; - layout :: (x: i32) -> i32; - handle :: (event: i32) -> bool; + draw :: (self: *Self) -> i32; + name :: (self: *Self) -> string; + layout :: (self: *Self, x: i32) -> i32; + handle :: (self: *Self, event: i32) -> bool; } Circle :: struct { radius: i32; } diff --git a/examples/0405-protocols-enum-through-protocol-dispatch.sx b/examples/0405-protocols-enum-through-protocol-dispatch.sx index 5aef554a..95cd0577 100644 --- a/examples/0405-protocols-enum-through-protocol-dispatch.sx +++ b/examples/0405-protocols-enum-through-protocol-dispatch.sx @@ -6,7 +6,7 @@ Fmt :: enum { a; b; } Proto :: protocol { - take_fmt :: (f: Fmt); + take_fmt :: (self: *Self, f: Fmt); } Impl :: struct {} diff --git a/examples/0406-protocols-protocol-real-pointer-return.sx b/examples/0406-protocols-protocol-real-pointer-return.sx index 5e782137..99b6c674 100644 --- a/examples/0406-protocols-protocol-real-pointer-return.sx +++ b/examples/0406-protocols-protocol-real-pointer-return.sx @@ -7,7 +7,7 @@ #import "modules/std.sx"; Proto :: protocol { - get :: () -> *u8; + get :: (self: *Self) -> *u8; } Impl :: struct { diff --git a/examples/0408-protocols-optional-protocol.sx b/examples/0408-protocols-optional-protocol.sx index 2cb845a5..32f507ba 100644 --- a/examples/0408-protocols-optional-protocol.sx +++ b/examples/0408-protocols-optional-protocol.sx @@ -7,7 +7,7 @@ #import "modules/std.sx"; GPU :: protocol { - ping :: () -> i64; + ping :: (self: *Self) -> i64; } Impl :: struct {} diff --git a/examples/0413-protocols-parameterized-protocol-value.sx b/examples/0413-protocols-parameterized-protocol-value.sx index 76836368..3bf0fcbe 100644 --- a/examples/0413-protocols-parameterized-protocol-value.sx +++ b/examples/0413-protocols-parameterized-protocol-value.sx @@ -6,7 +6,7 @@ #import "modules/std.sx"; -VL :: protocol(T: Type) { get :: () -> T; } +VL :: protocol(T: Type) { get :: (self: *Self) -> T; } IntCell :: struct { v: i64; } StrCell :: struct { s: string; } impl VL(i64) for IntCell { get :: (self: *IntCell) -> i64 => self.v; } diff --git a/examples/0414-protocols-generic-struct-protocol-erase.sx b/examples/0414-protocols-generic-struct-protocol-erase.sx index a4a887a2..4de4039f 100644 --- a/examples/0414-protocols-generic-struct-protocol-erase.sx +++ b/examples/0414-protocols-generic-struct-protocol-erase.sx @@ -9,7 +9,7 @@ #import "modules/std.sx"; -VL :: protocol(T: Type) { get :: () -> T; } +VL :: protocol(T: Type) { get :: (self: *Self) -> T; } IntCell :: struct { v: i64; } impl VL(i64) for IntCell { get :: (self: *IntCell) -> i64 => self.v; } diff --git a/examples/0415-protocols-protocols.sx b/examples/0415-protocols-protocols.sx index 8a0bb986..bf88a050 100644 --- a/examples/0415-protocols-protocols.sx +++ b/examples/0415-protocols-protocols.sx @@ -203,12 +203,12 @@ c_abs :: (n: i32) -> i32 extern libc "abs"; // --- Protocol declarations (Phase 1: static dispatch only) --- Counter :: protocol { - inc :: (); - get :: () -> i32; + inc :: (self: *Self); + get :: (self: *Self) -> i32; } Summable :: protocol { - sum :: () -> i32; + sum :: (self: *Self) -> i32; } SimpleCounter :: struct { val: i32; } @@ -226,8 +226,8 @@ impl Summable for Point { // Phase 2: #inline protocol for dynamic dispatch Adder :: protocol #inline { - add :: (n: i32); - value :: () -> i32; + add :: (self: *Self, n: i32); + value :: (self: *Self) -> i32; } Accumulator :: struct { @@ -250,8 +250,8 @@ impl Adder for Doubler { // Phase 4: default methods Repeater :: protocol { - say :: (msg: string); - say_twice :: (msg: string) { + say :: (self: *Self, msg: string); + say_twice :: (self: *Self, msg: string) { self.say(msg); self.say(msg); } @@ -270,11 +270,11 @@ impl Repeater for Printer { // P4 edge: Chained default→default calls Chained :: protocol { - base :: (msg: string) -> i32; - wrap :: (msg: string) -> i32 { + base :: (self: *Self, msg: string) -> i32; + wrap :: (self: *Self, msg: string) -> i32 { self.base(msg) + 1 } - double_wrap :: (msg: string) -> i32 { + double_wrap :: (self: *Self, msg: string) -> i32 { self.wrap(msg) + self.wrap(msg) } } @@ -291,7 +291,7 @@ impl Chained for ChainImpl { // Phase 5: Self type Eq :: protocol { - eq :: (other: Self) -> bool; + eq :: (self: *Self, other: Self) -> bool; } impl Eq for Point { @@ -301,7 +301,7 @@ impl Eq for Point { } Cloneable :: protocol { - clone :: () -> Self; + clone :: (self: *Self) -> Self; } impl Cloneable for Point { @@ -324,7 +324,7 @@ are_equal :: ($T: Type/Eq, a: T, b: T) -> bool { } Hashable :: protocol { - hash :: () -> i64; + hash :: (self: *Self) -> i64; } impl Hashable for Point { diff --git a/examples/0416-protocols-auto-type-erasure.sx b/examples/0416-protocols-auto-type-erasure.sx index f26d8e09..2cc9c97b 100644 --- a/examples/0416-protocols-auto-type-erasure.sx +++ b/examples/0416-protocols-auto-type-erasure.sx @@ -9,12 +9,12 @@ Point :: struct { x, y: i32; } add :: (a: i32, b: i32) -> i32 { a + b } Counter :: protocol { - inc :: (); - get :: () -> i32; + inc :: (self: *Self); + get :: (self: *Self) -> i32; } Summable :: protocol { - sum :: () -> i32; + sum :: (self: *Self) -> i32; } SimpleCounter :: struct { val: i32; } @@ -32,8 +32,8 @@ impl Summable for Point { // Phase 2: #inline protocol for dynamic dispatch Adder :: protocol #inline { - add :: (n: i32); - value :: () -> i32; + add :: (self: *Self, n: i32); + value :: (self: *Self) -> i32; } Accumulator :: struct { diff --git a/examples/0417-protocols-protocol-return-name-collision.sx b/examples/0417-protocols-protocol-return-name-collision.sx index ad99bea8..424dcd29 100644 --- a/examples/0417-protocols-protocol-return-name-collision.sx +++ b/examples/0417-protocols-protocol-return-name-collision.sx @@ -24,7 +24,7 @@ Keycode :: enum { unknown; escape; enter; } KeyData :: struct { key: Keycode; } Event :: enum { none; key_up: KeyData; } -Plat :: protocol { one_event :: () -> Event; } +Plat :: protocol { one_event :: (self: *Self) -> Event; } Impl :: struct { dummy: i64; } impl Plat for Impl { diff --git a/examples/0418-protocols-explicit-receiver.sx b/examples/0418-protocols-explicit-receiver.sx new file mode 100644 index 00000000..842f5862 --- /dev/null +++ b/examples/0418-protocols-explicit-receiver.sx @@ -0,0 +1,47 @@ +// Protocol methods declare their receiver EXPLICITLY as the first parameter — +// `self: *Self` (or `self: Self`) — matching the `impl` method signature. This +// is required (the old implicit-receiver form is a parse error). The receiver +// annotation is validated then erased, so dispatch and call sites are unchanged: +// a method with N declared extra args is still called with N args. +#import "modules/std.sx"; + +Size :: struct { w: i32; h: i32; } + +// `self: *Self` receiver; one no-arg method and one with extra args. +Measurable :: protocol #inline { + measure :: (self: *Self) -> Size; + scaled :: (self: *Self, factor: i32) -> Size; +} + +// `self: Self` (by-value) receiver is also accepted. +Named :: protocol { + name :: (self: Self) -> string; +} + +Widget :: struct { base: i32; } + +impl Measurable for Widget { + measure :: (self: *Widget) -> Size { return Size.{ w = self.base, h = self.base * 2 }; } + scaled :: (self: *Widget, factor: i32) -> Size { + return Size.{ w = self.base * factor, h = self.base * 2 * factor }; + } +} + +impl Named for Widget { + name :: (self: Widget) -> string { return "widget"; } +} + +main :: () -> i32 { + w : Widget = .{ base = 5 }; + p : *Widget = @w; + + m : Measurable = xx p; + s := m.measure(); // 0 extra args + s2 := m.scaled(3); // 1 extra arg + print("measure={}x{}\n", s.w, s.h); // 5x10 + print("scaled={}x{}\n", s2.w, s2.h); // 15x30 + + n : Named = xx p; + print("name={}\n", n.name()); // widget + return 0; +} diff --git a/examples/0517-packs-pack-reflection-intrinsics.sx b/examples/0517-packs-pack-reflection-intrinsics.sx index bbc55471..f05e304d 100644 --- a/examples/0517-packs-pack-reflection-intrinsics.sx +++ b/examples/0517-packs-pack-reflection-intrinsics.sx @@ -24,7 +24,7 @@ // User-defined parameterised protocol + an impl, so has_impl can // confirm parameterised matching works with a known-true case. Wrap :: protocol(Target: Type) { - wrap :: () -> Target; + wrap :: (self: *Self) -> Target; } impl Wrap(i64) for i32 { diff --git a/examples/0528-packs-protocol-pack-methods.sx b/examples/0528-packs-protocol-pack-methods.sx index b9667e3a..83ac59b5 100644 --- a/examples/0528-packs-protocol-pack-methods.sx +++ b/examples/0528-packs-protocol-pack-methods.sx @@ -9,7 +9,7 @@ #import "modules/std.sx"; Greeter :: protocol { - greet :: () -> i64; + greet :: (self: *Self) -> i64; } Dog :: struct { age: i64; } diff --git a/examples/0529-packs-protocol-pack-parameterized.sx b/examples/0529-packs-protocol-pack-parameterized.sx index d38eabfe..7482fd37 100644 --- a/examples/0529-packs-protocol-pack-parameterized.sx +++ b/examples/0529-packs-protocol-pack-parameterized.sx @@ -10,7 +10,7 @@ #import "modules/std.sx"; Box :: protocol(T: Type) { - get :: () -> T; + get :: (self: *Self) -> T; } IntCell :: struct { v: i64; } diff --git a/examples/0530-packs-pack-interface-only.sx b/examples/0530-packs-pack-interface-only.sx index 7889c47e..d4722a47 100644 --- a/examples/0530-packs-pack-interface-only.sx +++ b/examples/0530-packs-pack-interface-only.sx @@ -7,7 +7,7 @@ #import "modules/std.sx"; Box :: protocol(T: Type) { - get :: () -> T; + get :: (self: *Self) -> T; } IntCell :: struct { v: i64; } impl Box(i64) for IntCell { get :: (self: *IntCell) -> i64 => self.v; } diff --git a/examples/0531-packs-pack-value-projection.sx b/examples/0531-packs-pack-value-projection.sx index 36bb057d..2dc592a6 100644 --- a/examples/0531-packs-pack-value-projection.sx +++ b/examples/0531-packs-pack-value-projection.sx @@ -8,7 +8,7 @@ #import "modules/std.sx"; Box :: protocol(T: Type) { - get :: () -> T; + get :: (self: *Self) -> T; } IntCell :: struct { v: i64; } diff --git a/examples/0532-packs-pack-spread-call.sx b/examples/0532-packs-pack-spread-call.sx index 72944b9f..a3d62014 100644 --- a/examples/0532-packs-pack-spread-call.sx +++ b/examples/0532-packs-pack-spread-call.sx @@ -6,7 +6,7 @@ #import "modules/std.sx"; Box :: protocol(T: Type) { - get :: () -> i64; + get :: (self: *Self) -> i64; } IntCell :: struct { v: i64; } Dbl :: struct { n: i64; } diff --git a/examples/0533-packs-pack-tuple-materialize.sx b/examples/0533-packs-pack-tuple-materialize.sx index 7a9faf01..b2ec35e5 100644 --- a/examples/0533-packs-pack-tuple-materialize.sx +++ b/examples/0533-packs-pack-tuple-materialize.sx @@ -6,7 +6,7 @@ #import "modules/std.sx"; Box :: protocol(T: Type) { - get :: () -> T; + get :: (self: *Self) -> T; } IntCell :: struct { v: i64; } StrCell :: struct { s: string; } diff --git a/examples/0534-packs-pack-type-projection.sx b/examples/0534-packs-pack-type-projection.sx index e09df906..60da85e0 100644 --- a/examples/0534-packs-pack-type-projection.sx +++ b/examples/0534-packs-pack-type-projection.sx @@ -7,7 +7,7 @@ #import "modules/std.sx"; Box :: protocol(T: Type) { - get :: () -> T; + get :: (self: *Self) -> T; } IntCell :: struct { v: i64; } diff --git a/examples/0535-packs-slice-of-protocol-variadic.sx b/examples/0535-packs-slice-of-protocol-variadic.sx index 33be4615..ce7563e0 100644 --- a/examples/0535-packs-slice-of-protocol-variadic.sx +++ b/examples/0535-packs-slice-of-protocol-variadic.sx @@ -9,7 +9,7 @@ #import "modules/std.sx"; -Show :: protocol { show :: () -> string; } +Show :: protocol { show :: (self: *Self) -> string; } A :: struct { x: i64; } B :: struct { s: string; } impl Show for A { show :: (self: *A) -> string => "A"; } diff --git a/examples/0536-packs-pack-as-value.sx b/examples/0536-packs-pack-as-value.sx index 46ca0e4e..85c1aab9 100644 --- a/examples/0536-packs-pack-as-value.sx +++ b/examples/0536-packs-pack-as-value.sx @@ -5,7 +5,7 @@ #import "modules/std.sx"; -Show :: protocol { show :: () -> string; } +Show :: protocol { show :: (self: *Self) -> string; } A :: struct {} impl Show for A { show :: (self: *A) -> string => "A"; } diff --git a/examples/0537-packs-pack-xx-to-slice.sx b/examples/0537-packs-pack-xx-to-slice.sx index 9fed452c..86ef05f3 100644 --- a/examples/0537-packs-pack-xx-to-slice.sx +++ b/examples/0537-packs-pack-xx-to-slice.sx @@ -5,7 +5,7 @@ #import "modules/std.sx"; -Show :: protocol { show :: () -> string; } +Show :: protocol { show :: (self: *Self) -> string; } A :: struct {} B :: struct { s: string; } impl Show for A { show :: (self: *A) -> string => "A"; } diff --git a/examples/0539-packs-combined-pack-field.sx b/examples/0539-packs-combined-pack-field.sx index b8f85ea1..ad2925a2 100644 --- a/examples/0539-packs-combined-pack-field.sx +++ b/examples/0539-packs-combined-pack-field.sx @@ -7,7 +7,7 @@ #import "modules/std.sx"; -VL :: protocol(T: Type) { get :: () -> T; } +VL :: protocol(T: Type) { get :: (self: *Self) -> T; } IntCell :: struct { v: i64; } StrCell :: struct { s: string; } impl VL(i64) for IntCell { get :: (self: *IntCell) -> i64 => self.v; } diff --git a/examples/0540-packs-pack-type-arg-spread.sx b/examples/0540-packs-pack-type-arg-spread.sx index 16339d3e..ce394182 100644 --- a/examples/0540-packs-pack-type-arg-spread.sx +++ b/examples/0540-packs-pack-type-arg-spread.sx @@ -6,7 +6,7 @@ #import "modules/std.sx"; -VL :: protocol(T: Type) { get :: () -> T; } +VL :: protocol(T: Type) { get :: (self: *Self) -> T; } IntCell :: struct { v: i64; } impl VL(i64) for IntCell { get :: (self: *IntCell) -> i64 => self.v; } diff --git a/examples/0541-packs-pack-to-protocol-tuple.sx b/examples/0541-packs-pack-to-protocol-tuple.sx index 49765ecf..752d0dd6 100644 --- a/examples/0541-packs-pack-to-protocol-tuple.sx +++ b/examples/0541-packs-pack-to-protocol-tuple.sx @@ -5,7 +5,7 @@ #import "modules/std.sx"; -VL :: protocol(T: Type) { get :: () -> T; } +VL :: protocol(T: Type) { get :: (self: *Self) -> T; } IntCell :: struct { v: i64; } StrCell :: struct { s: string; } impl VL(i64) for IntCell { get :: (self: *IntCell) -> i64 => self.v; } diff --git a/examples/0542-packs-mapper-projection-spread.sx b/examples/0542-packs-mapper-projection-spread.sx index 1b2829e6..3e7703f9 100644 --- a/examples/0542-packs-mapper-projection-spread.sx +++ b/examples/0542-packs-mapper-projection-spread.sx @@ -4,7 +4,7 @@ #import "modules/std.sx"; -VL :: protocol(T: Type) { get :: () -> T; } +VL :: protocol(T: Type) { get :: (self: *Self) -> T; } IntCell :: struct { v: i64; } impl VL(i64) for IntCell { get :: (self: *IntCell) -> i64 => self.v; } diff --git a/examples/0543-packs-canonical-map.sx b/examples/0543-packs-canonical-map.sx index 2b185748..5be21fc4 100644 --- a/examples/0543-packs-canonical-map.sx +++ b/examples/0543-packs-canonical-map.sx @@ -12,7 +12,7 @@ #import "modules/std.sx"; -VL :: protocol(T: Type) { get :: () -> T; } +VL :: protocol(T: Type) { get :: (self: *Self) -> T; } IntCell :: struct { v: i64; } impl VL(i64) for IntCell { get :: (self: *IntCell) -> i64 => self.v; } diff --git a/examples/0545-packs-inline-for-element.sx b/examples/0545-packs-inline-for-element.sx index fdb5499a..1d1e220f 100644 --- a/examples/0545-packs-inline-for-element.sx +++ b/examples/0545-packs-inline-for-element.sx @@ -11,7 +11,7 @@ #import "modules/std.sx"; -Show :: protocol { show :: () -> string; } +Show :: protocol { show :: (self: *Self) -> string; } IntBox :: struct { v: i64; } StrBox :: struct { s: string; } impl Show for IntBox { show :: (self: *IntBox) -> string { int_to_string(self.v) } } diff --git a/examples/0547-packs-xx-pack-index-to-protocol.sx b/examples/0547-packs-xx-pack-index-to-protocol.sx index 92139d4c..b84a2976 100644 --- a/examples/0547-packs-xx-pack-index-to-protocol.sx +++ b/examples/0547-packs-xx-pack-index-to-protocol.sx @@ -11,7 +11,7 @@ #import "modules/std.sx"; -VL :: protocol(T: Type) { get :: () -> T; } +VL :: protocol(T: Type) { get :: (self: *Self) -> T; } IntCell :: struct { v: i64; } impl VL(i64) for IntCell { get :: (self: *IntCell) -> i64 => self.v; } diff --git a/examples/0548-packs-xx-pack-index-two-elements.sx b/examples/0548-packs-xx-pack-index-two-elements.sx index de01b714..1d91d989 100644 --- a/examples/0548-packs-xx-pack-index-two-elements.sx +++ b/examples/0548-packs-xx-pack-index-two-elements.sx @@ -7,7 +7,7 @@ #import "modules/std.sx"; -VL :: protocol(T: Type) { get :: () -> T; } +VL :: protocol(T: Type) { get :: (self: *Self) -> T; } IntCell :: struct { v: i64; } impl VL(i64) for IntCell { get :: (self: *IntCell) -> i64 => self.v; } Doubler :: struct { n: i64; } diff --git a/examples/0820-protocols-same-name-method-own-wins.sx b/examples/0820-protocols-same-name-method-own-wins.sx index d2a8af22..323897f4 100644 --- a/examples/0820-protocols-same-name-method-own-wins.sx +++ b/examples/0820-protocols-same-name-method-own-wins.sx @@ -15,7 +15,7 @@ Box :: struct { m: i32; } Provider :: protocol { - get :: () -> Box; + get :: (self: *Self) -> Box; } Holder :: struct { val: i32 = 7; } diff --git a/examples/0821-protocols-same-name-method-ambiguous.sx b/examples/0821-protocols-same-name-method-ambiguous.sx index 2b5b5a2a..2bc20cc5 100644 --- a/examples/0821-protocols-same-name-method-ambiguous.sx +++ b/examples/0821-protocols-same-name-method-ambiguous.sx @@ -10,7 +10,7 @@ #import "0821-protocols-same-name-method-ambiguous/b.sx"; Provider :: protocol { - get :: () -> Box; + get :: (self: *Self) -> Box; } main :: () -> i32 { diff --git a/examples/0824-protocols-same-name-method-wrapped-own-wins.sx b/examples/0824-protocols-same-name-method-wrapped-own-wins.sx index a93390d0..66ddbe88 100644 --- a/examples/0824-protocols-same-name-method-wrapped-own-wins.sx +++ b/examples/0824-protocols-same-name-method-wrapped-own-wins.sx @@ -21,17 +21,17 @@ Holder :: struct { b: Box = ---; } Provider :: protocol { // discriminating wrapped/compound RETURNS - getp :: () -> *Box; - geto :: () -> ?Box; - gett :: () -> (Box, Box); - geta :: () -> [2]Box; + getp :: (self: *Self) -> *Box; + geto :: (self: *Self) -> ?Box; + gett :: (self: *Self) -> (Box, Box); + geta :: (self: *Self) -> [2]Box; // routing-only wrapped/compound PARAMS - sump :: (p: *Box) -> i32; - sumo :: (o: ?Box) -> i32; - sums :: (s: []Box) -> i32; - suma :: (a: [2]Box) -> i32; - sumt :: (t: (Box, Box)) -> i32; - sumn :: (n: *?[]Box) -> i32; + sump :: (self: *Self, p: *Box) -> i32; + sumo :: (self: *Self, o: ?Box) -> i32; + sums :: (self: *Self, s: []Box) -> i32; + suma :: (self: *Self, a: [2]Box) -> i32; + sumt :: (self: *Self, t: (Box, Box)) -> i32; + sumn :: (self: *Self, n: *?[]Box) -> i32; } impl Provider for Holder { diff --git a/examples/0825-protocols-same-name-method-wrapped-ambiguous.sx b/examples/0825-protocols-same-name-method-wrapped-ambiguous.sx index f35ab3ea..daa09573 100644 --- a/examples/0825-protocols-same-name-method-wrapped-ambiguous.sx +++ b/examples/0825-protocols-same-name-method-wrapped-ambiguous.sx @@ -15,7 +15,7 @@ #import "0825-protocols-same-name-method-wrapped-ambiguous/b.sx"; Provider :: protocol { - getp :: () -> *Box; + getp :: (self: *Self) -> *Box; } main :: () -> i32 { diff --git a/examples/0828-protocols-param-impl-arg-wrapped-own-wins.sx b/examples/0828-protocols-param-impl-arg-wrapped-own-wins.sx index 2bb9d088..29045917 100644 --- a/examples/0828-protocols-param-impl-arg-wrapped-own-wins.sx +++ b/examples/0828-protocols-param-impl-arg-wrapped-own-wins.sx @@ -15,7 +15,7 @@ Box :: struct { m: i32; } Holder :: struct { n: i32; } Tagged :: protocol(T: Type) { - tag :: () -> i32; + tag :: (self: *Self) -> i32; } impl Tagged(*Box) for Holder { diff --git a/examples/0829-packs-param-impl-mixed-pack-source-ambiguous.sx b/examples/0829-packs-param-impl-mixed-pack-source-ambiguous.sx index 0bfd0902..6e31687f 100644 --- a/examples/0829-packs-param-impl-mixed-pack-source-ambiguous.sx +++ b/examples/0829-packs-param-impl-mixed-pack-source-ambiguous.sx @@ -22,7 +22,7 @@ Block :: struct { tag: i32; } Sink :: protocol(T: Type) { - convert :: () -> T; + convert :: (self: *Self) -> T; } impl Sink(Block) for Closure(*Box, ..$args) -> $R { diff --git a/examples/0902-optionals-optional-all-null.sx b/examples/0902-optionals-optional-all-null.sx index 2b9cc9b5..83c76ccc 100644 --- a/examples/0902-optionals-optional-all-null.sx +++ b/examples/0902-optionals-optional-all-null.sx @@ -10,7 +10,7 @@ ProposedSize :: struct { } Sizable :: protocol { - size :: (proposal: ProposedSize) -> f32; + size :: (self: *Self, proposal: ProposedSize) -> f32; } Widget :: struct {} diff --git a/examples/0903-optionals-optional-roundtrip.sx b/examples/0903-optionals-optional-roundtrip.sx index 03e2fae2..07e92d9f 100644 --- a/examples/0903-optionals-optional-roundtrip.sx +++ b/examples/0903-optionals-optional-roundtrip.sx @@ -16,7 +16,7 @@ direct_size :: (proposal: ProposedSize) -> f32 { } Sizable :: protocol { - size :: (proposal: ProposedSize) -> f32; + size :: (self: *Self, proposal: ProposedSize) -> f32; } Widget :: struct {} diff --git a/examples/1164-diagnostics-inline-for-pack-rejections.sx b/examples/1164-diagnostics-inline-for-pack-rejections.sx index ca4a4529..362a6381 100644 --- a/examples/1164-diagnostics-inline-for-pack-rejections.sx +++ b/examples/1164-diagnostics-inline-for-pack-rejections.sx @@ -6,7 +6,7 @@ #import "modules/std.sx"; -Show :: protocol { show :: () -> string; } +Show :: protocol { show :: (self: *Self) -> string; } IntBox :: struct { v: i64; } impl Show for IntBox { show :: (self: *IntBox) -> string { int_to_string(self.v) } } diff --git a/examples/1165-diagnostics-generic-return-unbound.sx b/examples/1165-diagnostics-generic-return-unbound.sx index d9aef7c8..57632012 100644 --- a/examples/1165-diagnostics-generic-return-unbound.sx +++ b/examples/1165-diagnostics-generic-return-unbound.sx @@ -14,7 +14,7 @@ Foo :: struct { weird :: (self: *Foo) -> $T { 0 } } -Show2 :: protocol { show2 :: () -> string; } +Show2 :: protocol { show2 :: (self: *Self) -> string; } IntBox :: struct { v: i64; } impl Show2 for IntBox { show2 :: (self: *IntBox) -> string { "x" } diff --git a/examples/1190-diagnostics-protocol-missing-receiver.sx b/examples/1190-diagnostics-protocol-missing-receiver.sx new file mode 100644 index 00000000..d45c9861 --- /dev/null +++ b/examples/1190-diagnostics-protocol-missing-receiver.sx @@ -0,0 +1,12 @@ +// A protocol method that omits the explicit receiver (the old implicit form) +// is now a parse error — the receiver `self: *Self`/`self: Self` is required as +// the first parameter. This guards the diagnostic. +#import "modules/std.sx"; + +Show :: protocol { + show :: () -> string; // ERROR: missing `self` receiver +} + +main :: () -> i32 { + return 0; +} diff --git a/examples/expected/0418-protocols-explicit-receiver.exit b/examples/expected/0418-protocols-explicit-receiver.exit new file mode 100644 index 00000000..573541ac --- /dev/null +++ b/examples/expected/0418-protocols-explicit-receiver.exit @@ -0,0 +1 @@ +0 diff --git a/examples/expected/0418-protocols-explicit-receiver.stderr b/examples/expected/0418-protocols-explicit-receiver.stderr new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/examples/expected/0418-protocols-explicit-receiver.stderr @@ -0,0 +1 @@ + diff --git a/examples/expected/0418-protocols-explicit-receiver.stdout b/examples/expected/0418-protocols-explicit-receiver.stdout new file mode 100644 index 00000000..4a632468 --- /dev/null +++ b/examples/expected/0418-protocols-explicit-receiver.stdout @@ -0,0 +1,3 @@ +measure=5x10 +scaled=15x30 +name=widget diff --git a/examples/expected/1190-diagnostics-protocol-missing-receiver.exit b/examples/expected/1190-diagnostics-protocol-missing-receiver.exit new file mode 100644 index 00000000..d00491fd --- /dev/null +++ b/examples/expected/1190-diagnostics-protocol-missing-receiver.exit @@ -0,0 +1 @@ +1 diff --git a/examples/expected/1190-diagnostics-protocol-missing-receiver.stderr b/examples/expected/1190-diagnostics-protocol-missing-receiver.stderr new file mode 100644 index 00000000..f3156483 --- /dev/null +++ b/examples/expected/1190-diagnostics-protocol-missing-receiver.stderr @@ -0,0 +1,5 @@ +error: protocol method must declare its receiver as the first parameter: `self: *Self` (or `self: Self`) + --> examples/1190-diagnostics-protocol-missing-receiver.sx:7:16 + | + 7 | show :: () -> string; // ERROR: missing `self` receiver + | ^^ diff --git a/examples/expected/1190-diagnostics-protocol-missing-receiver.stdout b/examples/expected/1190-diagnostics-protocol-missing-receiver.stdout new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/examples/expected/1190-diagnostics-protocol-missing-receiver.stdout @@ -0,0 +1 @@ + diff --git a/library/modules/gpu/api.sx b/library/modules/gpu/api.sx index 33e5ba24..2d219761 100644 --- a/library/modules/gpu/api.sx +++ b/library/modules/gpu/api.sx @@ -11,47 +11,47 @@ GPU :: protocol { // Bind the GPU to a backend-specific render target (e.g. a // CAMetalLayer on iOS). pixel_w/pixel_h are the drawable's pixel // dimensions; call resize when they change. - init :: (target: *void, pixel_w: i32, pixel_h: i32) -> bool; - shutdown :: (); + init :: (self: *Self, target: *void, pixel_w: i32, pixel_h: i32) -> bool; + shutdown :: (self: *Self); - resize :: (pixel_w: i32, pixel_h: i32); + resize :: (self: *Self, pixel_w: i32, pixel_h: i32); - begin_frame :: (clear: ClearColor) -> bool; + begin_frame :: (self: *Self, clear: ClearColor) -> bool; // target_time is the host clock time at which the drawable should be // presented (units match the platform's CADisplayLink.targetTimestamp // on Apple). Metal forwards it to presentDrawable:atTime: to cap the // pipeline at one frame so the inset slide lands on the same vsync as // UIKit's keyboard view. GL backends ignore it. - end_frame :: (target_time: f64); + end_frame :: (self: *Self, target_time: f64); - create_shader :: (vsrc: string, fsrc: string) -> ShaderHandle; - create_buffer :: (size_bytes: i64) -> BufferHandle; - update_buffer :: (buf: BufferHandle, data: *void, size_bytes: i64); + create_shader :: (self: *Self, vsrc: string, fsrc: string) -> ShaderHandle; + create_buffer :: (self: *Self, size_bytes: i64) -> BufferHandle; + update_buffer :: (self: *Self, buf: BufferHandle, data: *void, size_bytes: i64); // Sub-buffer write at a byte offset. Required for Metal where re-using // the same buffer slice across multiple draws in a single command // encoder is a race: the GPU executes draws asynchronously and reads // shared-storage buffer contents at execution time, so the LAST writer // wins if every flush targets offset 0. Renderers that issue more than // one draw per frame must advance their write offset between flushes. - update_buffer_at :: (buf: BufferHandle, data: *void, size_bytes: i64, byte_offset: i64); - create_texture :: (w: i32, h: i32, format: TextureFormat, pixels: *void) -> TextureHandle; - update_texture_region :: (tex: TextureHandle, x: i32, y: i32, w: i32, h: i32, pixels: *void); + update_buffer_at :: (self: *Self, buf: BufferHandle, data: *void, size_bytes: i64, byte_offset: i64); + create_texture :: (self: *Self, w: i32, h: i32, format: TextureFormat, pixels: *void) -> TextureHandle; + update_texture_region :: (self: *Self, tex: TextureHandle, x: i32, y: i32, w: i32, h: i32, pixels: *void); // Release a GPU resource. Implementations release the backing object and // null the slot so the handle becomes inert. Calling with handle 0 or // an already-destroyed handle is a no-op. Handles are not re-used; the // backing List entry stays at its index with a null sentinel. - destroy_shader :: (sh: ShaderHandle); - destroy_buffer :: (buf: BufferHandle); - destroy_texture :: (tex: TextureHandle); + destroy_shader :: (self: *Self, sh: ShaderHandle); + destroy_buffer :: (self: *Self, buf: BufferHandle); + destroy_texture :: (self: *Self, tex: TextureHandle); - set_shader :: (sh: ShaderHandle); - set_vertex_buffer :: (buf: BufferHandle); - set_texture :: (slot: u32, tex: TextureHandle); - set_vertex_constants :: (slot: u32, data: *void, size_bytes: i64); - set_scissor :: (x: i32, y: i32, w: i32, h: i32); - disable_scissor :: (); + set_shader :: (self: *Self, sh: ShaderHandle); + set_vertex_buffer :: (self: *Self, buf: BufferHandle); + set_texture :: (self: *Self, slot: u32, tex: TextureHandle); + set_vertex_constants :: (self: *Self, slot: u32, data: *void, size_bytes: i64); + set_scissor :: (self: *Self, x: i32, y: i32, w: i32, h: i32); + disable_scissor :: (self: *Self); - draw_triangles :: (vertex_offset: i32, vertex_count: i32); + draw_triangles :: (self: *Self, vertex_offset: i32, vertex_count: i32); } diff --git a/library/modules/platform/api.sx b/library/modules/platform/api.sx index 7edf0dd3..b0a476ef 100644 --- a/library/modules/platform/api.sx +++ b/library/modules/platform/api.sx @@ -4,24 +4,24 @@ #import "modules/platform/types.sx"; Platform :: protocol { - init :: (title: [:0]u8, w: i32, h: i32) -> bool; + init :: (self: *Self, title: [:0]u8, w: i32, h: i32) -> bool; - run_frame_loop :: (frame_fn: Closure()); + run_frame_loop :: (self: *Self, frame_fn: Closure()); - poll_events :: () -> []Event; + poll_events :: (self: *Self) -> []Event; - begin_frame :: () -> FrameContext; - end_frame :: (); + begin_frame :: (self: *Self) -> FrameContext; + end_frame :: (self: *Self); - safe_insets :: () -> EdgeInsets; - keyboard :: () -> KeyboardState; - show_keyboard :: (); - hide_keyboard :: (); + safe_insets :: (self: *Self) -> EdgeInsets; + keyboard :: (self: *Self) -> KeyboardState; + show_keyboard :: (self: *Self); + hide_keyboard :: (self: *Self); // Request the run loop to stop. On iOS/Android this is a no-op // (mobile apps don't quit on user request); on SDL it tears down the // `while !quit` loop. - stop :: (); + stop :: (self: *Self); - shutdown :: (); + shutdown :: (self: *Self); } diff --git a/library/modules/std/core.sx b/library/modules/std/core.sx index 6c1dbf36..ce6fa5d8 100644 --- a/library/modules/std/core.sx +++ b/library/modules/std/core.sx @@ -60,8 +60,8 @@ string :: []u8 #builtin; // Bytes-level primitives carry the `_bytes` suffix so the typed // helpers in std/mem.sx own the bare names (`alloc(T, n)`, `free(s)`). Allocator :: protocol #inline { - alloc_bytes :: (size: i64) -> *void; - dealloc_bytes :: (ptr: *void); + alloc_bytes :: (self: *Self, size: i64) -> *void; + dealloc_bytes :: (self: *Self, ptr: *void); } // --- Io capability protocol (impls live in std/io.sx) --- @@ -98,12 +98,12 @@ ParkToken :: struct { } Io :: protocol #inline { - spawn_raw :: (entry: *void, arg: *void, opts: SpawnOpts) -> *void; - suspend_raw :: (park: ParkToken) -> !; - ready :: (park: ParkToken); - poll :: (deadline_ms: i64) -> i64; - now_ms :: () -> i64; - arm_timer :: (deadline_ms: i64, park: ParkToken) -> *void; + spawn_raw :: (self: *Self, entry: *void, arg: *void, opts: SpawnOpts) -> *void; + suspend_raw :: (self: *Self, park: ParkToken) -> !; + ready :: (self: *Self, park: ParkToken); + poll :: (self: *Self, deadline_ms: i64) -> i64; + now_ms :: (self: *Self) -> i64; + arm_timer :: (self: *Self, deadline_ms: i64, park: ParkToken) -> *void; } // --- Context --- @@ -127,5 +127,5 @@ Context :: struct { // and emits a direct call. Compile-time only — no vtable, no runtime // dispatch. Into :: protocol(Target: Type) { - convert :: () -> Target; + convert :: (self: *Self) -> Target; } diff --git a/library/modules/ui/animation.sx b/library/modules/ui/animation.sx index b1b4c353..c78a75bd 100755 --- a/library/modules/ui/animation.sx +++ b/library/modules/ui/animation.sx @@ -4,7 +4,7 @@ // --- Lerpable protocol (inline — static dispatch, no vtable) --- Lerpable :: protocol #inline { - lerp :: (b: Self, t: f32) -> Self; + lerp :: (self: *Self, b: Self, t: f32) -> Self; } // --- Easing Functions --- diff --git a/library/modules/ui/view.sx b/library/modules/ui/view.sx index cd94e335..09051b26 100755 --- a/library/modules/ui/view.sx +++ b/library/modules/ui/view.sx @@ -4,16 +4,16 @@ View :: protocol { // Measure: given a size proposal, return desired size - size_that_fits :: (proposal: ProposedSize) -> Size; + size_that_fits :: (self: *Self, proposal: ProposedSize) -> Size; // Place: position children within the given bounds - layout :: (bounds: Frame); + layout :: (self: *Self, bounds: Frame); // Render: emit render nodes - render :: (ctx: *RenderContext, frame: Frame); + render :: (self: *Self, ctx: *RenderContext, frame: Frame); // Event handling: return true if the event was consumed - handle_event :: (event: *Event, frame: Frame) -> bool; + handle_event :: (self: *Self, event: *Event, frame: Frame) -> bool; } // A child view with its computed frame (set during layout) diff --git a/readme.md b/readme.md index 28528f34..470fee2d 100644 --- a/readme.md +++ b/readme.md @@ -184,7 +184,7 @@ function declaration, an `impl` method definition, or a `::` type declaration (`i2 :: 5` and `i2 :: (n) { … }` are rejected just like `i2 := 5`). **Member-name positions are exempt**: a struct *field*, a union *tag*, and a protocol *method-signature* may be a bare reserved spelling (`struct { i2: i64 }`, -`union { u8: … }`, `protocol { i2 :: () -> i64 }`) — they are reached via `obj.name`, +`union { u8: … }`, `protocol { i2 :: (self: *Self) -> i64 }`) — they are reached via `obj.name`, so they never mis-lower. The bare exemption covers only the identifier-classified reserved names (`i1`..`i64`, `u1`..`u64`, `bool`, `string`, `void`, `usize`, `isize`, `Any`); `f32` and `f64` are lexer keywords, so even in a member slot they @@ -334,7 +334,7 @@ Closures capture by value. Bare functions auto-promote to closures when needed. ```sx Drawable :: protocol { - draw :: (x: i32, y: i32); + draw :: (self: *Self, x: i32, y: i32); // receiver is explicit + required } impl Drawable for Circle { @@ -345,11 +345,15 @@ shape : Drawable = xx my_circle; // type erasure via xx shape.draw(10, 20); // dynamic dispatch ``` +Every protocol method declares its receiver explicitly as the first parameter +(`self: *Self` or `self: Self`), matching the `impl` signature; the annotation is +erased before dispatch, so the call site is unchanged. + `#inline` protocols store function pointers directly (no vtable indirection): ```sx Allocator :: protocol #inline { - alloc :: (size: i64) -> *void; - dealloc :: (ptr: *void); + alloc :: (self: *Self, size: i64) -> *void; + dealloc :: (self: *Self, ptr: *void); } ``` diff --git a/specs.md b/specs.md index 8bf3ddbe..e310aeec 100644 --- a/specs.md +++ b/specs.md @@ -45,7 +45,7 @@ reserved spellings — `i1`..`i64`, `u1`..`u64`, `bool`, `string`, `cstring`, `v member-name slots require an identifier token; a bare `f32` / `f64` is therefore rejected at parse (`expected field name in struct`) even in a member position. Use the backtick there too — `` struct { `f32: i64; } `` / `` union { `f64: … } `` / -`` protocol { `f32 :: () -> i64; } `` work as field / tag / method names. +`` protocol { `f32 :: (self: *Self) -> i64; } `` work as field / tag / method names. ```sx i2 := 2.5; // ERROR: 'i2' is a reserved type name and cannot be used as an identifier @@ -563,12 +563,12 @@ Protocols define a set of method signatures that types can implement. They enabl #### Declaration ```sx Allocator :: protocol #inline { - alloc :: (size: i64) -> *void; - dealloc :: (ptr: *void); + alloc :: (self: *Self, size: i64) -> *void; + dealloc :: (self: *Self, ptr: *void); } ``` -Protocol methods have an **implicit receiver** — no `self` in the protocol signature. The compiler adds `*Self` automatically. The `#inline` modifier embeds function pointers directly in the protocol value (no vtable indirection). +Protocol methods declare their receiver **explicitly** as the first parameter — `self: *Self` (or `self: Self`) — matching the corresponding `impl` method signature. This is **required**: a protocol method whose first parameter is not `self: *Self`/`self: Self` is a parse error. (It removes the old implicit-receiver ambiguity over whether the first listed parameter was the receiver or an extra argument.) The receiver annotation is validated then erased — the dispatch ABI is unchanged, so existing `impl` blocks and call sites are unaffected. The `#inline` modifier embeds function pointers directly in the protocol value (no vtable indirection). #### `#inline` vs default layout @@ -675,8 +675,8 @@ s : Sizable = xx @w; // identical to `xx w` — borrows w Protocol methods can have bodies. `self` dispatches through the vtable (dynamic dispatch): ```sx Writer :: protocol { - write :: (data: string) -> i64; // required - write_line :: (data: string) -> i64 { // default + write :: (self: *Self, data: string) -> i64; // required + write_line :: (self: *Self, data: string) -> i64 { // default n := self.write(data); n + self.write("\n"); } @@ -688,7 +688,7 @@ Default methods are used unless overridden in the impl. Default methods calling #### `Self` Type `Self` is a contextual keyword in protocol declarations — resolves to the concrete type in impls: ```sx -Eq :: protocol { eq :: (other: Self) -> bool; } +Eq :: protocol { eq :: (self: *Self, other: Self) -> bool; } impl Eq for Point { eq :: (self: *Point, other: Point) -> bool { @@ -755,7 +755,7 @@ in `modules/std.sx`: ```sx Into :: protocol(Target: Type) { - convert :: () -> Target; + convert :: (self: *Self) -> Target; } ``` diff --git a/src/parser.zig b/src/parser.zig index dff7dca2..3b27d812 100644 --- a/src/parser.zig +++ b/src/parser.zig @@ -1223,6 +1223,27 @@ pub const Parser = struct { } try self.expect(.r_paren); + // Every protocol method must declare its receiver EXPLICITLY as the + // first parameter — `self: *Self` (or `self: Self`) — matching how + // `impl` methods and ordinary methods are written. This removes the + // old implicit-receiver ambiguity (was the first listed param the + // receiver, or an extra arg?). The receiver is validated and then + // stripped here, so downstream lowering sees only the EXTRA-arg + // params, exactly as it did under the implicit form. + if (param_names.items.len == 0 or !std.mem.eql(u8, param_names.items[0], "self")) { + return self.fail("protocol method must declare its receiver as the first parameter: `self: *Self` (or `self: Self`)"); + } + { + const rtype = param_types.items[0]; + const is_self_val = rtype.data == .type_expr and std.mem.eql(u8, rtype.data.type_expr.name, "Self"); + const is_self_ptr = rtype.data == .pointer_type_expr and + rtype.data.pointer_type_expr.pointee_type.data == .type_expr and + std.mem.eql(u8, rtype.data.pointer_type_expr.pointee_type.data.type_expr.name, "Self"); + if (!is_self_val and !is_self_ptr) { + return self.fail("protocol method receiver must be typed `*Self` or `Self`"); + } + } + // Optional return type var return_type: ?*Node = null; if (self.current.tag == .arrow) { @@ -1238,12 +1259,19 @@ pub const Parser = struct { if (self.current.tag == .semicolon) self.advance(); } + // Strip the receiver (index 0) — the method's stored params are the + // extra args only. + const all_param_types = try param_types.toOwnedSlice(self.allocator); + const all_param_names = try param_names.toOwnedSlice(self.allocator); + const all_param_name_spans = try param_name_spans.toOwnedSlice(self.allocator); + const all_param_name_is_raw = try param_name_is_raw.toOwnedSlice(self.allocator); + try methods.append(self.allocator, .{ .name = method_name, - .params = try param_types.toOwnedSlice(self.allocator), - .param_names = try param_names.toOwnedSlice(self.allocator), - .param_name_spans = try param_name_spans.toOwnedSlice(self.allocator), - .param_name_is_raw = try param_name_is_raw.toOwnedSlice(self.allocator), + .params = all_param_types[1..], + .param_names = all_param_names[1..], + .param_name_spans = all_param_name_spans[1..], + .param_name_is_raw = all_param_name_is_raw[1..], .return_type = return_type, .default_body = default_body, }); diff --git a/src/sema.zig b/src/sema.zig index 3f87ac64..2370b411 100644 --- a/src/sema.zig +++ b/src/sema.zig @@ -2205,7 +2205,7 @@ test "sema: method-return slice + .ptr index + tagged-enum element" { const source = "Event :: enum { none; click: i64; }" ++ - "Plat :: protocol { poll :: () -> []Event; }" ++ + "Plat :: protocol { poll :: (self: *Self) -> []Event; }" ++ "go :: (p: *Plat) { evs := p.poll(); e := evs.ptr[0]; }"; var parser = parser_mod.Parser.init(alloc, source); const root = try parser.parse();