refactor(ir): source lowerCall's namespace/value boundary from CallResolver (A3.2 convergence step 3)
lowerCall re-derived the namespace-vs-value (receiver-prepend) decision with a
19-line block duplicating the exact identifier/type_expr + scope/global walk
that CallResolver already owns (objectIsValue, the negation of is_namespace).
This boundary determines whether the receiver is prepended, so it must agree
with the plan's free_fn_ufcs (prepends) vs namespace_fn (does not)
classification from fa59a9d.
Make CallResolver.objectIsValue pub and set
is_namespace = !self.callResolver().objectIsValue(fa.object)
so plan and lowering share one boundary definition and can never drift.
`!objectIsValue` matches the old block case-for-case (non-identifier => value;
identifier/type_expr in scope/global => value; else => namespace), so this is a
behavior-identical substitution.
Deeper switch(plan.kind) routing of lowerCall is intentionally NOT done here: it
is not behavior-preserving as-is. `plan` is typing-only and coarser than
`lowerCall` — its method/namespace arms carry comptime / generic /
generic-template / #compiler / type-constructor dispatch `plan` does not model,
and its value-receiver kinds (struct_method/protocol_dispatch/foreign_instance)
do not gate on objectIsValue, so a type-name receiver (Point.make()) could be
mis-classified vs the namespace/static call lowerCall actually performs. Driving
prepend decisions off plan.kind would mis-prepend; objectIsValue is the correct
single source, hence routing the boundary specifically. PLAN-ARCH A3.2 success
criteria met (shared classifier; no duplicated return-type logic; plan tests;
stable .ir snapshots).
zig build, zig build test, tests/run_examples.sh (357/0) all green.
This commit is contained in:
@@ -413,7 +413,9 @@ pub const CallResolver = struct {
|
||||
/// (so `pkg.fn(...)` is a namespace call). This is exactly the negation of
|
||||
/// `lowerCall`'s `is_namespace`: a non-identifier object is always a value;
|
||||
/// an identifier / type_expr is a value iff it names a local or a global.
|
||||
fn objectIsValue(self: CallResolver, obj: *const Node) bool {
|
||||
/// `pub` so `lowerCall` sources its namespace/value boundary here rather
|
||||
/// than re-deriving it — one definition, shared by typing and lowering.
|
||||
pub fn objectIsValue(self: CallResolver, obj: *const Node) bool {
|
||||
const obj_name: []const u8 = switch (obj.data) {
|
||||
.identifier => |id| id.name,
|
||||
.type_expr => |te| te.name,
|
||||
|
||||
Reference in New Issue
Block a user