Protocol method declarations now declare their receiver explicitly as the first parameter — 'self: *Self' (or 'self: Self') — matching the impl method signature, instead of the old implicit-receiver form where the listed params were only the extra args. That asymmetry repeatedly caused confusion over whether the first param was the receiver or an argument. The parser validates the first param is 'self' typed Self/*Self, then strips it, so all downstream lowering and the dispatch ABI are unchanged (impl blocks and call sites are unaffected). A protocol method missing the receiver is now a parse error. Migrated all 129 protocol method signatures across library + examples (+ one inline-sx test in sema.zig) to the explicit form. Updated specs.md + readme.md. New: examples/0418-protocols-explicit-receiver.sx (feature), examples/1190-diagnostics-protocol-missing-receiver.sx (negative/diagnostic).
49 lines
1.5 KiB
Plaintext
49 lines
1.5 KiB
Plaintext
// Protocol value as a field of a wrapper struct, constructed from a stack
|
|
// local inside a function and appended to a `List`. The payload must be
|
|
// heap-copied so dispatch survives the constructing function returning.
|
|
|
|
#import "modules/std.sx";
|
|
|
|
Sizable :: protocol {
|
|
size :: (self: *Self) -> i64;
|
|
}
|
|
|
|
Widget :: struct { value: i64; }
|
|
impl Sizable for Widget {
|
|
size :: (self: *Widget) -> i64 { self.value }
|
|
}
|
|
|
|
// Wrapper struct with a protocol field (like ViewChild)
|
|
Item :: struct {
|
|
view: Sizable;
|
|
}
|
|
|
|
Container :: struct {
|
|
items: List(Item);
|
|
|
|
add :: (self: *Container, w: Widget) {
|
|
p := w; // local copy
|
|
self.items.append(Item.{ view = p }); // protocol created from stack local `p`
|
|
|
|
// Works here: stack local `p` is still alive
|
|
out("inside add: ");
|
|
print("{}\n", self.items.items[self.items.len - 1].view.size());
|
|
}
|
|
}
|
|
|
|
main :: () -> void {
|
|
c : Container = .{};
|
|
c.add(Widget.{ value = 42 });
|
|
c.add(Widget.{ value = 99 });
|
|
|
|
// BUG: items[0] should return 42, but returns 99 (reads items[1]'s stack slot)
|
|
// Both protocol values point to the same stack address (the `p` local in add())
|
|
r0 := c.items.items[0].view.size();
|
|
r1 := c.items.items[1].view.size();
|
|
print("items[0] = {} (expected 42)\n", r0);
|
|
print("items[1] = {} (expected 99)\n", r1);
|
|
|
|
// With more stack activity between add() and the reads, this crashes
|
|
// (stack memory overwritten by other function calls)
|
|
}
|