diff --git a/examples/0793-modules-same-name-const-type-infer.sx b/examples/0793-modules-same-name-const-type-infer.sx new file mode 100644 index 0000000..f540f4e --- /dev/null +++ b/examples/0793-modules-same-name-const-type-infer.sx @@ -0,0 +1,16 @@ +// issue 0105 / F4 — same-name VALUE const TYPE inference is source-aware. Two +// flat-imported modules each declare a top-level `K` with a DIFFERENT declared +// TYPE (A: `s32`, B: `f64`) and an inferred-return function that reads `K` bare. +// The inferred return type must come from each module's OWN `K` (own-wins), not +// the global last-wins const TYPE: `a_k` infers A's `s32` and yields the integer +// `1` (printed `1`, not `1.000000`), while `b_k` infers B's `f64` and yields +// `2.500000`. Selection routes through the source-aware `selectModuleConst`, the +// same author selector emission/folding use — type inference must agree. +#import "modules/std.sx"; +#import "0793-modules-same-name-const-type-infer/a.sx"; +#import "0793-modules-same-name-const-type-infer/b.sx"; + +main :: () -> s32 { + print("a={} b={}\n", a_k(), b_k()); + 0 +} diff --git a/examples/0793-modules-same-name-const-type-infer/a.sx b/examples/0793-modules-same-name-const-type-infer/a.sx new file mode 100644 index 0000000..3d02899 --- /dev/null +++ b/examples/0793-modules-same-name-const-type-infer/a.sx @@ -0,0 +1,5 @@ +// Module A authors its OWN `K` declared `s32`. Its inferred-return `a_k` reads +// `K` bare; the inferred return type must be A's `s32`, so the value prints as +// the integer `1`, never coerced to B's `f64`. +K : s32 : 1; +a_k :: () { return K; } diff --git a/examples/0793-modules-same-name-const-type-infer/b.sx b/examples/0793-modules-same-name-const-type-infer/b.sx new file mode 100644 index 0000000..91b7bea --- /dev/null +++ b/examples/0793-modules-same-name-const-type-infer/b.sx @@ -0,0 +1,5 @@ +// Module B authors a DIFFERENT same-name `K` declared `f64` (a shadow of A's +// `K`). Its inferred-return `b_k` reads `K` bare; the inferred return type must +// be B's `f64`, so the value prints as `2.500000`. +K : f64 : 2.5; +b_k :: () { return K; } diff --git a/examples/0794-modules-same-name-const-type-ambiguous.sx b/examples/0794-modules-same-name-const-type-ambiguous.sx new file mode 100644 index 0000000..e5634d8 --- /dev/null +++ b/examples/0794-modules-same-name-const-type-ambiguous.sx @@ -0,0 +1,15 @@ +// issue 0105 / F4 — same-name VALUE const with DIFFERENT declared TYPES across +// two flat-imported modules (A: `s32`, B: `f64`), referenced bare at a mixed-type +// site. A bare `K` here is genuinely ambiguous — there are ≥2 flat-visible same- +// name authors — so it must diagnose loudly (exit 1), exactly as the same-typed +// 0787 does. The type-inference change must NOT mask the ambiguity (inferring +// `.unresolved` for the ambiguous reference) — the emission path still fires the +// loud author-count diagnostic regardless of the consts' declared types. +#import "modules/std.sx"; +#import "0794-modules-same-name-const-type-ambiguous/a.sx"; +#import "0794-modules-same-name-const-type-ambiguous/b.sx"; + +main :: () -> s32 { + print("K={}\n", K); + 0 +} diff --git a/examples/0794-modules-same-name-const-type-ambiguous/a.sx b/examples/0794-modules-same-name-const-type-ambiguous/a.sx new file mode 100644 index 0000000..a4460aa --- /dev/null +++ b/examples/0794-modules-same-name-const-type-ambiguous/a.sx @@ -0,0 +1,2 @@ +// Module A authors `K` declared `s32`. +K : s32 : 1; diff --git a/examples/0794-modules-same-name-const-type-ambiguous/b.sx b/examples/0794-modules-same-name-const-type-ambiguous/b.sx new file mode 100644 index 0000000..b713b4f --- /dev/null +++ b/examples/0794-modules-same-name-const-type-ambiguous/b.sx @@ -0,0 +1,2 @@ +// Module B authors a DIFFERENT same-name `K` declared `f64`. +K : f64 : 2.5; diff --git a/examples/expected/0793-modules-same-name-const-type-infer.exit b/examples/expected/0793-modules-same-name-const-type-infer.exit new file mode 100644 index 0000000..573541a --- /dev/null +++ b/examples/expected/0793-modules-same-name-const-type-infer.exit @@ -0,0 +1 @@ +0 diff --git a/examples/expected/0793-modules-same-name-const-type-infer.stderr b/examples/expected/0793-modules-same-name-const-type-infer.stderr new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/examples/expected/0793-modules-same-name-const-type-infer.stderr @@ -0,0 +1 @@ + diff --git a/examples/expected/0793-modules-same-name-const-type-infer.stdout b/examples/expected/0793-modules-same-name-const-type-infer.stdout new file mode 100644 index 0000000..ace952f --- /dev/null +++ b/examples/expected/0793-modules-same-name-const-type-infer.stdout @@ -0,0 +1 @@ +a=1 b=2.500000 diff --git a/examples/expected/0794-modules-same-name-const-type-ambiguous.exit b/examples/expected/0794-modules-same-name-const-type-ambiguous.exit new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/examples/expected/0794-modules-same-name-const-type-ambiguous.exit @@ -0,0 +1 @@ +1 diff --git a/examples/expected/0794-modules-same-name-const-type-ambiguous.stderr b/examples/expected/0794-modules-same-name-const-type-ambiguous.stderr new file mode 100644 index 0000000..671ec2f --- /dev/null +++ b/examples/expected/0794-modules-same-name-const-type-ambiguous.stderr @@ -0,0 +1,5 @@ +error: 'K' is ambiguous: it is declared in multiple flat-imported modules; qualify the reference or remove the duplicate import + --> examples/0794-modules-same-name-const-type-ambiguous.sx:13:21 + | +13 | print("K={}\n", K); + | ^ diff --git a/examples/expected/0794-modules-same-name-const-type-ambiguous.stdout b/examples/expected/0794-modules-same-name-const-type-ambiguous.stdout new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/examples/expected/0794-modules-same-name-const-type-ambiguous.stdout @@ -0,0 +1 @@ + diff --git a/src/ir/expr_typer.zig b/src/ir/expr_typer.zig index ed3d2f5..e7b5b70 100644 --- a/src/ir/expr_typer.zig +++ b/src/ir/expr_typer.zig @@ -277,9 +277,21 @@ pub const ExprTyper = struct { if (self.l.program_index.global_names.get(id.name)) |gi| { return gi.ty; } - // Check module-level value constants (e.g., WIDTH :f32: 800) - if (self.l.program_index.module_const_map.get(id.name)) |ci| { - return ci.ty; + // Check module-level value constants (e.g., WIDTH :f32: 800). + // F4: a same-name VALUE const must infer the SOURCE-AWARE author's + // TYPE (own-wins / one-flat-visible), not the global last-wins + // `module_const_map` — otherwise a return-type / coercion inferred + // on one module's `K` borrows another module's `K` TYPE. The global + // map still gates "is this a const name at all?"; `.none` is the + // registration-only author with no per-source partition (emit its + // global type), and an ambiguous bare reference yields `.unresolved` + // (the emission path diagnoses the ambiguity loudly). + if (self.l.program_index.module_const_map.get(id.name)) |ci_global| { + return switch (self.l.selectModuleConst(id.name)) { + .resolved => |sel| sel.info.ty, + .none => ci_global.ty, + .ambiguous => .unresolved, + }; } // A bare type name (alias like `Vec4`, struct name, or // builtin primitive) referenced in expression position diff --git a/src/ir/lower.zig b/src/ir/lower.zig index 506ac87..c6093f5 100644 --- a/src/ir/lower.zig +++ b/src/ir/lower.zig @@ -13837,7 +13837,7 @@ pub const Lowering = struct { /// the querying module is `main_file` there; a fully unwired index (no source /// at all) falls open to the global registration, byte-identical to the legacy /// reader for the registration / comptime-host path. - fn selectModuleConst(self: *Lowering, name: []const u8) ConstAuthor { + pub fn selectModuleConst(self: *Lowering, name: []const u8) ConstAuthor { const from = self.current_source_file orelse self.main_file orelse { if (self.program_index.module_const_map.get(name)) |ci| return .{ .resolved = .{ .info = ci, .source = null } }; return .none;