lang 2.4: protocol-interface method calls on pack elements + conformance fix
Calling a protocol method on a pack element now works: `xs[i].greet()` on a `..xs: Greeter` pack dispatches to the concrete element's impl, and elements may be heterogeneous (Dog, Cat). This is the protocol-interface access the pack is for. (Protocol method decls omit the implicit `self`; impls list it — the earlier malformed `(self: *Self)` decls were why dispatch looked broken.) Also fixes packArgConformsTo for non-parameterised protocols: it queried `protocol_thunk_map`, which is only populated lazily when a protocol VALUE is built with `xx`, so it false-negatived valid conformers. Now it queries impl-declaration state directly — `param_impl_map` for parameterised protocols, or `<ty>.<method>` entries in `fn_ast_map` for non-parameterised ones. examples/193-protocol-pack-methods.sx (heterogeneous Dog+Cat pack, per-element greet(), order-independent).
This commit is contained in:
@@ -3032,27 +3032,44 @@ pub const Lowering = struct {
|
||||
return self.protocol_thunk_map.contains(thunk_key);
|
||||
}
|
||||
|
||||
/// Does `ty` conform to protocol `p_name` under SOME type-args? Used to
|
||||
/// check protocol-pack elements (`..xs: P`), where each element's
|
||||
/// protocol type-args are inferred from its impl rather than written out.
|
||||
/// Covers plain protocols (`protocol_thunk_map`) and parameterised ones
|
||||
/// (any `param_impl_map` key `P\x00<args>\x00<mangle(ty)>`). An arg already
|
||||
/// of the protocol's own (erased) type trivially conforms.
|
||||
/// Does `ty` conform to protocol `p_name` (under SOME type-args for a
|
||||
/// parameterised protocol)? Used to check protocol-pack elements
|
||||
/// (`..xs: P`), where each element's protocol type-args are inferred from
|
||||
/// its impl rather than written out.
|
||||
///
|
||||
/// Conformance is queried at the IMPL-DECLARATION level (not via
|
||||
/// `protocol_thunk_map`, which is only populated lazily when a protocol
|
||||
/// VALUE is created with `xx`):
|
||||
/// - Parameterised `P`: any `param_impl_map` key `P\x00<args>\x00<mangle(ty)>`.
|
||||
/// - Non-parameterised `P`: every required (non-default) method `m` is
|
||||
/// registered as `<ty>.<m>` in `fn_ast_map` (how `registerImplBlock`
|
||||
/// records a non-parameterised impl).
|
||||
/// An arg already of the protocol's own (erased) type trivially conforms.
|
||||
fn packArgConformsTo(self: *Lowering, p_name: []const u8, ty: TypeId) bool {
|
||||
if (self.hasImplPlain(p_name, ty)) return true;
|
||||
// Arg already erased to the protocol struct itself (e.g. `xx a`).
|
||||
if (!ty.isBuiltin()) {
|
||||
const info = self.module.types.get(ty);
|
||||
if (info == .@"struct" and info.@"struct".is_protocol and
|
||||
std.mem.eql(u8, self.module.types.getString(info.@"struct".name), p_name)) return true;
|
||||
}
|
||||
const prefix = std.fmt.allocPrint(self.alloc, "{s}\x00", .{p_name}) catch return false;
|
||||
const suffix = std.fmt.allocPrint(self.alloc, "\x00{s}", .{self.mangleTypeName(ty)}) catch return false;
|
||||
var it = self.param_impl_map.keyIterator();
|
||||
while (it.next()) |k| {
|
||||
if (std.mem.startsWith(u8, k.*, prefix) and std.mem.endsWith(u8, k.*, suffix)) return true;
|
||||
const pd = self.protocol_ast_map.get(p_name) orelse return false;
|
||||
if (pd.type_params.len > 0) {
|
||||
const prefix = std.fmt.allocPrint(self.alloc, "{s}\x00", .{p_name}) catch return false;
|
||||
const suffix = std.fmt.allocPrint(self.alloc, "\x00{s}", .{self.mangleTypeName(ty)}) catch return false;
|
||||
var it = self.param_impl_map.keyIterator();
|
||||
while (it.next()) |k| {
|
||||
if (std.mem.startsWith(u8, k.*, prefix) and std.mem.endsWith(u8, k.*, suffix)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
// Non-parameterised: require each non-default method as `<ty>.<m>`.
|
||||
const ty_name = self.formatTypeName(ty);
|
||||
for (pd.methods) |m| {
|
||||
if (m.default_body != null) continue;
|
||||
const q = std.fmt.allocPrint(self.alloc, "{s}.{s}", .{ ty_name, m.name }) catch return false;
|
||||
if (!self.fn_ast_map.contains(q)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Evaluate a compile-time condition for `inline if`.
|
||||
|
||||
Reference in New Issue
Block a user