Step 3 second slice. Adds two reflection builtins used by pack-fn bodies to branch on type identity / protocol membership at compile time. type_name already existed (lower.zig:8693); reused as-is. type_eq(T1, T2) -> bool structural TypeId equality has_impl(P, T) -> bool T has a reachable impl for P Both are wired through `tryConstBoolCondition` so the inline-if ladder folds them at lower time — `inline if type_eq(...)` / `inline if has_impl(...)` collapse to a single branch with no runtime instructions, perfect for guard-based dispatch inside pack-fn bodies. `has_impl`'s protocol arg accepts two shapes: - plain protocol name: `has_impl(Allocator, CAllocator)` → walks `protocol_thunk_map["Allocator\x00CAllocator"]`. - parameterised call: `has_impl(Into(Block), s64)` → builds the param_impl_map key `"Into\x00Block\x00s64"` and checks containment. The protocol type-args resolve through `resolveTypeArg` so type aliases, generics, and pack-indexed types all work as protocol args. `computeHasImpl` is the shared implementation between the runtime builtin path and the `tryConstBoolCondition` fast path so both branches stay in sync. `examples/168-pack-reflection-intrinsics.sx` exercises every shape: - type_name for primitive types. - type_eq with both equal + unequal cases, including pointer types (s64 vs *s64). - inline-if folding type_eq. - has_impl with a real plain-protocol impl (Allocator/CAllocator → true; Allocator/s64 → false). - has_impl with a user-defined parameterised protocol (Wrap(s64)/s32 → true; mismatched target args → false). 208/208 example tests + `zig build test` green. Caveat: plain-protocol has_impl uses `protocol_thunk_map` which is lazily populated when an `xx` cast or protocol dispatch creates the thunks. For a static check before any dispatch, that could false-negative. Allocator/CAllocator works in 168 because stdlib's startup uses CAllocator through the Allocator protocol — the thunks already exist by the time has_impl runs. A more robust static check (walk fn_ast_map for "<T_name>.<method>" entries against the protocol's method list) is deferred to a follow-up if needed. LSP "undefined variable" warnings on type names in expression position (s64, *s64, Wrap(s64), etc. passed to type_eq / has_impl) are cosmetic — sema doesn't know these intrinsics accept types as args. Tracked separately.
63 lines
2.1 KiB
Plaintext
63 lines
2.1 KiB
Plaintext
// Variadic heterogeneous type packs — step 3: type-reflection
|
|
// intrinsics.
|
|
//
|
|
// Three comptime helpers used by pack-fn bodies to branch on
|
|
// type identity / protocol membership:
|
|
//
|
|
// type_name(T) -> string // display name of T
|
|
// type_eq(T1, T2) -> bool // structural TypeId equality
|
|
// has_impl(P, T) -> bool // T has a reachable impl for P
|
|
//
|
|
// All three fold to compile-time constants and are accepted by
|
|
// `tryConstBoolCondition`, so `inline if type_eq(...)` /
|
|
// `inline if has_impl(...)` collapse to a single branch at lower
|
|
// time — no runtime cost.
|
|
//
|
|
// `has_impl`'s protocol arg accepts both shapes:
|
|
// - plain protocol name: `has_impl(Allocator, CAllocator)`.
|
|
// - parameterised call: `has_impl(Wrap(s64), s32)` — the args
|
|
// match the impl's protocol type-args exactly.
|
|
|
|
#import "modules/std.sx";
|
|
#import "modules/allocators.sx";
|
|
|
|
// 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;
|
|
}
|
|
|
|
impl Wrap(s64) for s32 {
|
|
wrap :: (self: s32) -> s64 => xx self;
|
|
}
|
|
|
|
main :: () -> s32 {
|
|
// type_name — display names.
|
|
print("{} {} {}\n", type_name(s64), type_name(string), type_name(bool));
|
|
|
|
// type_eq — structural equality on TypeIds.
|
|
print("{} {} {} {}\n",
|
|
type_eq(s64, s64),
|
|
type_eq(s64, string),
|
|
type_eq(*s64, *s64),
|
|
type_eq(*s64, *s32));
|
|
|
|
// inline-if folds type_eq at lower time.
|
|
inline if type_eq(s64, s64) {
|
|
print("inline-if folded: same\n");
|
|
} else {
|
|
print("inline-if folded: different\n");
|
|
}
|
|
|
|
// has_impl — plain protocol (Allocator is unary).
|
|
print("Allocator/CAllocator: {}\n", has_impl(Allocator, CAllocator));
|
|
print("Allocator/s64: {}\n", has_impl(Allocator, s64));
|
|
|
|
// has_impl — parameterised protocol (Wrap takes a Target type arg).
|
|
print("Wrap(s64)/s32: {}\n", has_impl(Wrap(s64), s32));
|
|
print("Wrap(s64)/bool: {}\n", has_impl(Wrap(s64), bool));
|
|
print("Wrap(bool)/s32: {}\n", has_impl(Wrap(bool), s32));
|
|
|
|
return 0;
|
|
}
|