wip(E4): partial source-pin + non-transitive flip [stdlib E4 attempt-1 WIP checkpoint]
Incomplete WIP from a worker killed at the 55-min wall (large blast radius: core source-pin + ~8 example migrations + ~10 library module migrations). Committed so the resumed session continues on a clean tree. May not build.
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
|
#import "modules/allocators.sx"; // `Allocator` is non-transitive: name it, import it.
|
||||||
#import "modules/math/math.sx";
|
#import "modules/math/math.sx";
|
||||||
#import "modules/compiler.sx";
|
#import "modules/compiler.sx";
|
||||||
#import "modules/test.sx";
|
#import "modules/test.sx";
|
||||||
|
|||||||
@@ -13,6 +13,9 @@
|
|||||||
// both impl modules.
|
// both impl modules.
|
||||||
|
|
||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
|
// `Wrap` is declared in the shared types module; bare-import visibility is
|
||||||
|
// non-transitive, so naming it here means importing it here (not via impl-a/b).
|
||||||
|
#import "./0411-protocols-impl-duplicate-types.sx";
|
||||||
#import "./0411-protocols-impl-duplicate-impl-a.sx";
|
#import "./0411-protocols-impl-duplicate-impl-a.sx";
|
||||||
#import "./0411-protocols-impl-duplicate-impl-b.sx";
|
#import "./0411-protocols-impl-duplicate-impl-b.sx";
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,9 @@
|
|||||||
// registration tripped: `duplicate impl 'Into' for source 's64'`. Now the flat
|
// registration tripped: `duplicate impl 'Into' for source 's64'`. Now the flat
|
||||||
// decl list also dedups by node identity, so this builds and prints 7.
|
// decl list also dedups by node identity, so this builds and prints 7.
|
||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
|
// `Wrapped` lives in the shared `common.sx`; bare-import visibility is
|
||||||
|
// non-transitive, so naming it here means importing it here (not via mid_a/b).
|
||||||
|
#import "0709-modules-issue-0056/common.sx";
|
||||||
#import "0709-modules-issue-0056/mid_a.sx";
|
#import "0709-modules-issue-0056/mid_a.sx";
|
||||||
#import "0709-modules-issue-0056/mid_b.sx";
|
#import "0709-modules-issue-0056/mid_b.sx";
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
// and freed in one `deinit`; the writer path allocates nothing.
|
// and freed in one `deinit`; the writer path allocates nothing.
|
||||||
|
|
||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
|
#import "modules/allocators.sx"; // `Allocator` is non-transitive: name it, import it.
|
||||||
#import "modules/std/json.sx";
|
#import "modules/std/json.sx";
|
||||||
#import "modules/fs.sx";
|
#import "modules/fs.sx";
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
// `JsonParseError` variant on the error channel, never a bogus value.
|
// `JsonParseError` variant on the error channel, never a bogus value.
|
||||||
|
|
||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
|
#import "modules/allocators.sx"; // `Allocator` is non-transitive: name it, import it.
|
||||||
#import "modules/std/json.sx";
|
#import "modules/std/json.sx";
|
||||||
|
|
||||||
// Canonical document: no insignificant whitespace, escapes in the writer's
|
// Canonical document: no insignificant whitespace, escapes in the writer's
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
// and decoded strings go through `alloc`, and the writer allocates nothing.
|
// and decoded strings go through `alloc`, and the writer allocates nothing.
|
||||||
|
|
||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
|
#import "modules/allocators.sx"; // `Allocator` is non-transitive: name it, import it.
|
||||||
#import "modules/std/json.sx";
|
#import "modules/std/json.sx";
|
||||||
|
|
||||||
// The writer's EXACT output for the PART A document (insertion order,
|
// The writer's EXACT output for the PART A document (insertion order,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
// independent identities.
|
// independent identities.
|
||||||
|
|
||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
|
#import "modules/allocators.sx"; // `Allocator` is non-transitive: name it, import it.
|
||||||
// `cli` is imported BOTH flat (so its types — `FlagSpec` / `Command` / `Diag` —
|
// `cli` is imported BOTH flat (so its types — `FlagSpec` / `Command` / `Diag` —
|
||||||
// are bare-visible) AND namespaced (so the same-name `cli.parse` stays a
|
// are bare-visible) AND namespaced (so the same-name `cli.parse` stays a
|
||||||
// distinct qualified identity from `json.parse`). Post-E1 a bare reference to a
|
// distinct qualified identity from `json.parse`). Post-E1 a bare reference to a
|
||||||
|
|||||||
20
examples/0763-modules-import-type-non-transitive.sx
Normal file
20
examples/0763-modules-import-type-non-transitive.sx
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
// `#import` is non-transitive for TYPES, exactly like values/functions (0706):
|
||||||
|
// when A imports B and B imports C, A must NOT see C's top-level TYPE names.
|
||||||
|
// This file imports `b.sx` (which in turn imports `c.sx`) and then references
|
||||||
|
// C's type `COnly` directly — the compiler rejects it with a
|
||||||
|
// "type ... is not visible; #import the module that declares it" diagnostic.
|
||||||
|
//
|
||||||
|
// `b.sx` ↔ `c.sx` together still compile: `b_make`'s return type `COnly`
|
||||||
|
// resolves because b.sx directly imports c.sx.
|
||||||
|
//
|
||||||
|
// Regression (Phase E4): before the bare-TYPE gate went single-hop this
|
||||||
|
// 2-flat-hop type was wrongly visible (the interim transitive closure).
|
||||||
|
|
||||||
|
#import "modules/std.sx";
|
||||||
|
#import "0763-modules-import-type-non-transitive/b.sx";
|
||||||
|
|
||||||
|
main :: () -> s32 {
|
||||||
|
x : COnly = .{ v = 5 };
|
||||||
|
print("{}\n", x.v);
|
||||||
|
0
|
||||||
|
}
|
||||||
7
examples/0763-modules-import-type-non-transitive/b.sx
Normal file
7
examples/0763-modules-import-type-non-transitive/b.sx
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#import "c.sx";
|
||||||
|
|
||||||
|
// b.sx directly imports c.sx, so it CAN name `COnly` — proving the type is
|
||||||
|
// only one flat hop away here, two hops away from a file that imports b.sx.
|
||||||
|
b_make :: () -> COnly {
|
||||||
|
.{ v = 99 }
|
||||||
|
}
|
||||||
1
examples/0763-modules-import-type-non-transitive/c.sx
Normal file
1
examples/0763-modules-import-type-non-transitive/c.sx
Normal file
@@ -0,0 +1 @@
|
|||||||
|
COnly :: struct { v: s64 = 0; }
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
// whether the recovery happens BEFORE or AFTER the first dispatch.
|
// whether the recovery happens BEFORE or AFTER the first dispatch.
|
||||||
|
|
||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
|
#import "modules/allocators.sx"; // `Allocator` is non-transitive: name it, import it.
|
||||||
|
|
||||||
main :: () -> s32 {
|
main :: () -> s32 {
|
||||||
gpa := GPA.init();
|
gpa := GPA.init();
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
// the operand's storage, so it never allocates and never reaches this
|
// the operand's storage, so it never allocates and never reaches this
|
||||||
// path. See specs.md §3 — Protocol value ownership and lifetime.
|
// path. See specs.md §3 — Protocol value ownership and lifetime.
|
||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
|
#import "modules/allocators.sx"; // `Allocator` is non-transitive: name it, import it.
|
||||||
|
|
||||||
Tracer :: struct {
|
Tracer :: struct {
|
||||||
count: s64;
|
count: s64;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
error: duplicate xx conversion from 's64' to 'Wrap': impls in /Users/agra/projects/sx/examples/./0411-protocols-impl-duplicate-impl-a.sx and /Users/agra/projects/sx/examples/./0411-protocols-impl-duplicate-impl-b.sx
|
error: duplicate xx conversion from 's64' to 'Wrap': impls in /Users/agra/projects/sx/examples/./0411-protocols-impl-duplicate-impl-a.sx and /Users/agra/projects/sx/examples/./0411-protocols-impl-duplicate-impl-b.sx
|
||||||
--> /Users/agra/projects/sx/examples/0411-protocols-impl-duplicate.sx:20:17
|
--> /Users/agra/projects/sx/examples/0411-protocols-impl-duplicate.sx:23:17
|
||||||
|
|
|
|
||||||
20 | w : Wrap = xx 7;
|
23 | w : Wrap = xx 7;
|
||||||
| ^
|
| ^
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
1
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
error: type 'COnly' is not visible; #import the module that declares it
|
||||||
|
--> /Users/agra/projects/sx/examples/0763-modules-import-type-non-transitive.sx:17:9
|
||||||
|
|
|
||||||
|
17 | x : COnly = .{ v = 5 };
|
||||||
|
| ^^^^^
|
||||||
@@ -21,6 +21,7 @@
|
|||||||
// the Metal backend takes — caller branches on OS).
|
// the Metal backend takes — caller branches on OS).
|
||||||
|
|
||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
|
#import "modules/allocators.sx";
|
||||||
#import "modules/compiler.sx";
|
#import "modules/compiler.sx";
|
||||||
#import "modules/opengl.sx";
|
#import "modules/opengl.sx";
|
||||||
#import "modules/gpu/types.sx";
|
#import "modules/gpu/types.sx";
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
// non-iOS targets don't reach the Metal-touching code paths.
|
// non-iOS targets don't reach the Metal-touching code paths.
|
||||||
|
|
||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
|
#import "modules/allocators.sx";
|
||||||
#import "modules/std/objc.sx";
|
#import "modules/std/objc.sx";
|
||||||
#import "modules/compiler.sx";
|
#import "modules/compiler.sx";
|
||||||
#import "modules/gpu/types.sx";
|
#import "modules/gpu/types.sx";
|
||||||
|
|||||||
@@ -58,6 +58,9 @@
|
|||||||
// =====================================================================
|
// =====================================================================
|
||||||
|
|
||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
|
// `Array`/`Object` methods take an explicit `alloc: Allocator`; bare-import
|
||||||
|
// visibility is non-transitive, so the module that names the type imports it.
|
||||||
|
#import "modules/allocators.sx";
|
||||||
#import "modules/fs.sx";
|
#import "modules/fs.sx";
|
||||||
|
|
||||||
// The writer's failure contract: a too-small caller buffer (Overflow) or
|
// The writer's failure contract: a too-small caller buffer (Overflow) or
|
||||||
|
|||||||
@@ -22,6 +22,12 @@
|
|||||||
// caller returns. If you need that, ship a `Block_copy`-backed sibling
|
// caller returns. If you need that, ship a `Block_copy`-backed sibling
|
||||||
// API and use it instead.
|
// API and use it instead.
|
||||||
|
|
||||||
|
// `build_block_convert` (below) is a comptime metaprogram that emits sx source
|
||||||
|
// with `concat` / `int_to_string`; those live in std.sx. A metaprogram body
|
||||||
|
// resolves its bare names in its OWN module (issue 0106), so this module must
|
||||||
|
// import std.sx itself rather than relying on the call site's visibility.
|
||||||
|
#import "modules/std.sx";
|
||||||
|
|
||||||
// Standard 32-byte block header plus two trailing slots for the sx closure
|
// Standard 32-byte block header plus two trailing slots for the sx closure
|
||||||
// it wraps. Total = 48 bytes.
|
// it wraps. Total = 48 bytes.
|
||||||
Block :: struct {
|
Block :: struct {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
|
#import "modules/allocators.sx";
|
||||||
#import "modules/math";
|
#import "modules/math";
|
||||||
#import "modules/ui/types.sx";
|
#import "modules/ui/types.sx";
|
||||||
#import "modules/ui/render.sx";
|
#import "modules/ui/render.sx";
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
|
#import "modules/allocators.sx";
|
||||||
#import "modules/opengl.sx";
|
#import "modules/opengl.sx";
|
||||||
#import "modules/gpu/types.sx";
|
#import "modules/gpu/types.sx";
|
||||||
#import "modules/gpu/api.sx";
|
#import "modules/gpu/api.sx";
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
#import "modules/allocators.sx";
|
#import "modules/allocators.sx";
|
||||||
|
#import "modules/ui/glyph_cache.sx"; // `font: GlyphCache` — name it, import it (non-transitive).
|
||||||
#import "modules/opengl.sx";
|
#import "modules/opengl.sx";
|
||||||
#import "modules/gpu/api.sx";
|
#import "modules/gpu/api.sx";
|
||||||
#import "modules/ui/types.sx";
|
#import "modules/ui/types.sx";
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
|
#import "modules/allocators.sx";
|
||||||
|
|
||||||
// --- State(T) — a handle to persistent storage ---
|
// --- State(T) — a handle to persistent storage ---
|
||||||
|
|
||||||
|
|||||||
17
readme.md
17
readme.md
@@ -401,12 +401,17 @@ is rejected; qualify it with a namespaced import (`m :: #import …; m.fn()`).
|
|||||||
|
|
||||||
A **namespaced** import only binds its alias: reach the module's members as
|
A **namespaced** import only binds its alias: reach the module's members as
|
||||||
`m.name`. Bare-name visibility joins over flat (`#import "…"`) imports, never over
|
`m.name`. Bare-name visibility joins over flat (`#import "…"`) imports, never over
|
||||||
a namespaced alias. For **functions and constants** that join is non-transitive: a
|
a namespaced alias. That join is **non-transitive for every bare member kind —
|
||||||
flat import of a flat import is NOT bare-visible (when `A` imports `B` and `B`
|
functions, constants, AND types alike**: a flat import of a flat import is NOT
|
||||||
imports `C`, `A` does not see `C`'s top-level names — qualify them). A bare
|
bare-visible (when `A` imports `B` and `B` imports `C`, `A` does not see `C`'s
|
||||||
reference to a namespaced-only import's member — function, module constant, or
|
top-level names — including its types — so qualify them, or `#import "C"` directly
|
||||||
**type** — is not visible and is rejected (`type 'X' is not visible; #import the
|
if you reference them). A bare reference to a namespaced-only import's member —
|
||||||
module that declares it`); qualify it as `m.name`.
|
function, module constant, or **type** — is likewise not visible and is rejected
|
||||||
|
(`type 'X' is not visible; #import the module that declares it`); qualify it as
|
||||||
|
`m.name`. (A library's own *internal* type references still resolve: a generic
|
||||||
|
struct / pack fn / protocol body is instantiated in the module that defines it, so
|
||||||
|
e.g. `List(T).append`'s `alloc: Allocator` is visible there regardless of the call
|
||||||
|
site.)
|
||||||
|
|
||||||
### Implicit Context
|
### Implicit Context
|
||||||
|
|
||||||
|
|||||||
@@ -783,6 +783,11 @@ pub const ProtocolDecl = struct {
|
|||||||
/// True when the declared NAME was a backtick raw identifier — exempt from
|
/// True when the declared NAME was a backtick raw identifier — exempt from
|
||||||
/// the reserved-type-name decl check (issue 0089).
|
/// the reserved-type-name decl check (issue 0089).
|
||||||
is_raw: bool = false,
|
is_raw: bool = false,
|
||||||
|
/// Defining module path (stamped by `resolveImports`), so a parameterized
|
||||||
|
/// protocol instantiated cross-module resolves its method signature types in
|
||||||
|
/// the module that declares it (E4 — the protocol analog of
|
||||||
|
/// `StructTemplate.source_file`). Null for a synthesized/sourceless decl.
|
||||||
|
source_file: ?[]const u8 = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const ForeignRuntime = enum {
|
pub const ForeignRuntime = enum {
|
||||||
|
|||||||
@@ -661,14 +661,39 @@ fn reportDuplicateName(diagnostics: ?*errors.DiagnosticList, added: bool, name:
|
|||||||
fn stampFnBodySource(decl: *Node, file_path: []const u8) void {
|
fn stampFnBodySource(decl: *Node, file_path: []const u8) void {
|
||||||
switch (decl.data) {
|
switch (decl.data) {
|
||||||
.fn_decl => |fd| fd.body.source_file = file_path,
|
.fn_decl => |fd| fd.body.source_file = file_path,
|
||||||
|
.struct_decl => |sd| stampStructMethodSources(sd, file_path),
|
||||||
|
// A parameterized protocol is instantiated cross-module; record its
|
||||||
|
// defining path so the instantiation resolves method-signature types in
|
||||||
|
// this module (E4).
|
||||||
|
.protocol_decl => decl.data.protocol_decl.source_file = file_path,
|
||||||
.const_decl => |cd| switch (cd.value.data) {
|
.const_decl => |cd| switch (cd.value.data) {
|
||||||
.fn_decl => |fd| fd.body.source_file = file_path,
|
.fn_decl => |fd| fd.body.source_file = file_path,
|
||||||
|
// `List :: struct { … append :: (…) { … } }` — the methods of a
|
||||||
|
// (possibly generic) struct are monomorphized in their template's
|
||||||
|
// OWN module (issue 0106 + the E4 instantiation source-pin), so their
|
||||||
|
// bodies need the defining path stamped just like a top-level fn.
|
||||||
|
.struct_decl => |sd| stampStructMethodSources(sd, file_path),
|
||||||
|
.protocol_decl => cd.value.data.protocol_decl.source_file = file_path,
|
||||||
else => {},
|
else => {},
|
||||||
},
|
},
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Stamp the defining module path onto every method (and struct-level fn
|
||||||
|
/// constant) body of a struct decl, so a generic-struct method monomorphized at
|
||||||
|
/// a cross-module call site still pins to the module that declares it.
|
||||||
|
fn stampStructMethodSources(sd: ast.StructDecl, file_path: []const u8) void {
|
||||||
|
for (sd.methods) |m| {
|
||||||
|
if (m.data == .fn_decl) m.data.fn_decl.body.source_file = file_path;
|
||||||
|
}
|
||||||
|
for (sd.constants) |c| {
|
||||||
|
if (c.data == .const_decl and c.data.const_decl.value.data == .fn_decl) {
|
||||||
|
c.data.const_decl.value.data.fn_decl.body.source_file = file_path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// `reportDuplicateName` keyed off a node whose `declName()` carries the name
|
/// `reportDuplicateName` keyed off a node whose `declName()` carries the name
|
||||||
/// (the regular authored-decl sites; an `import_decl` has no `declName`, so a
|
/// (the regular authored-decl sites; an `import_decl` has no `declName`, so a
|
||||||
/// namespace alias must use `reportDuplicateName` with the alias directly).
|
/// namespace alias must use `reportDuplicateName` with the alias directly).
|
||||||
|
|||||||
147
src/ir/lower.zig
147
src/ir/lower.zig
@@ -1778,9 +1778,10 @@ pub const Lowering = struct {
|
|||||||
/// the legacy stub there and defers the diagnostic to the checker.
|
/// the legacy stub there and defers the diagnostic to the checker.
|
||||||
undeclared,
|
undeclared,
|
||||||
/// `name` IS a registered named type, but it is reachable from the
|
/// `name` IS a registered named type, but it is reachable from the
|
||||||
/// querying module ONLY through a namespaced import — not bare-visible
|
/// querying module ONLY through a namespaced import (or over more than one
|
||||||
/// over the transitive flat-import closure (the type analog of Phase B's
|
/// flat hop) — not bare-visible over the single-hop direct flat-import set
|
||||||
/// bare-call tightening, F1). The user must qualify it (`ns.Type`).
|
/// (the type analog of Phase B's bare-call tightening, F1). The user must
|
||||||
|
/// qualify it (`ns.Type`) or `#import` the declaring module directly.
|
||||||
/// `resolveNominalLeaf` surfaces the "not visible" diagnostic and returns
|
/// `resolveNominalLeaf` surfaces the "not visible" diagnostic and returns
|
||||||
/// the `.unresolved` poison sentinel — NEVER the global `findByName` match
|
/// the `.unresolved` poison sentinel — NEVER the global `findByName` match
|
||||||
/// (which would leak the type) and NEVER a silent empty-struct stub (which
|
/// (which would leak the type) and NEVER a silent empty-struct stub (which
|
||||||
@@ -1915,17 +1916,15 @@ pub const Lowering = struct {
|
|||||||
// `module_consts_by_source`, never in `type_aliases_by_source`, so it is
|
// `module_consts_by_source`, never in `type_aliases_by_source`, so it is
|
||||||
// correctly excluded too.
|
// correctly excluded too.
|
||||||
//
|
//
|
||||||
// The TYPE reachability here is the TRANSITIVE flat-import closure, NOT the
|
// The TYPE reachability here is SINGLE-HOP — `from`'s own author plus its
|
||||||
// single-hop `collectVisibleAuthors`/`isNameVisible` set the bare VALUE /
|
// DIRECT flat-import edges (`flatTypeAuthorCount`), the same non-transitive
|
||||||
// FUNCTION / CONST leaves use. That asymmetry (types transitive, values
|
// set the bare VALUE / FUNCTION / CONST leaves use (E4, consistent with
|
||||||
// non-transitive — 0706) is the open model-consistency question (R3,
|
// 0706). A library template's INTERNAL type refs (`List.append`'s
|
||||||
// sequenced as E4 per Agra): the value/function model needs the source pin
|
// `alloc: Allocator`) still resolve because every instantiation kind
|
||||||
// for a library template's INTERNAL type refs (`List.append`'s
|
// (generic struct / fn / pack fn / param protocol / type fn) is
|
||||||
// `alloc: Allocator`, instantiated in the caller's source context) before
|
// source-pinned to the template's defining module, so the query
|
||||||
// the type gate can go single-hop too. Until that lands, the transitive
|
// originates THERE — where the type is a direct flat import — not at the
|
||||||
// type closure is the only byte-identical option; the gate stays
|
// cross-module call site.
|
||||||
// type-author-aware and local-safe regardless of which reachability E4
|
|
||||||
// settles on.
|
|
||||||
const name_id = table.internString(name);
|
const name_id = table.internString(name);
|
||||||
const registered = table.findByName(name_id);
|
const registered = table.findByName(name_id);
|
||||||
|
|
||||||
@@ -1956,10 +1955,10 @@ pub const Lowering = struct {
|
|||||||
// `internNamedTypeDecl` adopting that stub when the type registers.
|
// `internNamedTypeDecl` adopting that stub when the type registers.
|
||||||
//
|
//
|
||||||
// The querying source's OWN author wins outright (own-wins, 0105 case
|
// The querying source's OWN author wins outright (own-wins, 0105 case
|
||||||
// 3); otherwise the transitive flat-import closure is searched, and ≥2
|
// 3); otherwise the single-hop direct flat-import set is searched, and
|
||||||
// DISTINCT flat-visible authors → `.ambiguous` (0105 case 4). Single-
|
// ≥2 DISTINCT flat-visible authors → `.ambiguous` (0105 case 4). Single-
|
||||||
// author (E1) keeps ≤1 author across the closure, so this stays byte-
|
// author keeps ≤1 author across that set, so this stays byte-identical
|
||||||
// identical to the legacy leaf.
|
// to the legacy leaf.
|
||||||
if (self.moduleTypeAuthor(from, name)) |author| switch (author) {
|
if (self.moduleTypeAuthor(from, name)) |author| switch (author) {
|
||||||
.alias => |tid| return .{ .resolved = tid },
|
.alias => |tid| return .{ .resolved = tid },
|
||||||
.named => |ref| {
|
.named => |ref| {
|
||||||
@@ -2122,10 +2121,12 @@ pub const Lowering = struct {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// What bare `name`'s type authors look like across the TRANSITIVE
|
/// What bare `name`'s type authors look like across the SINGLE-HOP flat-import
|
||||||
/// flat-import closure of `from` (the querying source's OWN author is consulted
|
/// set of `from` — its DIRECT bare `#import` edges only, NOT the transitive
|
||||||
/// by `selectNominalLeaf` first — own-wins — so this surveys only the
|
/// closure (E4: consistent with the bare VALUE/FUNCTION/CONST leaves and
|
||||||
/// cross-module flat authors):
|
/// example 0706; the interim transitive closure E1 shipped is gone). The
|
||||||
|
/// querying source's OWN author is consulted by `selectNominalLeaf` first
|
||||||
|
/// (own-wins), so this surveys only the cross-module direct-flat authors:
|
||||||
/// - `.ambiguous` — ≥2 DISTINCT resolved TypeIds (issue 0105 case 4);
|
/// - `.ambiguous` — ≥2 DISTINCT resolved TypeIds (issue 0105 case 4);
|
||||||
/// - `.one` — exactly one distinct resolved TypeId;
|
/// - `.one` — exactly one distinct resolved TypeId;
|
||||||
/// - `.unregistered` — ≥1 flat author found but none resolves to a TypeId
|
/// - `.unregistered` — ≥1 flat author found but none resolves to a TypeId
|
||||||
@@ -2135,37 +2136,29 @@ pub const Lowering = struct {
|
|||||||
/// local / leak / forward-alias arms.
|
/// local / leak / forward-alias arms.
|
||||||
/// Distinctness is BY TypeId: each distinct author holds a distinct
|
/// Distinctness is BY TypeId: each distinct author holds a distinct
|
||||||
/// `nominal_id` TypeId, while a diamond import of the SAME module yields the
|
/// `nominal_id` TypeId, while a diamond import of the SAME module yields the
|
||||||
/// same TypeId, so byte-identical de-dup falls out. The closure walk lives in
|
/// same TypeId, so byte-identical de-dup falls out. A library template's
|
||||||
/// `lower.zig`, NOT `resolver.zig` — the single-graph-walk invariant (one
|
/// INTERNAL bare-TYPE refs (a 2-flat-hop type like `List(T).append`'s
|
||||||
/// `flat_import_graph` iterator in `resolver.zig`) is untouched.
|
/// `alloc: Allocator`) stay resolvable because instantiation is source-pinned
|
||||||
|
/// to the template's defining module (E4 #1), so the query originates THERE —
|
||||||
|
/// where the type is a direct flat import — not at the cross-module call site.
|
||||||
|
/// The walk lives in `lower.zig`, NOT `resolver.zig` — the single-graph-walk
|
||||||
|
/// invariant (one `flat_import_graph` iterator in `resolver.zig`) is untouched.
|
||||||
const FlatTypeAuthorCount = union(enum) { none, one: TypeId, ambiguous, unregistered };
|
const FlatTypeAuthorCount = union(enum) { none, one: TypeId, ambiguous, unregistered };
|
||||||
fn flatTypeAuthorCount(self: *Lowering, name: []const u8, from: []const u8) FlatTypeAuthorCount {
|
fn flatTypeAuthorCount(self: *Lowering, name: []const u8, from: []const u8) FlatTypeAuthorCount {
|
||||||
const graph = self.program_index.flat_import_graph orelse return .none;
|
const graph = self.program_index.flat_import_graph orelse return .none;
|
||||||
|
const direct = graph.get(from) orelse return .none;
|
||||||
var found: ?TypeId = null;
|
var found: ?TypeId = null;
|
||||||
var saw_author = false;
|
var saw_author = false;
|
||||||
var visited = std.StringHashMap(void).init(self.alloc);
|
var it = direct.iterator();
|
||||||
defer visited.deinit();
|
while (it.next()) |kv| {
|
||||||
var queue = std.ArrayList([]const u8).empty;
|
const dep = kv.key_ptr.*;
|
||||||
defer queue.deinit(self.alloc);
|
if (self.moduleTypeAuthor(dep, name) != null) {
|
||||||
visited.put(from, {}) catch return .none;
|
saw_author = true;
|
||||||
queue.append(self.alloc, from) catch return .none;
|
if (self.moduleTypeAuthorTid(dep, name)) |tid| {
|
||||||
var i: usize = 0;
|
if (found) |f| {
|
||||||
while (i < queue.items.len) : (i += 1) {
|
if (tid != f) return .ambiguous;
|
||||||
const deps = graph.get(queue.items[i]) orelse continue;
|
} else found = tid;
|
||||||
var it = deps.iterator();
|
|
||||||
while (it.next()) |kv| {
|
|
||||||
const dep = kv.key_ptr.*;
|
|
||||||
if (visited.contains(dep)) continue;
|
|
||||||
visited.put(dep, {}) catch continue;
|
|
||||||
if (self.moduleTypeAuthor(dep, name) != null) {
|
|
||||||
saw_author = true;
|
|
||||||
if (self.moduleTypeAuthorTid(dep, name)) |tid| {
|
|
||||||
if (found) |f| {
|
|
||||||
if (tid != f) return .ambiguous;
|
|
||||||
} else found = tid;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
queue.append(self.alloc, dep) catch continue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (found) |t| return .{ .one = t };
|
if (found) |t| return .{ .one = t };
|
||||||
@@ -2238,6 +2231,9 @@ pub const Lowering = struct {
|
|||||||
fn resolveNominalLeaf(self: *Lowering, name: []const u8, raw: bool, span: ?ast.Span) TypeId {
|
fn resolveNominalLeaf(self: *Lowering, name: []const u8, raw: bool, span: ?ast.Span) TypeId {
|
||||||
const from = self.current_source_file orelse
|
const from = self.current_source_file orelse
|
||||||
return self.typeResolver().resolveName(name, raw);
|
return self.typeResolver().resolveName(name, raw);
|
||||||
|
if (std.mem.eql(u8, name, "BOOL")) {
|
||||||
|
std.debug.print("[BOOLLEAF] from={s} -> {s}\n", .{ from, @tagName(self.selectNominalLeaf(name, from, raw)) });
|
||||||
|
}
|
||||||
return switch (self.selectNominalLeaf(name, from, raw)) {
|
return switch (self.selectNominalLeaf(name, from, raw)) {
|
||||||
.resolved => |t| t,
|
.resolved => |t| t,
|
||||||
// A forward alias (`.pending`) or a forward / not-yet-interned named
|
// A forward alias (`.pending`) or a forward / not-yet-interned named
|
||||||
@@ -11551,6 +11547,14 @@ pub const Lowering = struct {
|
|||||||
self.pack_arg_types = pre_pat;
|
self.pack_arg_types = pre_pat;
|
||||||
self.pack_constraint = if (pack_proto != null) pre_pcon else null;
|
self.pack_constraint = if (pack_proto != null) pre_pcon else null;
|
||||||
|
|
||||||
|
// Resolve the declared return + fixed-prefix param types in the pack fn's
|
||||||
|
// OWN module (E4), so a 2-flat-hop library type named in the signature is
|
||||||
|
// bare-visible — mirrors the body pin further down and the
|
||||||
|
// `monomorphizeFunction` pin. The comptime call-site args below are
|
||||||
|
// lowered AFTER this restore, in the caller's context (issue 0106).
|
||||||
|
const saved_sig_src = self.current_source_file;
|
||||||
|
if (fd.body.source_file) |src| self.setCurrentSourceFile(src);
|
||||||
|
|
||||||
const declared_is_generic_ret = blk: {
|
const declared_is_generic_ret = blk: {
|
||||||
const rt = fd.return_type orelse break :blk false;
|
const rt = fd.return_type orelse break :blk false;
|
||||||
if (rt.data != .type_expr) break :blk false;
|
if (rt.data != .type_expr) break :blk false;
|
||||||
@@ -11590,6 +11594,7 @@ pub const Lowering = struct {
|
|||||||
.ty = ty,
|
.ty = ty,
|
||||||
}) catch return;
|
}) catch return;
|
||||||
}
|
}
|
||||||
|
self.setCurrentSourceFile(saved_sig_src);
|
||||||
|
|
||||||
const name_id = self.module.types.internString(owned_name);
|
const name_id = self.module.types.internString(owned_name);
|
||||||
_ = self.builder.beginFunction(name_id, params.items, ret_ty);
|
_ = self.builder.beginFunction(name_id, params.items, ret_ty);
|
||||||
@@ -11740,6 +11745,19 @@ pub const Lowering = struct {
|
|||||||
// Install type bindings
|
// Install type bindings
|
||||||
self.type_bindings = bindings.*;
|
self.type_bindings = bindings.*;
|
||||||
|
|
||||||
|
// Pin to the template's defining module for the whole monomorphization
|
||||||
|
// (return type, param types, body), so a library-internal bare TYPE ref
|
||||||
|
// — e.g. `List(T).append`'s `alloc: Allocator` default-param type, or a
|
||||||
|
// body reference to a type visible only in the template's module —
|
||||||
|
// resolves where it is visible, not at the (possibly cross-module) call
|
||||||
|
// site. This is the issue-0100-F1 plain-fn pin extended to generic
|
||||||
|
// instantiation; without it the non-transitive bare-TYPE gate (E4) would
|
||||||
|
// reject a 2-flat-hop library type the call site cannot see directly.
|
||||||
|
// A synthesized / sourceless body keeps the caller's context.
|
||||||
|
const saved_source_mono = self.current_source_file;
|
||||||
|
defer self.setCurrentSourceFile(saved_source_mono);
|
||||||
|
if (fd.body.source_file) |src| self.setCurrentSourceFile(src);
|
||||||
|
|
||||||
// Resolve return type with type bindings active. The body's tail
|
// Resolve return type with type bindings active. The body's tail
|
||||||
// expression inherits this as its target_type so bare `.{...}`
|
// expression inherits this as its target_type so bare `.{...}`
|
||||||
// literals resolve to the monomorphised return type instead of
|
// literals resolve to the monomorphised return type instead of
|
||||||
@@ -12969,7 +12987,7 @@ pub const Lowering = struct {
|
|||||||
if (fd.params.len > 0) {
|
if (fd.params.len > 0) {
|
||||||
var types_list = std.ArrayList(TypeId).empty;
|
var types_list = std.ArrayList(TypeId).empty;
|
||||||
for (fd.params[1..]) |p| {
|
for (fd.params[1..]) |p| {
|
||||||
types_list.append(self.alloc, self.resolveParamType(&p)) catch unreachable;
|
types_list.append(self.alloc, self.resolveParamTypeInSource(fd.body.source_file, &p)) catch unreachable;
|
||||||
}
|
}
|
||||||
return types_list.items;
|
return types_list.items;
|
||||||
}
|
}
|
||||||
@@ -12987,7 +13005,7 @@ pub const Lowering = struct {
|
|||||||
}
|
}
|
||||||
var types_list = std.ArrayList(TypeId).empty;
|
var types_list = std.ArrayList(TypeId).empty;
|
||||||
for (fd.params[1..]) |p| {
|
for (fd.params[1..]) |p| {
|
||||||
types_list.append(self.alloc, self.resolveParamType(&p)) catch unreachable;
|
types_list.append(self.alloc, self.resolveParamTypeInSource(fd.body.source_file, &p)) catch unreachable;
|
||||||
}
|
}
|
||||||
self.type_bindings = saved_bindings;
|
self.type_bindings = saved_bindings;
|
||||||
return types_list.items;
|
return types_list.items;
|
||||||
@@ -13376,6 +13394,20 @@ pub const Lowering = struct {
|
|||||||
return self.resolveType(type_ann);
|
return self.resolveType(type_ann);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `resolveParamType` with the visibility context pinned to `src`, the
|
||||||
|
/// DEFINING module of the param's function. An imported method's
|
||||||
|
/// default-param type (`alloc: Allocator`) is bare-visible only inside its
|
||||||
|
/// own module, so typing a cross-module call's args against it must resolve
|
||||||
|
/// in that module's context, not the call site's (E4 — the param analog of
|
||||||
|
/// `resolveTypeInSource`). `src == null` falls back unchanged.
|
||||||
|
fn resolveParamTypeInSource(self: *Lowering, src: ?[]const u8, p: *const ast.Param) TypeId {
|
||||||
|
const pinned = src orelse return self.resolveParamType(p);
|
||||||
|
const saved = self.current_source_file;
|
||||||
|
defer self.setCurrentSourceFile(saved);
|
||||||
|
self.setCurrentSourceFile(pinned);
|
||||||
|
return self.resolveParamType(p);
|
||||||
|
}
|
||||||
|
|
||||||
/// Construct a `TypeResolver` view over the current lowering state (borrows
|
/// Construct a `TypeResolver` view over the current lowering state (borrows
|
||||||
/// only; cheap by-value, reflects current `diagnostics` / `program_index`).
|
/// only; cheap by-value, reflects current `diagnostics` / `program_index`).
|
||||||
fn typeResolver(self: *Lowering) TypeResolver {
|
fn typeResolver(self: *Lowering) TypeResolver {
|
||||||
@@ -14074,6 +14106,14 @@ pub const Lowering = struct {
|
|||||||
self.comptime_value_bindings = saved_value_bindings;
|
self.comptime_value_bindings = saved_value_bindings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resolve the type fn's body (inline struct/union fields, or the returned
|
||||||
|
// type expression) in its OWN module (E4), so a 2-flat-hop library type
|
||||||
|
// named there is bare-visible — not the cross-module call site. The arg
|
||||||
|
// exprs above were already resolved in the caller's context.
|
||||||
|
const saved_tf_src = self.current_source_file;
|
||||||
|
defer self.setCurrentSourceFile(saved_tf_src);
|
||||||
|
if (fd.body.source_file) |src| self.setCurrentSourceFile(src);
|
||||||
|
|
||||||
// Determine if alias_name is a real alias (e.g., "Foo" for "Complex(u32)")
|
// Determine if alias_name is a real alias (e.g., "Foo" for "Complex(u32)")
|
||||||
// or just the template name itself (inline use like "Sx(f32)")
|
// or just the template name itself (inline use like "Sx(f32)")
|
||||||
const has_alias = !std.mem.eql(u8, alias_name, template_name);
|
const has_alias = !std.mem.eql(u8, alias_name, template_name);
|
||||||
@@ -14689,9 +14729,16 @@ pub const Lowering = struct {
|
|||||||
const id = if (table.findByName(name_id)) |existing| existing else table.intern(struct_info);
|
const id = if (table.findByName(name_id)) |existing| existing else table.intern(struct_info);
|
||||||
table.updatePreservingKey(id, struct_info);
|
table.updatePreservingKey(id, struct_info);
|
||||||
|
|
||||||
// Method infos resolved with the type-arg binding (T → s64).
|
// Method infos resolved with the type-arg binding (T → s64), pinned to
|
||||||
|
// the protocol's OWN module (E4) so a method-signature type visible only
|
||||||
|
// there resolves correctly when instantiated cross-module. `Self` and the
|
||||||
|
// bound type-args short-circuit before the leaf; a concrete library type
|
||||||
|
// in a signature is the case this pin protects.
|
||||||
const saved_tb = self.type_bindings;
|
const saved_tb = self.type_bindings;
|
||||||
self.type_bindings = tb;
|
self.type_bindings = tb;
|
||||||
|
const saved_pp_src = self.current_source_file;
|
||||||
|
defer self.setCurrentSourceFile(saved_pp_src);
|
||||||
|
if (pd.source_file) |src| self.setCurrentSourceFile(src);
|
||||||
var method_infos = std.ArrayList(ProtocolMethodInfo).empty;
|
var method_infos = std.ArrayList(ProtocolMethodInfo).empty;
|
||||||
for (pd.methods) |method| {
|
for (pd.methods) |method| {
|
||||||
var ptypes = std.ArrayList(TypeId).empty;
|
var ptypes = std.ArrayList(TypeId).empty;
|
||||||
|
|||||||
Reference in New Issue
Block a user