Merge branch 'dist-foundation' into flow/sx-foundation/NL.2

This commit is contained in:
agra
2026-06-04 23:13:50 +03:00
78 changed files with 1861 additions and 177 deletions

View File

@@ -6633,10 +6633,38 @@ 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).
//
// 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 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
// 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) {
@@ -11876,8 +11904,8 @@ pub const Lowering = struct {
// type_bridge, which now takes the alias map as an explicit argument
// (the `TypeTable.aliases` borrow is gone, A2.3).
switch (node.data) {
.type_expr => |te| return self.typeResolver().resolveName(te.name),
.identifier => |id| return self.typeResolver().resolveName(id.name),
.type_expr => |te| return self.typeResolver().resolveName(te.name, te.is_raw),
.identifier => |id| return self.typeResolver().resolveName(id.name, id.is_raw),
// A non-spread tuple literal in a type position is a tuple-type
// literal (`(s32, s32)`); validate its elements are types and reject
// non-type elements loudly (issue 0067).
@@ -12041,8 +12069,10 @@ pub const Lowering = struct {
const base_name = if (std.mem.lastIndexOfScalar(u8, pt.name, '.')) |dot| pt.name[dot + 1 ..] else pt.name;
const table = &self.module.types;
// Vector(N, T) — built-in parameterized type
if (std.mem.eql(u8, base_name, "Vector")) {
// Vector(N, T) — built-in parameterized type. A backtick raw base
// (`` `Vector(…) ``) is the LITERAL user type named `Vector`, so it
// skips this intrinsic and resolves through the template map (0089).
if (!pt.is_raw and std.mem.eql(u8, base_name, "Vector")) {
if (pt.args.len == 2) {
const length = self.resolveVectorLane(pt.args[0]) orelse return .unresolved;
const elem = self.resolveTypeWithBindings(pt.args[1]);