fix: visibility-aware type-name resolution at registration time
Enum payloads, union fields, inline struct/enum/union field types, and named error-set references now resolve through the visibility-aware `inner` recursion hook (the same seam `resolveCompound` uses) instead of the flat `findByName`. A bare type name in any of these positions now selects the querying module's OWN author over a same-name namespaced import -- the own-wins rule already applied to top-level named references and struct fields. - buildEnumInfo / buildUnionInfo / resolveInlineEnum / resolveInlineStruct / resolveInlineUnion / resolveErrorType take the `inner: anytype` seam; registerEnumDecl / registerUnionDecl and the struct-const annotation pass `self` (visibility-aware); resolveAstType passes the stateless `si`. - resolveTypeWithBindings routes inline type decls and named error refs through `self` instead of delegating to flat resolveAstType. Regression tests: examples/0781 (top-level enum payload over a namespaced import), examples/0784 (inline struct field). Addresses issue 0132's broader latent class; the protocol-return case (0132 primary) is a separate registerProtocolDecl fix and stays open. The error-set reference path is in place but dormant pending error-set per-decl nominal identity (issue 0134).
This commit is contained in:
29
examples/0781-modules-same-name-enum-payload-own-wins.sx
Normal file
29
examples/0781-modules-same-name-enum-payload-own-wins.sx
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
// Own-wins for ENUM-PAYLOAD type registration over a NAMESPACED import.
|
||||||
|
// Regression (issue 0132, broader class).
|
||||||
|
//
|
||||||
|
// `#import "modules/std.sx"` carries the stdlib `event.Event` struct — it is
|
||||||
|
// NAMESPACED (reachable only as `event.Event`), never flat-visible. This file
|
||||||
|
// ALSO authors its OWN `Event :: struct { code }`, used as the payload of the
|
||||||
|
// enum `Wrap`. The payload type name `Event` must resolve at REGISTRATION to
|
||||||
|
// THIS file's own `Event` (which has `code`), not the namespaced stdlib struct.
|
||||||
|
//
|
||||||
|
// Fail-before: `registerEnumDecl` built the tagged-union body through the
|
||||||
|
// stateless `type_bridge.buildEnumInfo`, whose flat `findByName` picked the
|
||||||
|
// wrong same-name author — `got`'s payload became the stdlib `Event`, so
|
||||||
|
// `e.code` errored "field 'code' not found on type 'Event'". Fixed by threading
|
||||||
|
// the visibility-aware resolver (`*Lowering` as the `resolveInner` hook) through
|
||||||
|
// `buildEnumInfo` / `buildUnionInfo`, matching what `registerStructDecl` already
|
||||||
|
// does for struct fields.
|
||||||
|
|
||||||
|
#import "modules/std.sx";
|
||||||
|
|
||||||
|
Event :: struct { code: i64; }
|
||||||
|
Wrap :: enum { none; got: Event; }
|
||||||
|
|
||||||
|
main :: () {
|
||||||
|
w : Wrap = .got(.{ code = 7 });
|
||||||
|
if w == {
|
||||||
|
case .got: (e) { print("code={}\n", e.code); }
|
||||||
|
case .none: print("none\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
// Own-wins for an INLINE struct field's member type over a NAMESPACED import.
|
||||||
|
// Regression (issue 0132's broader class — the inline-decl resolution boundary).
|
||||||
|
//
|
||||||
|
// `Holder.inner` is an inline `struct { e: Event }`. The member type `Event`
|
||||||
|
// must resolve to THIS file's `Event` (which has `code`), not the namespaced
|
||||||
|
// stdlib `event.Event` struct carried by `#import "modules/std.sx"` (reachable
|
||||||
|
// only as `event.Event`, never bare).
|
||||||
|
//
|
||||||
|
// Fail-before: `Lowering.resolveTypeWithBindings` delegated inline `struct_decl`
|
||||||
|
// field types to the FLAT `type_bridge.resolveAstType`, dropping the visibility
|
||||||
|
// context — so `e: Event` resolved via global `findByName` to the stdlib struct
|
||||||
|
// and `h.inner.e.code` errored "field 'code' not found on type 'Event'". Fixed
|
||||||
|
// by routing inline enum/struct/union decls through the `inner` recursion hook
|
||||||
|
// with `self` (visibility-aware), the same own-wins rule top-level decls use.
|
||||||
|
|
||||||
|
#import "modules/std.sx";
|
||||||
|
|
||||||
|
Event :: struct { code: i64; }
|
||||||
|
Holder :: struct { inner: struct { e: Event; }; }
|
||||||
|
|
||||||
|
main :: () {
|
||||||
|
h : Holder = ---;
|
||||||
|
h.inner.e = .{ code = 5 };
|
||||||
|
print("code={}\n", h.inner.e.code);
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
0
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
code=7
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
0
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
code=5
|
||||||
@@ -717,6 +717,14 @@ pub const Lowering = struct {
|
|||||||
return self.resolveTypeWithBindings(node);
|
return self.resolveTypeWithBindings(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Bare TYPE-NAME twin of `resolveInner` for callers holding a name rather
|
||||||
|
/// than an AST node (e.g. an error-set reference `!Named`) — routed through
|
||||||
|
/// the visibility-aware `resolveNominalLeaf`, so a same-name-shadowed set
|
||||||
|
/// resolves to the querying module's own author (issue 0132's class).
|
||||||
|
pub fn resolveName(self: *Lowering, name: []const u8) TypeId {
|
||||||
|
return self.resolveNominalLeaf(name, false, null);
|
||||||
|
}
|
||||||
|
|
||||||
/// Fixed-array dimension hook for `TypeResolver.resolveCompound`. A literal
|
/// Fixed-array dimension hook for `TypeResolver.resolveCompound`. A literal
|
||||||
/// `[16]T` and a named-const `N :: 16; [N]T` must resolve to the SAME length:
|
/// `[16]T` and a named-const `N :: 16; [N]T` must resolve to the SAME length:
|
||||||
/// the dimension folds to a compile-time integer (looked up in the comptime /
|
/// the dimension folds to a compile-time integer (looked up in the comptime /
|
||||||
@@ -936,6 +944,28 @@ pub const Lowering = struct {
|
|||||||
// literal (`(i32, i32)`); validate its elements are types and reject
|
// literal (`(i32, i32)`); validate its elements are types and reject
|
||||||
// non-type elements loudly.
|
// non-type elements loudly.
|
||||||
.tuple_literal => return self.resolveTupleLiteralTypeArg(node),
|
.tuple_literal => return self.resolveTupleLiteralTypeArg(node),
|
||||||
|
// Inline type declarations used as a field type (`x: enum { ... }`,
|
||||||
|
// `x: struct { ... }`, `x: union { ... }`): build their bodies with
|
||||||
|
// THIS lowering as the `inner` recursion hook, so a payload / field
|
||||||
|
// type NAME resolves in the enclosing module's visibility context —
|
||||||
|
// the SAME own-wins-over-namespaced rule the top-level registration
|
||||||
|
// uses (issue 0132's class). Delegating to the flat `else` below
|
||||||
|
// dropped `self`, leaving inline-decl payloads on the global
|
||||||
|
// `findByName` first-match.
|
||||||
|
.enum_decl => return type_bridge.resolveInlineEnum(&node.data.enum_decl, &self.module.types, self),
|
||||||
|
.struct_decl => return type_bridge.resolveInlineStruct(&node.data.struct_decl, &self.module.types, self),
|
||||||
|
.union_decl => return type_bridge.resolveInlineUnion(&node.data.union_decl, &self.module.types, self),
|
||||||
|
// A NAMED error-set reference (`!Named`) resolves its name through
|
||||||
|
// `self` (visibility-aware) too; the bare `!` inferred set has no name
|
||||||
|
// to shadow. NOTE: this reference-side resolution is currently DORMANT
|
||||||
|
// for same-name error-set collisions — error-set DECLARATIONS don't
|
||||||
|
// yet get per-decl nominal identity (E6a covers struct/enum/union
|
||||||
|
// only), so a same-name set collapses to one TypeId at registration
|
||||||
|
// and there is nothing distinct for the reference to select. See issue
|
||||||
|
// 0134; once decls get nominal identity this activates with no change
|
||||||
|
// here. `error_set_decl` is NOT in this switch: it interns only tag
|
||||||
|
// names, resolving no type names, so it stays on the flat `else`.
|
||||||
|
.error_type_expr => return type_bridge.resolveErrorType(&node.data.error_type_expr, &self.module.types, self),
|
||||||
else => return type_bridge.resolveAstType(node, &self.module.types, &self.program_index.type_alias_map, &self.program_index.module_const_map),
|
else => return type_bridge.resolveAstType(node, &self.module.types, &self.program_index.type_alias_map, &self.program_index.module_const_map),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,7 +703,7 @@ pub fn registerStructDecl(self: *Lowering, sd: *const ast.StructDecl, source_fil
|
|||||||
if (const_node.data == .const_decl) {
|
if (const_node.data == .const_decl) {
|
||||||
const cd = const_node.data.const_decl;
|
const cd = const_node.data.const_decl;
|
||||||
const qualified = std.fmt.allocPrint(self.alloc, "{s}.{s}", .{ sd.name, cd.name }) catch continue;
|
const qualified = std.fmt.allocPrint(self.alloc, "{s}.{s}", .{ sd.name, cd.name }) catch continue;
|
||||||
const ty: ?TypeId = if (cd.type_annotation) |ta| type_bridge.resolveAstType(ta, table, &self.program_index.type_alias_map, &self.program_index.module_const_map) else null;
|
const ty: ?TypeId = if (cd.type_annotation) |ta| self.resolveType(ta) else null;
|
||||||
self.struct_const_map.put(qualified, .{ .value = cd.value, .ty = ty }) catch {};
|
self.struct_const_map.put(qualified, .{ .value = cd.value, .ty = ty }) catch {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -725,7 +725,12 @@ pub fn registerEnumDecl(self: *Lowering, ed: *const ast.EnumDecl) void {
|
|||||||
const name_id = table.internString(ed.name);
|
const name_id = table.internString(ed.name);
|
||||||
const decl_key: *const anyopaque = @ptrCast(ed);
|
const decl_key: *const anyopaque = @ptrCast(ed);
|
||||||
const nominal_id: u32 = if (table.type_decl_tids.get(decl_key)) |id| nominalIdOf(table.get(id)) else self.shadowNominalId(name_id);
|
const nominal_id: u32 = if (table.type_decl_tids.get(decl_key)) |id| nominalIdOf(table.get(id)) else self.shadowNominalId(name_id);
|
||||||
const info = type_bridge.buildEnumInfo(ed, table, &self.program_index.type_alias_map, &self.program_index.module_const_map);
|
// Pass `self` (the visibility-aware `Lowering` resolver) as the `inner`
|
||||||
|
// recursion hook — the same seam `resolveCompound` uses — so a payload type
|
||||||
|
// NAME resolves in the enum's OWN module visibility context (own author wins
|
||||||
|
// over a namespaced same-name import), not via a global `findByName`
|
||||||
|
// first-match (issue 0132's class).
|
||||||
|
const info = type_bridge.buildEnumInfo(ed, table, self);
|
||||||
_ = self.internNamedTypeDecl(decl_key, name_id, info, nominal_id);
|
_ = self.internNamedTypeDecl(decl_key, name_id, info, nominal_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -736,7 +741,8 @@ pub fn registerUnionDecl(self: *Lowering, ud: *const ast.UnionDecl) void {
|
|||||||
const name_id = table.internString(ud.name);
|
const name_id = table.internString(ud.name);
|
||||||
const decl_key: *const anyopaque = @ptrCast(ud);
|
const decl_key: *const anyopaque = @ptrCast(ud);
|
||||||
const nominal_id: u32 = if (table.type_decl_tids.get(decl_key)) |id| nominalIdOf(table.get(id)) else self.shadowNominalId(name_id);
|
const nominal_id: u32 = if (table.type_decl_tids.get(decl_key)) |id| nominalIdOf(table.get(id)) else self.shadowNominalId(name_id);
|
||||||
const info = type_bridge.buildUnionInfo(ud, table, &self.program_index.type_alias_map, &self.program_index.module_const_map);
|
// `self` as the visibility-aware `inner` hook — see `registerEnumDecl`.
|
||||||
|
const info = type_bridge.buildUnionInfo(ud, table, self);
|
||||||
_ = self.internNamedTypeDecl(decl_key, name_id, info, nominal_id);
|
_ = self.internNamedTypeDecl(decl_key, name_id, info, nominal_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,12 @@ const StatelessInner = struct {
|
|||||||
pub fn resolveInner(self: StatelessInner, node: *const Node) TypeId {
|
pub fn resolveInner(self: StatelessInner, node: *const Node) TypeId {
|
||||||
return resolveAstType(node, self.table, self.alias_map, self.consts);
|
return resolveAstType(node, self.table, self.alias_map, self.consts);
|
||||||
}
|
}
|
||||||
|
/// Bare TYPE-NAME twin of `resolveInner`, for callers that hold a name
|
||||||
|
/// rather than an AST node (an error-set reference `!Named`). Flat:
|
||||||
|
/// registered name → alias → stub, no visibility scoping.
|
||||||
|
pub fn resolveName(self: StatelessInner, name: []const u8) TypeId {
|
||||||
|
return resolveTypeName(name, self.table, self.alias_map, false);
|
||||||
|
}
|
||||||
/// Fixed-array dimension at registration time: a literal `[16]T`, a named
|
/// Fixed-array dimension at registration time: a literal `[16]T`, a named
|
||||||
/// module-global const `N :: 16; [N]T` (typed `N : i64 : 16` too), or a
|
/// module-global const `N :: 16; [N]T` (typed `N : i64 : 16` too), or a
|
||||||
/// constant-foldable expression over those (`[M + 1]`, `[(M + 1) * 2]`).
|
/// constant-foldable expression over those (`[M + 1]`, `[(M + 1) * 2]`).
|
||||||
@@ -183,12 +189,15 @@ pub fn resolveAstType(node: ?*const Node, table: *TypeTable, alias_map: AliasMap
|
|||||||
// type — so the omission surfaces; the lowering-side `resolveParamType`
|
// type — so the omission surfaces; the lowering-side `resolveParamType`
|
||||||
// turns it into a real diagnostic.
|
// turns it into a real diagnostic.
|
||||||
.inferred_type => .unresolved,
|
.inferred_type => .unresolved,
|
||||||
// Inline type declarations (used as field types)
|
// Inline type declarations (used as field types). Enum/union bodies are
|
||||||
.enum_decl => |ed| resolveInlineEnum(&ed, table, alias_map, consts),
|
// built through the shared `inner`-parameterized builders; the stateless
|
||||||
.struct_decl => |sd| resolveInlineStruct(&sd, table, alias_map, consts),
|
// path passes `si` (the `StatelessInner` already constructed above) — the
|
||||||
.union_decl => |ud| resolveInlineUnion(&ud, table, alias_map, consts),
|
// same `resolveInner` recursion hook `resolveCompound` receives.
|
||||||
|
.enum_decl => |ed| resolveInlineEnum(&ed, table, si),
|
||||||
|
.struct_decl => |sd| resolveInlineStruct(&sd, table, si),
|
||||||
|
.union_decl => |ud| resolveInlineUnion(&ud, table, si),
|
||||||
.error_set_decl => |esd| resolveInlineErrorSet(&esd, table),
|
.error_set_decl => |esd| resolveInlineErrorSet(&esd, table),
|
||||||
.error_type_expr => |ete| resolveErrorType(&ete, table, alias_map),
|
.error_type_expr => |ete| resolveErrorType(&ete, table, si),
|
||||||
else => {
|
else => {
|
||||||
// A non-type AST node reached type resolution — a caller bug.
|
// A non-type AST node reached type resolution — a caller bug.
|
||||||
// Returning a plausible `.i64` would silently fabricate an 8-byte
|
// Returning a plausible `.i64` would silently fabricate an 8-byte
|
||||||
@@ -340,14 +349,17 @@ fn resolveParameterizedType(pt: *const ast.ParameterizedTypeExpr, table: *TypeTa
|
|||||||
|
|
||||||
// ── Inline type declarations ─────────────────────────────────────────
|
// ── Inline type declarations ─────────────────────────────────────────
|
||||||
|
|
||||||
/// Stateless inline-enum resolution for a FIELD-type position (`x: enum {...}`):
|
/// Inline-enum resolution for a FIELD-type position (`x: enum {...}`). Payload
|
||||||
/// the legacy `findByName` short-circuit keeps a single global slot per display
|
/// type NAMES resolve through the injected `inner` recursion hook: the stateless
|
||||||
/// name. The TOP-LEVEL per-decl nominal identity path (`Lowering.registerEnumDecl`)
|
/// `StatelessInner` (flat) when reached from `resolveAstType`, or `*Lowering`
|
||||||
|
/// (visibility-aware) when reached from `Lowering.resolveTypeWithBindings` — so a
|
||||||
|
/// payload name resolves in the enclosing module's context (issue 0132's class).
|
||||||
|
/// The TOP-LEVEL per-decl nominal identity path (`Lowering.registerEnumDecl`)
|
||||||
/// shares the body via `buildEnumInfo` but interns under its own nominal id.
|
/// shares the body via `buildEnumInfo` but interns under its own nominal id.
|
||||||
fn resolveInlineEnum(ed: *const ast.EnumDecl, table: *TypeTable, alias_map: AliasMap, consts: ConstMap) TypeId {
|
pub fn resolveInlineEnum(ed: *const ast.EnumDecl, table: *TypeTable, inner: anytype) TypeId {
|
||||||
const name_id = table.internString(ed.name);
|
const name_id = table.internString(ed.name);
|
||||||
if (table.findByName(name_id)) |existing| return existing;
|
if (table.findByName(name_id)) |existing| return existing;
|
||||||
const info = buildEnumInfo(ed, table, alias_map, consts);
|
const info = buildEnumInfo(ed, table, inner);
|
||||||
const id = table.internNominal(info, 0);
|
const id = table.internNominal(info, 0);
|
||||||
table.updatePreservingKey(id, info);
|
table.updatePreservingKey(id, info);
|
||||||
return id;
|
return id;
|
||||||
@@ -361,7 +373,7 @@ fn resolveInlineEnum(ed: *const ast.EnumDecl, table: *TypeTable, alias_map: Alia
|
|||||||
/// enum builds a `.tagged_union`; a payload-less enum a plain `.enum`. Nested
|
/// enum builds a `.tagged_union`; a payload-less enum a plain `.enum`. Nested
|
||||||
/// payload structs / variant field types ARE interned here — they are distinct
|
/// payload structs / variant field types ARE interned here — they are distinct
|
||||||
/// nested nominals, not the enum's own identity.
|
/// nested nominals, not the enum's own identity.
|
||||||
pub fn buildEnumInfo(ed: *const ast.EnumDecl, table: *TypeTable, alias_map: AliasMap, consts: ConstMap) TypeInfo {
|
pub fn buildEnumInfo(ed: *const ast.EnumDecl, table: *TypeTable, inner: anytype) TypeInfo {
|
||||||
const alloc = table.alloc;
|
const alloc = table.alloc;
|
||||||
const name_id = table.internString(ed.name);
|
const name_id = table.internString(ed.name);
|
||||||
|
|
||||||
@@ -384,7 +396,7 @@ pub fn buildEnumInfo(ed: *const ast.EnumDecl, table: *TypeTable, alias_map: Alia
|
|||||||
} else {
|
} else {
|
||||||
var sfields = std.ArrayList(TypeInfo.StructInfo.Field).empty;
|
var sfields = std.ArrayList(TypeInfo.StructInfo.Field).empty;
|
||||||
for (sd.field_names, sd.field_types) |fname, ftype_node| {
|
for (sd.field_names, sd.field_types) |fname, ftype_node| {
|
||||||
const fty = resolveAstType(ftype_node, table, alias_map, consts);
|
const fty = inner.resolveInner(ftype_node);
|
||||||
sfields.append(alloc, .{
|
sfields.append(alloc, .{
|
||||||
.name = table.internString(fname),
|
.name = table.internString(fname),
|
||||||
.ty = fty,
|
.ty = fty,
|
||||||
@@ -398,10 +410,10 @@ pub fn buildEnumInfo(ed: *const ast.EnumDecl, table: *TypeTable, alias_map: Alia
|
|||||||
table.updatePreservingKey(field_ty, sinfo);
|
table.updatePreservingKey(field_ty, sinfo);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
field_ty = resolveAstType(vt, table, alias_map, consts);
|
field_ty = inner.resolveInner(vt);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
field_ty = resolveAstType(vt, table, alias_map, consts);
|
field_ty = inner.resolveInner(vt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -415,7 +427,7 @@ pub fn buildEnumInfo(ed: *const ast.EnumDecl, table: *TypeTable, alias_map: Alia
|
|||||||
var backing_type: ?TypeId = null;
|
var backing_type: ?TypeId = null;
|
||||||
var tag_type: ?TypeId = null;
|
var tag_type: ?TypeId = null;
|
||||||
if (ed.backing_type) |bt| {
|
if (ed.backing_type) |bt| {
|
||||||
const backing_ty = resolveAstType(bt, table, alias_map, consts);
|
const backing_ty = inner.resolveInner(bt);
|
||||||
backing_type = backing_ty;
|
backing_type = backing_ty;
|
||||||
// Extract tag type from first field of backing struct
|
// Extract tag type from first field of backing struct
|
||||||
const backing_info = table.get(backing_ty);
|
const backing_info = table.get(backing_ty);
|
||||||
@@ -495,7 +507,7 @@ pub fn buildEnumInfo(ed: *const ast.EnumDecl, table: *TypeTable, alias_map: Alia
|
|||||||
if (ed.backing_type) |bt| {
|
if (ed.backing_type) |bt| {
|
||||||
// Only use simple backing types (u8, u16, u32, etc.), not struct backing (enum struct)
|
// Only use simple backing types (u8, u16, u32, etc.), not struct backing (enum struct)
|
||||||
if (bt.data != .struct_decl) {
|
if (bt.data != .struct_decl) {
|
||||||
enum_backing = resolveAstType(bt, table, alias_map, consts);
|
enum_backing = inner.resolveInner(bt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -508,7 +520,14 @@ pub fn buildEnumInfo(ed: *const ast.EnumDecl, table: *TypeTable, alias_map: Alia
|
|||||||
} };
|
} };
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolveInlineStruct(sd: *const ast.StructDecl, table: *TypeTable, alias_map: AliasMap, consts: ConstMap) TypeId {
|
/// Inline-struct resolution for a FIELD-type position (`x: struct {...}`). Field
|
||||||
|
/// type NAMES resolve through the injected `inner` hook (flat `StatelessInner`
|
||||||
|
/// from `resolveAstType`, or visibility-aware `*Lowering` from
|
||||||
|
/// `resolveTypeWithBindings` — issue 0132's class). The TOP-LEVEL struct path
|
||||||
|
/// (`Lowering.registerStructDecl`) builds its own field list directly via
|
||||||
|
/// `self.resolveType` (it also expands `#using` and qualifies `__anon` names),
|
||||||
|
/// so it does not route through here.
|
||||||
|
pub fn resolveInlineStruct(sd: *const ast.StructDecl, table: *TypeTable, inner: anytype) TypeId {
|
||||||
const alloc = table.alloc;
|
const alloc = table.alloc;
|
||||||
const name_id = table.internString(sd.name);
|
const name_id = table.internString(sd.name);
|
||||||
|
|
||||||
@@ -516,7 +535,7 @@ fn resolveInlineStruct(sd: *const ast.StructDecl, table: *TypeTable, alias_map:
|
|||||||
|
|
||||||
var fields = std.ArrayList(TypeInfo.StructInfo.Field).empty;
|
var fields = std.ArrayList(TypeInfo.StructInfo.Field).empty;
|
||||||
for (sd.field_names, sd.field_types) |fname, ftype_node| {
|
for (sd.field_names, sd.field_types) |fname, ftype_node| {
|
||||||
const field_ty = resolveAstType(ftype_node, table, alias_map, consts);
|
const field_ty = inner.resolveInner(ftype_node);
|
||||||
fields.append(alloc, .{
|
fields.append(alloc, .{
|
||||||
.name = table.internString(fname),
|
.name = table.internString(fname),
|
||||||
.ty = field_ty,
|
.ty = field_ty,
|
||||||
@@ -531,13 +550,16 @@ fn resolveInlineStruct(sd: *const ast.StructDecl, table: *TypeTable, alias_map:
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stateless inline-union resolution for a FIELD-type position. The TOP-LEVEL
|
/// Inline-union resolution for a FIELD-type position. Field type NAMES resolve
|
||||||
/// per-decl nominal identity path (`Lowering.registerUnionDecl`) shares the body
|
/// through the injected `inner` hook (flat `StatelessInner` from `resolveAstType`,
|
||||||
/// via `buildUnionInfo` but interns under its own nominal id.
|
/// or visibility-aware `*Lowering` from `resolveTypeWithBindings` — issue 0132's
|
||||||
fn resolveInlineUnion(ud: *const ast.UnionDecl, table: *TypeTable, alias_map: AliasMap, consts: ConstMap) TypeId {
|
/// class). The TOP-LEVEL per-decl nominal identity path
|
||||||
|
/// (`Lowering.registerUnionDecl`) shares the body via `buildUnionInfo` but interns
|
||||||
|
/// under its own nominal id.
|
||||||
|
pub fn resolveInlineUnion(ud: *const ast.UnionDecl, table: *TypeTable, inner: anytype) TypeId {
|
||||||
const name_id = table.internString(ud.name);
|
const name_id = table.internString(ud.name);
|
||||||
if (table.findByName(name_id)) |existing| return existing;
|
if (table.findByName(name_id)) |existing| return existing;
|
||||||
const info = buildUnionInfo(ud, table, alias_map, consts);
|
const info = buildUnionInfo(ud, table, inner);
|
||||||
const id = table.internNominal(info, 0);
|
const id = table.internNominal(info, 0);
|
||||||
table.updatePreservingKey(id, info);
|
table.updatePreservingKey(id, info);
|
||||||
return id;
|
return id;
|
||||||
@@ -547,13 +569,13 @@ fn resolveInlineUnion(ud: *const ast.UnionDecl, table: *TypeTable, alias_map: Al
|
|||||||
/// nominal slot — the shared body-BUILDER behind both the stateless inline
|
/// nominal slot — the shared body-BUILDER behind both the stateless inline
|
||||||
/// field-type path (`resolveInlineUnion`) and the stateful per-decl registration
|
/// field-type path (`resolveInlineUnion`) and the stateful per-decl registration
|
||||||
/// (`Lowering.registerUnionDecl`).
|
/// (`Lowering.registerUnionDecl`).
|
||||||
pub fn buildUnionInfo(ud: *const ast.UnionDecl, table: *TypeTable, alias_map: AliasMap, consts: ConstMap) TypeInfo {
|
pub fn buildUnionInfo(ud: *const ast.UnionDecl, table: *TypeTable, inner: anytype) TypeInfo {
|
||||||
const alloc = table.alloc;
|
const alloc = table.alloc;
|
||||||
const name_id = table.internString(ud.name);
|
const name_id = table.internString(ud.name);
|
||||||
|
|
||||||
var fields = std.ArrayList(TypeInfo.StructInfo.Field).empty;
|
var fields = std.ArrayList(TypeInfo.StructInfo.Field).empty;
|
||||||
for (ud.field_names, ud.field_types) |fname, ftype_node| {
|
for (ud.field_names, ud.field_types) |fname, ftype_node| {
|
||||||
const field_ty = resolveAstType(ftype_node, table, alias_map, consts);
|
const field_ty = inner.resolveInner(ftype_node);
|
||||||
fields.append(alloc, .{
|
fields.append(alloc, .{
|
||||||
.name = table.internString(fname),
|
.name = table.internString(fname),
|
||||||
.ty = field_ty,
|
.ty = field_ty,
|
||||||
@@ -589,8 +611,8 @@ fn resolveInlineErrorSet(esd: *const ast.ErrorSetDecl, table: *TypeTable) TypeId
|
|||||||
/// function by the whole-program SCC pass (E1.4); for now every bare `!`
|
/// function by the whole-program SCC pass (E1.4); for now every bare `!`
|
||||||
/// resolves to the same empty inferred set, which is correct while no
|
/// resolves to the same empty inferred set, which is correct while no
|
||||||
/// function raises (E1.3+).
|
/// function raises (E1.3+).
|
||||||
fn resolveErrorType(ete: *const ast.ErrorTypeExpr, table: *TypeTable, alias_map: AliasMap) TypeId {
|
pub fn resolveErrorType(ete: *const ast.ErrorTypeExpr, table: *TypeTable, inner: anytype) TypeId {
|
||||||
if (ete.name) |name| return resolveTypeName(name, table, alias_map, false);
|
if (ete.name) |name| return inner.resolveName(name);
|
||||||
// `!` is not a legal type/identifier name, so this reserved StringId can
|
// `!` is not a legal type/identifier name, so this reserved StringId can
|
||||||
// never collide with a user-declared set.
|
// never collide with a user-declared set.
|
||||||
const name_id = table.internString("!");
|
const name_id = table.internString("!");
|
||||||
|
|||||||
Reference in New Issue
Block a user