feat(lang): backtick raw identifier in every binding form + raw-not-a-type + foreign reserved-name fn bare-call [F0.6]
Completes the issue-0089 backtick raw-identifier / `#import c` exemption across all remaining identifier positions and closes three boundary gaps the F0.6 review found. 1. Exhaustive raw-binding coverage. The `is_raw` bit now threads through `ast.Identifier` and EVERY binding/capture form — `IfExpr`/`WhileExpr` optional bindings, `ForExpr` capture + index, `MatchArm` capture, `CatchExpr`/`OnFailStmt` tag bindings, `DestructureDecl` per-name, and the protocol-default-body / foreign-class method param lists — not just `var_decl`/`param`. `UnknownTypeChecker` skips the reserved-name check at each arm when raw, so a backtick works in every identifier position while a bare reserved spelling still errors (issue 0076 preserved). 2. Raw identifier is never a type. `parseTypeExpr`'s atom rejects a raw identifier in type position (`x : `s2 = 1`, `List(`s2)`) with an accurate diagnostic instead of silently type-classifying it. 3. Reserved-name function bare-callable. A bare `s2(4)` parses its callee as a `.type_expr` (reserved spelling); `lowerCall` now rewrites a type_expr callee to an identifier when a function of that name is in scope, so a backtick-declared sx fn and a `#import c` foreign fn whose C name collides with a reserved type spelling both resolve by their bare name. (`TypeName(val)` is not a cast, so there is no ambiguity.) Tests: examples/0152 (every control-flow/capture form + bare ref/call/member access), examples/1054 (catch/onfail tag bindings), examples/1139 (raw in type position rejected), examples/1220 extended (foreign reserved-name function bare-call). 0076 negatives 1119/1121/1122/1123/1124/1125 stay green. Gate: zig build + zig build test + 422 examples pass. specs.md + readme.md updated; issues/0089 RESOLVED banner refreshed.
This commit is contained in:
@@ -6621,10 +6621,30 @@ pub const Lowering = struct {
|
||||
// ── Calls ───────────────────────────────────────────────────────
|
||||
|
||||
fn lowerCall(self: *Lowering, c_in: *const ast.Call) Ref {
|
||||
var c = c_in;
|
||||
// A bare reserved-type-name spelling in call position parses as a
|
||||
// `.type_expr` (e.g. `s2(4)`), but if a function of that name is in
|
||||
// scope — a backtick-declared sx fn or a `#import c` foreign fn whose C
|
||||
// name collides with a reserved type spelling — it is a CALL to that
|
||||
// function. `TypeName(val)` is not a cast (casts are `cast(T, val)`), so
|
||||
// there is no ambiguity. Rewrite the callee to an identifier so the
|
||||
// normal call machinery resolves it, symmetric to the bare-value
|
||||
// reference that already resolves via scope/globals (issue 0089).
|
||||
if (c.callee.data == .type_expr) {
|
||||
const tname = c.callee.data.type_expr.name;
|
||||
const is_fn = self.program_index.fn_ast_map.contains(tname) or
|
||||
(if (self.scope) |scope| scope.lookupFn(tname) != null else false);
|
||||
if (is_fn) {
|
||||
const id_node = self.alloc.create(Node) catch unreachable;
|
||||
id_node.* = .{ .span = c.callee.span, .data = .{ .identifier = .{ .name = tname, .is_raw = true } } };
|
||||
const rewritten = self.alloc.create(ast.Call) catch unreachable;
|
||||
rewritten.* = .{ .callee = id_node, .args = c.args };
|
||||
c = rewritten;
|
||||
}
|
||||
}
|
||||
// Expand default parameter values for bare identifier callees:
|
||||
// when the caller omits trailing positional args, fill them in
|
||||
// from the callee's `param: T = expr` declarations.
|
||||
var c = c_in;
|
||||
if (self.expandCallDefaults(c)) |expanded| c = expanded;
|
||||
// Check reflection builtins first (before lowering args — some args are type names, not values)
|
||||
if (c.callee.data == .identifier) {
|
||||
|
||||
@@ -121,7 +121,9 @@ pub const UnknownTypeChecker = struct {
|
||||
if (vd.value) |v| self.checkBindingNames(v);
|
||||
},
|
||||
.destructure_decl => |dd| {
|
||||
for (dd.names, dd.name_spans) |n, sp| self.checkBindingName(n, sp);
|
||||
for (dd.names, dd.name_spans, dd.name_is_raw) |n, sp, raw| {
|
||||
if (!raw) self.checkBindingName(n, sp);
|
||||
}
|
||||
self.checkBindingNames(dd.value);
|
||||
},
|
||||
.fn_decl => |fd| {
|
||||
@@ -137,19 +139,25 @@ pub const UnknownTypeChecker = struct {
|
||||
if (p.default_expr) |de| self.checkBindingNames(de);
|
||||
},
|
||||
.if_expr => |ie| {
|
||||
if (ie.binding_name) |bn| self.checkBindingName(bn, ie.binding_span);
|
||||
if (ie.binding_name) |bn| {
|
||||
if (!ie.binding_is_raw) self.checkBindingName(bn, ie.binding_span);
|
||||
}
|
||||
self.checkBindingNames(ie.condition);
|
||||
self.checkBindingNames(ie.then_branch);
|
||||
if (ie.else_branch) |e| self.checkBindingNames(e);
|
||||
},
|
||||
.while_expr => |we| {
|
||||
if (we.binding_name) |bn| self.checkBindingName(bn, we.binding_span);
|
||||
if (we.binding_name) |bn| {
|
||||
if (!we.binding_is_raw) self.checkBindingName(bn, we.binding_span);
|
||||
}
|
||||
self.checkBindingNames(we.condition);
|
||||
self.checkBindingNames(we.body);
|
||||
},
|
||||
.for_expr => |fe| {
|
||||
if (fe.capture_name.len != 0) self.checkBindingName(fe.capture_name, fe.capture_span);
|
||||
if (fe.index_name) |idx| self.checkBindingName(idx, fe.index_span);
|
||||
if (fe.capture_name.len != 0 and !fe.capture_is_raw) self.checkBindingName(fe.capture_name, fe.capture_span);
|
||||
if (fe.index_name) |idx| {
|
||||
if (!fe.index_is_raw) self.checkBindingName(idx, fe.index_span);
|
||||
}
|
||||
self.checkBindingNames(fe.iterable);
|
||||
if (fe.range_end) |re| self.checkBindingNames(re);
|
||||
self.checkBindingNames(fe.body);
|
||||
@@ -157,23 +165,31 @@ pub const UnknownTypeChecker = struct {
|
||||
.match_expr => |me| {
|
||||
self.checkBindingNames(me.subject);
|
||||
for (me.arms) |arm| {
|
||||
if (arm.capture) |cap| self.checkBindingName(cap, arm.capture_span);
|
||||
if (arm.capture) |cap| {
|
||||
if (!arm.capture_is_raw) self.checkBindingName(cap, arm.capture_span);
|
||||
}
|
||||
if (arm.pattern) |p| self.checkBindingNames(p);
|
||||
self.checkBindingNames(arm.body);
|
||||
}
|
||||
},
|
||||
.match_arm => |arm| {
|
||||
if (arm.capture) |cap| self.checkBindingName(cap, arm.capture_span);
|
||||
if (arm.capture) |cap| {
|
||||
if (!arm.capture_is_raw) self.checkBindingName(cap, arm.capture_span);
|
||||
}
|
||||
if (arm.pattern) |p| self.checkBindingNames(p);
|
||||
self.checkBindingNames(arm.body);
|
||||
},
|
||||
.catch_expr => |ce| {
|
||||
if (ce.binding) |b| self.checkBindingName(b, ce.binding_span);
|
||||
if (ce.binding) |b| {
|
||||
if (!ce.binding_is_raw) self.checkBindingName(b, ce.binding_span);
|
||||
}
|
||||
self.checkBindingNames(ce.operand);
|
||||
self.checkBindingNames(ce.body);
|
||||
},
|
||||
.onfail_stmt => |os| {
|
||||
if (os.binding) |b| self.checkBindingName(b, os.binding_span);
|
||||
if (os.binding) |b| {
|
||||
if (!os.binding_is_raw) self.checkBindingName(b, os.binding_span);
|
||||
}
|
||||
self.checkBindingNames(os.body);
|
||||
},
|
||||
// impl / protocol-default / foreign-class method bodies: each
|
||||
@@ -183,13 +199,19 @@ pub const UnknownTypeChecker = struct {
|
||||
.impl_block => |ib| for (ib.methods) |m| self.checkBindingNames(m),
|
||||
.protocol_decl => |pd| for (pd.methods) |m| {
|
||||
if (m.default_body) |body| {
|
||||
for (m.param_names, m.param_name_spans) |pn, sp| self.checkBindingName(pn, sp);
|
||||
for (m.param_names, m.param_name_spans, 0..) |pn, sp, i| {
|
||||
if (i < m.param_name_is_raw.len and m.param_name_is_raw[i]) continue;
|
||||
self.checkBindingName(pn, sp);
|
||||
}
|
||||
self.checkBindingNames(body);
|
||||
}
|
||||
},
|
||||
.foreign_class_decl => |fcd| for (fcd.members) |member| switch (member) {
|
||||
.method => |m| if (m.body) |body| {
|
||||
for (m.param_names, m.param_name_spans) |pn, sp| self.checkBindingName(pn, sp);
|
||||
for (m.param_names, m.param_name_spans, 0..) |pn, sp, i| {
|
||||
if (i < m.param_name_is_raw.len and m.param_name_is_raw[i]) continue;
|
||||
self.checkBindingName(pn, sp);
|
||||
}
|
||||
self.checkBindingNames(body);
|
||||
},
|
||||
.field, .extends, .implements => {},
|
||||
|
||||
Reference in New Issue
Block a user