feat(lang): raw provenance through ALL sema compound type metadata — finish universal raw identifier in the LSP classifier [F0.6]

The codegen-side resolver was already raw-aware for the universal model;
the sema/LSP editor index (the second classifier) only honored the DIRECT
raw type. A COMPOUND raw type (`*`s2`, `?`s2`, `[N]`s2`, `[]`s2`, `[*]`s2`)
stores its inner type-name as a bare string on the Type info struct, and
every resolution site re-read it with skip_builtin=false — so the index
reclassified a user type named `s2` as the builtin int, diverging from
codegen (issue-0083 class, LSP surface only; codegen unchanged).

Structural cure: every compound info struct (Pointer/Optional/Slice/
ManyPointer/Array) carries a REQUIRED is_raw bit (no default — a future
construction site cannot drop it). is_raw is set at every construction
site (resolveTypeNode arms, fieldType arms, variadic slice, .ptr/slice_expr
derivation, for-loop by-ref, substType) and passed as skip_builtin at every
resolution site (elementTypeOf, field-access pointer unwrap, index, deref,
optional unwrap/null-coalesce, if/while optional binding, match subject).
Optional-unwrap + deref sites converted from Type.fromName/pointerPointeeType
(builtin-only, divergent) to resolveTypeNameStr(name, is_raw); the now-dead
pointerPointeeType removed.

Tests: src/sema.test.zig gains pointer/optional/array raw-vs-bare
regressions (raw → user type, bare → builtin control) — each FAILS on
pre-fix sema, PASSES after — plus a parameterized-raw coverage test.
This commit is contained in:
agra
2026-06-04 21:46:31 +03:00
parent ef8f021c01
commit 724a919fc1
4 changed files with 223 additions and 48 deletions

View File

@@ -42,16 +42,26 @@ pub const Type = union(enum) {
/// `ir.TypeId.unresolved`.
unresolved,
/// `is_raw` records whether the inner type-name came from a backtick raw
/// reference (`` `s2 ``) or an already-resolved user type. It is the
/// `skip_builtin` the resolver MUST pass when re-resolving the stored inner
/// name (issue 0089) — without it `resolveTypeNameStr` would reclassify a
/// user type named `s2` as the builtin int, diverging from codegen. The
/// field is REQUIRED (no default) so a future construction site cannot
/// silently drop the bit, the way the LSP index did for compound shapes.
pub const SliceTypeInfo = struct {
element_name: []const u8,
is_raw: bool,
};
pub const PointerTypeInfo = struct {
pointee_name: []const u8,
is_raw: bool,
};
pub const ManyPointerTypeInfo = struct {
element_name: []const u8,
is_raw: bool,
};
pub const FunctionTypeInfo = struct {
@@ -67,6 +77,7 @@ pub const Type = union(enum) {
pub const ArrayTypeInfo = struct {
element_name: []const u8,
length: u32,
is_raw: bool,
};
pub const VectorTypeInfo = struct {
@@ -76,6 +87,7 @@ pub const Type = union(enum) {
pub const OptionalTypeInfo = struct {
child_name: []const u8,
is_raw: bool,
};
pub const MetaTypeInfo = struct {
@@ -125,7 +137,7 @@ pub const Type = union(enum) {
if (std.mem.eql(u8, name, "f64")) return .f64;
return null;
},
'?' => if (name.len >= 2) .{ .optional_type = .{ .child_name = name[1..] } } else null,
'?' => if (name.len >= 2) .{ .optional_type = .{ .child_name = name[1..], .is_raw = false } } else null,
'A' => if (std.mem.eql(u8, name, "Any")) .any_type else null,
'v' => if (std.mem.eql(u8, name, "void")) .void_type else null,
'[' => {
@@ -141,11 +153,11 @@ pub const Type = union(enum) {
}
// Many-pointer: [*]T
if (name.len >= 4 and name[1] == '*' and name[2] == ']') {
return .{ .many_pointer_type = .{ .element_name = name[3..] } };
return .{ .many_pointer_type = .{ .element_name = name[3..], .is_raw = false } };
}
return null;
},
'*' => if (name.len >= 2) .{ .pointer_type = .{ .pointee_name = name[1..] } } else null,
'*' => if (name.len >= 2) .{ .pointer_type = .{ .pointee_name = name[1..], .is_raw = false } } else null,
'V' => {
// Vector(N,T)
if (name.len >= 10 and std.mem.startsWith(u8, name, "Vector(") and name[name.len - 1] == ')') {
@@ -235,13 +247,6 @@ pub const Type = union(enum) {
};
}
pub fn pointerPointeeType(self: Type) ?Type {
return switch (self) {
.pointer_type => |info| fromName(info.pointee_name),
else => null,
};
}
pub fn isManyPointer(self: Type) bool {
return switch (self) {
.many_pointer_type => true,