feat(lang): reserved-name check covers :: const/fn/type decls + scope call rewrite to raw provenance [F0.6]
A bare reserved-type-name `::` declaration was silently accepted, and the
attempt-2 lowerCall rewrite then made a bare `s2 :: (…) {…}` function callable —
bypassing the backtick rule for handwritten sx. The reserved-name binding check
covered `:=` / typed-local / param / captures but NOT the `::` declaration form.
- ast: `ConstDecl`/`FnDecl` carry `is_raw` + `name_span` threaded from the parser
(parseConstBinding / parseFnDecl, all call sites incl. struct/impl methods).
- semantic_diagnostics: reject a bare reserved spelling at EVERY declaration-name
site — const, function (incl. struct/impl methods), struct/enum/union/error-set,
protocol, foreign-class, ufcs alias, namespaced/library/c-import name. Backtick
(`is_raw`) and the compiler's `#builtin` definition (`string :: []u8 #builtin`)
are the only exemptions; a value whose node is itself a named decl defers to
that node's own check.
- c_import: synthesized foreign fn_decls are `is_raw = true`, so a C function
whose own name collides with a reserved spelling (`int s2(int);`) imports and
bare-calls unedited.
- lower: scope the `.type_expr`→`.identifier` call rewrite to a callee FnDecl of
RAW provenance (`is_raw`) — only a backtick / `#import c` foreign fn can carry a
reserved-name spelling, so a non-raw match never gets rewritten.
- examples: 0153 (positive — backtick `::` const + fn, bare + tick call), 1140
(negative — bare `::` const + fn rejected).
- docs: specs.md + readme.md state the backtick is required at every binding site
including `::` const / function / type declarations; issue 0089 banner updated.
This commit is contained in:
@@ -6630,17 +6630,25 @@ pub const Lowering = struct {
|
||||
// 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).
|
||||
//
|
||||
// Scoped to RAW provenance: only a backtick (`is_raw`) or `#import c`
|
||||
// foreign fn declaration may legally carry a reserved-name spelling
|
||||
// (the decl check rejects every bare reserved-name sx fn). Refusing the
|
||||
// rewrite for a non-raw match keeps a genuine reserved type spelling a
|
||||
// type — belt-and-suspenders should any future path ever reintroduce a
|
||||
// non-raw reserved-name callee.
|
||||
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 eff = if (self.scope) |scope| scope.lookupFn(tname) orelse tname else tname;
|
||||
const fd: ?*const ast.FnDecl = self.program_index.fn_ast_map.get(eff) orelse
|
||||
self.program_index.fn_ast_map.get(tname);
|
||||
if (fd) |decl| if (decl.is_raw) {
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user