Files
sx/src/types.zig
agra e13dbfeb94 refactor(types): shrink src/types.zig to editor/parse metadata (A8.2)
Remove the legacy parallel type model's compiler-like surface. The
compiler pipeline resolves/lowers/lays out against canonical
src/ir/types.zig (TypeId/TypeTable); src/types.zig.Type is now strictly
editor-indexing + parse-time name metadata.

- src/types.zig: delete the type-resolution surface (widen, bitWidth,
  isImplicitlyConvertibleTo) and every helper left dead once it was gone
  (eql, isInt/isFloat/isSigned/isUnsigned, isTuple/isVector, and the
  already-unused classification predicates isEnum/isUnion/isString/
  isStringLike/isAny/optionalChild/sliceElementType/manyPointerElementType/
  vectorElementType/isFunctionType/isClosureType/isCallable). Keep the Type
  union plus the display/name-classification helpers sema/lsp/parser use
  (fromName, fromTypeExpr, toName, displayName, isStruct/isOptional/isSlice/
  isPointer/isManyPointer/isArray, pointerPointeeType). Seal the file with a
  doc comment.
- src/sema.zig: inferExprType no longer calls Type.widen for arithmetic;
  it approximates the display type as the left operand's (no second
  resolver in the editor index).
- src/ir/type_bridge.zig: delete the dead bridgeType (legacy Type -> TypeId)
  function + its sole sx_types import; resolveAstType and the AST->TypeId
  path are untouched.
- src/ir/ir.zig: drop the bridgeType re-export.
- src/ir/type_bridge.test.zig: drop the two bridgeType tests (function gone).

Gate: zig build, zig build test (exit 0), tests/run_examples.sh 361/0,
zero examples/expected churn.
2026-06-03 13:21:00 +03:00

337 lines
12 KiB
Zig
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const std = @import("std");
const ast = @import("ast.zig");
const Node = ast.Node;
/// Editor-indexing and parse-time name metadata — used by `src/sema.zig` (the
/// language-server symbol/type index) for navigation, completion, and hover, and
/// by `src/parser.zig` for parse-time primitive-name classification. This is NOT
/// a compiler type model: it carries no type-resolution surface (no widening,
/// convertibility, or layout). The canonical model the compiler resolves, lowers,
/// and lays out against is `TypeId` / `TypeTable` in `src/ir/types.zig`. Keep this
/// display- and classification-only; never add resolution semantics here.
pub const Type = union(enum) {
// Variable-width integers (164 bits)
signed: u8,
unsigned: u8,
// Fixed-width floats
f32,
f64,
// Other
void_type,
boolean,
string_type,
enum_type: []const u8,
struct_type: []const u8,
union_type: []const u8,
array_type: ArrayTypeInfo,
slice_type: SliceTypeInfo,
pointer_type: PointerTypeInfo,
many_pointer_type: ManyPointerTypeInfo,
vector_type: VectorTypeInfo,
function_type: FunctionTypeInfo,
closure_type: ClosureTypeInfo,
any_type,
usize_type,
isize_type,
optional_type: OptionalTypeInfo,
meta_type: MetaTypeInfo,
tuple_type: TupleTypeInfo,
/// Type resolution failed (sema couldn't infer/resolve). A dedicated
/// sentinel — never a legitimate type — so callers can't mistake it for a
/// real result the way a fabricated `s(64)` would be. Mirrors
/// `ir.TypeId.unresolved`.
unresolved,
pub const SliceTypeInfo = struct {
element_name: []const u8,
};
pub const PointerTypeInfo = struct {
pointee_name: []const u8,
};
pub const ManyPointerTypeInfo = struct {
element_name: []const u8,
};
pub const FunctionTypeInfo = struct {
param_types: []const Type,
return_type: *const Type,
};
pub const ClosureTypeInfo = struct {
param_types: []const Type,
return_type: *const Type,
};
pub const ArrayTypeInfo = struct {
element_name: []const u8,
length: u32,
};
pub const VectorTypeInfo = struct {
element_name: []const u8,
length: u32,
};
pub const OptionalTypeInfo = struct {
child_name: []const u8,
};
pub const MetaTypeInfo = struct {
name: []const u8,
};
pub const TupleTypeInfo = struct {
field_names: ?[]const []const u8, // null for positional tuples
field_types: []const Type,
};
// Convenience constructors
pub fn s(width: u8) Type {
return .{ .signed = width };
}
pub fn u(width: u8) Type {
return .{ .unsigned = width };
}
pub fn fromName(name: []const u8) ?Type {
if (name.len == 0) return null;
return switch (name[0]) {
's' => {
if (std.mem.eql(u8, name, "string")) return .string_type;
if (name.len >= 2) {
const width = std.fmt.parseInt(u8, name[1..], 10) catch return null;
if (width >= 1 and width <= 64) return Type.s(width);
}
return null;
},
'u' => {
if (std.mem.eql(u8, name, "usize")) return .usize_type;
if (name.len >= 2) {
const width = std.fmt.parseInt(u8, name[1..], 10) catch return null;
if (width >= 1 and width <= 64) return Type.u(width);
}
return null;
},
'i' => {
if (std.mem.eql(u8, name, "isize")) return .isize_type;
return null;
},
'b' => if (std.mem.eql(u8, name, "bool")) .boolean else null,
'f' => {
if (std.mem.eql(u8, name, "f32")) return .f32;
if (std.mem.eql(u8, name, "f64")) return .f64;
return null;
},
'?' => if (name.len >= 2) .{ .optional_type = .{ .child_name = name[1..] } } 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,
'[' => {
// Sentinel-terminated slice: [:0]u8 → string_type
if (name.len >= 5 and name[1] == ':') {
if (std.mem.indexOfScalar(u8, name, ']')) |close| {
const sentinel = name[2..close];
const elem = name[close + 1 ..];
if (std.mem.eql(u8, sentinel, "0") and std.mem.eql(u8, elem, "u8")) {
return .string_type;
}
}
}
// Many-pointer: [*]T
if (name.len >= 4 and name[1] == '*' and name[2] == ']') {
return .{ .many_pointer_type = .{ .element_name = name[3..] } };
}
return null;
},
'*' => if (name.len >= 2) .{ .pointer_type = .{ .pointee_name = name[1..] } } else null,
'V' => {
// Vector(N,T)
if (name.len >= 10 and std.mem.startsWith(u8, name, "Vector(") and name[name.len - 1] == ')') {
const inner = name[7 .. name.len - 1];
if (std.mem.indexOfScalar(u8, inner, ',')) |comma| {
const length = std.fmt.parseInt(u32, inner[0..comma], 10) catch return null;
const elem_name = inner[comma + 1 ..];
if (elem_name.len > 0) {
return .{ .vector_type = .{ .element_name = elem_name, .length = length } };
}
}
}
return null;
},
else => null,
};
}
/// Returns the canonical type name for this type, or null for complex types.
/// Used for looking up impl methods on non-struct types (e.g., s32.eq).
pub fn toName(self: Type) ?[]const u8 {
return switch (self) {
.signed => |w| switch (w) {
8 => "s8",
16 => "s16",
32 => "s32",
64 => "s64",
else => null,
},
.unsigned => |w| switch (w) {
8 => "u8",
16 => "u16",
32 => "u32",
64 => "u64",
else => null,
},
.f32 => "f32",
.f64 => "f64",
.boolean => "bool",
.string_type => "string",
.void_type => "void",
.usize_type => "usize",
.isize_type => "isize",
.struct_type => |n| n,
.enum_type => |n| n,
.union_type => |n| n,
else => null,
};
}
pub fn fromTypeExpr(node: *Node) ?Type {
if (node.data != .type_expr) return null;
return fromName(node.data.type_expr.name);
}
pub fn isStruct(self: Type) bool {
return switch (self) {
.struct_type => true,
else => false,
};
}
pub fn isOptional(self: Type) bool {
return switch (self) {
.optional_type => true,
else => false,
};
}
pub fn isSlice(self: Type) bool {
return switch (self) {
.slice_type => true,
else => false,
};
}
pub fn isPointer(self: Type) bool {
return switch (self) {
.pointer_type => true,
else => false,
};
}
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,
else => false,
};
}
pub fn isArray(self: Type) bool {
return switch (self) {
.array_type => true,
else => false,
};
}
fn fmtAlloc(allocator: std.mem.Allocator, comptime fmt: []const u8, args: anytype) ![]const u8 {
var buf: [128]u8 = undefined;
const result = std.fmt.bufPrint(&buf, fmt, args) catch
return try std.fmt.allocPrint(allocator, fmt, args);
return try allocator.dupe(u8, result);
}
/// Format type name for mangling and display (e.g. "s32", "u8", "f64")
pub fn displayName(self: Type, allocator: std.mem.Allocator) ![]const u8 {
return switch (self) {
.signed => |w| {
var buf: [4]u8 = undefined;
const result = std.fmt.bufPrint(&buf, "s{d}", .{w}) catch unreachable;
return try allocator.dupe(u8, result);
},
.unsigned => |w| {
var buf: [4]u8 = undefined;
const result = std.fmt.bufPrint(&buf, "u{d}", .{w}) catch unreachable;
return try allocator.dupe(u8, result);
},
.f32 => "f32",
.f64 => "f64",
.boolean => "bool",
.string_type => "string",
.void_type => "void",
.any_type => "Any",
.usize_type => "usize",
.isize_type => "isize",
.unresolved => "<unresolved>",
.enum_type => |name| name,
.struct_type => |name| name,
.union_type => |name| name,
.slice_type => |info| return fmtAlloc(allocator, "[]{s}", .{info.element_name}),
.pointer_type => |info| return fmtAlloc(allocator, "*{s}", .{info.pointee_name}),
.many_pointer_type => |info| return fmtAlloc(allocator, "[*]{s}", .{info.element_name}),
.array_type => |info| return fmtAlloc(allocator, "[{d}]{s}", .{ info.length, info.element_name }),
.vector_type => |info| return fmtAlloc(allocator, "Vector({d},{s})", .{ info.length, info.element_name }),
.function_type => |info| {
var buf = std.ArrayList(u8).empty;
try buf.append(allocator, '(');
for (info.param_types, 0..) |pt, i| {
if (i > 0) try buf.appendSlice(allocator, ", ");
try buf.appendSlice(allocator, try pt.displayName(allocator));
}
try buf.append(allocator, ')');
if (!std.meta.eql(info.return_type.*, Type.void_type)) {
try buf.appendSlice(allocator, " -> ");
try buf.appendSlice(allocator, try info.return_type.displayName(allocator));
}
return try buf.toOwnedSlice(allocator);
},
.closure_type => |info| {
var buf = std.ArrayList(u8).empty;
try buf.appendSlice(allocator, "Closure(");
for (info.param_types, 0..) |pt, i| {
if (i > 0) try buf.appendSlice(allocator, ", ");
try buf.appendSlice(allocator, try pt.displayName(allocator));
}
try buf.append(allocator, ')');
if (!std.meta.eql(info.return_type.*, Type.void_type)) {
try buf.appendSlice(allocator, " -> ");
try buf.appendSlice(allocator, try info.return_type.displayName(allocator));
}
return try buf.toOwnedSlice(allocator);
},
.optional_type => |info| return fmtAlloc(allocator, "?{s}", .{info.child_name}),
.meta_type => |info| info.name,
.tuple_type => |info| {
var buf = std.ArrayList(u8).empty;
try buf.append(allocator, '(');
for (info.field_types, 0..) |ft, i| {
if (i > 0) try buf.appendSlice(allocator, ", ");
if (info.field_names) |names| {
try buf.appendSlice(allocator, names[i]);
try buf.appendSlice(allocator, ": ");
}
try buf.appendSlice(allocator, try ft.displayName(allocator));
}
try buf.append(allocator, ')');
return try buf.toOwnedSlice(allocator);
},
};
}
};