feat(types): nominal identity + key-safe TypeTable mutation, ban raw update [stdlib D]
Phase D of the unified resolver: make the TypeTable safe to key by nominal
identity before same-name type shadows land (Phase E). Behavior-preserving —
nominal_id=0 means structural (today's keying, byte-identical); single-author
names intern to the same TypeId as before.
types.zig:
- StructInfo/EnumInfo/UnionInfo/TaggedUnionInfo/ErrorSetInfo gain
`nominal_id: u32 = 0`. hash/eql fold it into the nominal arms ONLY, and only
when nonzero, so legacy (structural) interning hashes/compares byte-identically.
- internNominal(info, nominal_id): stamps the id into the nominal arm then
interns; nonzero id on a non-nominal info trips an assert.
- updatePreservingKey(id, info): field-fill that asserts the intern key is
unchanged (replaces the forward-decl stub→full pattern).
- replaceKeyedInfo(id, info): the one legitimate re-key (anon rename
__anon → Parent.field); removes the stale key and installs the new one.
- findUniqueByName: quarantined findByName that asserts ≤1 match.
- type_decl_tids: decl-node → TypeId identity map (the fn_decl_fids analogue),
consumed by the resolver in Phase E.
Ban raw TypeTable.update outside types.zig (the acceptance bar): every caller
in lower.zig / type_bridge.zig / protocols.zig is reclassified — forward-decl
field fills route through updatePreservingKey, qualifyAnonType's rename through
replaceKeyedInfo. The raw `update` method is removed. Inline named type-decl
registration ("current winners") routes through internNominal(info, 0).
Tests (types.test.zig): forward-decl field fill (stable key), anon rename
(re-key), generic struct instantiation, type-returning function, parameterized
protocol value struct, same display-name → distinct nominal ids, plus an
old==new assertion (internNominal(.,0) byte-identical to legacy intern),
findUniqueByName, and the type_decl_tids identity map.
Gate: zig build (0), zig build test (421/421), run_examples (477, byte-identical),
m3te ios-sim build via worktree binary (0). No shadows registered; stubs intact.
This commit is contained in:
@@ -914,7 +914,7 @@ pub const Lowering = struct {
|
||||
.fields = inst_info.@"struct".fields,
|
||||
} };
|
||||
const alias_id = if (self.module.types.findByName(alias_name_id)) |existing| existing else self.module.types.intern(alias_info);
|
||||
self.module.types.update(alias_id, alias_info);
|
||||
self.module.types.updatePreservingKey(alias_id, alias_info);
|
||||
}
|
||||
} else if (std.mem.eql(u8, callee_name, "Vector")) {
|
||||
// Builtin type constructor — checked BEFORE
|
||||
@@ -951,7 +951,7 @@ pub const Lowering = struct {
|
||||
.fields = inst_info.@"struct".fields,
|
||||
} };
|
||||
const alias_id = if (self.module.types.findByName(alias_name_id)) |existing| existing else self.module.types.intern(alias_info);
|
||||
self.module.types.update(alias_id, alias_info);
|
||||
self.module.types.updatePreservingKey(alias_id, alias_info);
|
||||
}
|
||||
} else {
|
||||
// Builtin parameterised type (Vector(N, T) etc) —
|
||||
@@ -13242,7 +13242,7 @@ pub const Lowering = struct {
|
||||
// Register the monomorphized struct
|
||||
const info: types.TypeInfo = .{ .@"struct" = .{ .name = name_id, .fields = fields.items } };
|
||||
const id = if (table.findByName(name_id)) |existing| existing else table.intern(info);
|
||||
table.update(id, info);
|
||||
table.updatePreservingKey(id, info);
|
||||
|
||||
// Bind the template name to this concrete instance so a method's
|
||||
// `self: *Combined` (the template name) resolves to `*Combined__s64_s64`
|
||||
@@ -13347,7 +13347,7 @@ pub const Lowering = struct {
|
||||
.fields = struct_fields.items,
|
||||
} };
|
||||
const mangled_id = if (table.findByName(mangled_name_id)) |existing| existing else table.intern(mangled_info);
|
||||
table.update(mangled_id, mangled_info);
|
||||
table.updatePreservingKey(mangled_id, mangled_info);
|
||||
|
||||
// If there's a real alias, also register under alias name and in alias map
|
||||
if (has_alias) {
|
||||
@@ -13357,7 +13357,7 @@ pub const Lowering = struct {
|
||||
.fields = struct_fields.items,
|
||||
} };
|
||||
const alias_id = if (table.findByName(alias_name_id)) |existing| existing else table.intern(alias_info);
|
||||
table.update(alias_id, alias_info);
|
||||
table.updatePreservingKey(alias_id, alias_info);
|
||||
|
||||
// Store defaults if any
|
||||
if (struct_decl.field_defaults.len > 0) {
|
||||
@@ -13429,7 +13429,7 @@ pub const Lowering = struct {
|
||||
.tag_type = .s64,
|
||||
} };
|
||||
const id = if (table.findByName(alias_name_id)) |existing| existing else table.intern(info);
|
||||
table.update(id, info);
|
||||
table.updatePreservingKey(id, info);
|
||||
|
||||
// Also register under mangled name
|
||||
if (!std.mem.eql(u8, alias_name, mangled_name)) {
|
||||
@@ -13440,7 +13440,7 @@ pub const Lowering = struct {
|
||||
.tag_type = .s64,
|
||||
} };
|
||||
const mid = if (table.findByName(mangled_name_id)) |existing| existing else table.intern(mangled_info);
|
||||
table.update(mid, mangled_info);
|
||||
table.updatePreservingKey(mid, mangled_info);
|
||||
}
|
||||
|
||||
return id;
|
||||
@@ -13621,8 +13621,8 @@ pub const Lowering = struct {
|
||||
// Check if a forward-reference placeholder already exists (with empty fields)
|
||||
// If so, update it in-place rather than creating a duplicate
|
||||
const info: types.TypeInfo = .{ .@"struct" = .{ .name = name_id, .fields = fields.items } };
|
||||
const id = if (table.findByName(name_id)) |existing| existing else table.intern(info);
|
||||
table.update(id, info);
|
||||
const id = if (table.findByName(name_id)) |existing| existing else table.internNominal(info, 0);
|
||||
table.updatePreservingKey(id, info);
|
||||
|
||||
// Store field defaults for struct literal lowering
|
||||
if (sd.field_defaults.len > 0) {
|
||||
@@ -13668,7 +13668,7 @@ pub const Lowering = struct {
|
||||
if (!std.mem.eql(u8, old_name, "__anon")) return;
|
||||
const qualified = std.fmt.allocPrint(self.alloc, "{s}.{s}", .{ parent_name, field_name }) catch return;
|
||||
const qname_id = table.internString(qualified);
|
||||
table.update(ty, .{ .@"union" = .{ .name = qname_id, .fields = u.fields } });
|
||||
table.replaceKeyedInfo(ty, .{ .@"union" = .{ .name = qname_id, .fields = u.fields } });
|
||||
},
|
||||
.tagged_union => |u| {
|
||||
const old_name = table.getString(u.name);
|
||||
@@ -13685,26 +13685,26 @@ pub const Lowering = struct {
|
||||
const suffix = sname["__anon".len..]; // .VariantName
|
||||
const sq = std.fmt.allocPrint(self.alloc, "{s}{s}", .{ qualified, suffix }) catch continue;
|
||||
const sq_id = table.internString(sq);
|
||||
table.update(f.ty, .{ .@"struct" = .{ .name = sq_id, .fields = finfo.@"struct".fields } });
|
||||
table.replaceKeyedInfo(f.ty, .{ .@"struct" = .{ .name = sq_id, .fields = finfo.@"struct".fields } });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
table.update(ty, .{ .tagged_union = .{ .name = qname_id, .fields = u.fields, .tag_type = u.tag_type, .backing_type = u.backing_type, .explicit_tag_values = u.explicit_tag_values } });
|
||||
table.replaceKeyedInfo(ty, .{ .tagged_union = .{ .name = qname_id, .fields = u.fields, .tag_type = u.tag_type, .backing_type = u.backing_type, .explicit_tag_values = u.explicit_tag_values } });
|
||||
},
|
||||
.@"enum" => |e| {
|
||||
const old_name = table.getString(e.name);
|
||||
if (!std.mem.eql(u8, old_name, "__anon")) return;
|
||||
const qualified = std.fmt.allocPrint(self.alloc, "{s}.{s}", .{ parent_name, field_name }) catch return;
|
||||
const qname_id = table.internString(qualified);
|
||||
table.update(ty, .{ .@"enum" = .{ .name = qname_id, .variants = e.variants, .explicit_values = e.explicit_values } });
|
||||
table.replaceKeyedInfo(ty, .{ .@"enum" = .{ .name = qname_id, .variants = e.variants, .explicit_values = e.explicit_values } });
|
||||
},
|
||||
.@"struct" => |s| {
|
||||
const old_name = table.getString(s.name);
|
||||
if (!std.mem.eql(u8, old_name, "__anon")) return;
|
||||
const qualified = std.fmt.allocPrint(self.alloc, "{s}.{s}", .{ parent_name, field_name }) catch return;
|
||||
const qname_id = table.internString(qualified);
|
||||
table.update(ty, .{ .@"struct" = .{ .name = qname_id, .fields = s.fields } });
|
||||
table.replaceKeyedInfo(ty, .{ .@"struct" = .{ .name = qname_id, .fields = s.fields } });
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
@@ -13758,7 +13758,7 @@ pub const Lowering = struct {
|
||||
}
|
||||
const struct_info: types.TypeInfo = .{ .@"struct" = .{ .name = name_id, .fields = fields.items, .is_protocol = true } };
|
||||
const id = if (table.findByName(name_id)) |existing| existing else table.intern(struct_info);
|
||||
table.update(id, struct_info);
|
||||
table.updatePreservingKey(id, struct_info);
|
||||
|
||||
// Method infos resolved with the type-arg binding (T → s64).
|
||||
const saved_tb = self.type_bindings;
|
||||
|
||||
Reference in New Issue
Block a user