ufcs
This commit is contained in:
73
src/sema.zig
73
src/sema.zig
@@ -385,6 +385,12 @@ pub const Analyzer = struct {
|
||||
if (self.struct_types.contains(target)) return .{ .struct_type = target };
|
||||
}
|
||||
} else if (sl.type_expr) |te| {
|
||||
// Handle parameterized struct: List(s32).{} parses as call node
|
||||
if (te.data == .call) {
|
||||
if (self.resolveCalleeName(te.data.call)) |callee| {
|
||||
if (self.struct_types.contains(callee)) return .{ .struct_type = callee };
|
||||
}
|
||||
}
|
||||
return self.inferExprType(te);
|
||||
}
|
||||
return .void_type;
|
||||
@@ -397,6 +403,10 @@ pub const Analyzer = struct {
|
||||
.null_literal => .void_type,
|
||||
.array_literal => .void_type,
|
||||
.type_expr => |te| .{ .meta_type = .{ .name = te.name } },
|
||||
.parameterized_type_expr => |pte| {
|
||||
if (self.struct_types.contains(pte.name)) return .{ .struct_type = pte.name };
|
||||
return .void_type;
|
||||
},
|
||||
else => .void_type,
|
||||
};
|
||||
}
|
||||
@@ -554,7 +564,8 @@ pub const Analyzer = struct {
|
||||
if (vd.value) |val| {
|
||||
try self.analyzeNode(val);
|
||||
}
|
||||
const ty = resolveTypeAnnotation(vd.type_annotation);
|
||||
const ty = resolveTypeAnnotation(vd.type_annotation) orelse
|
||||
if (vd.value) |val| self.inferExprType(val) else null;
|
||||
try self.addSymbol(vd.name, .variable, ty, node.span);
|
||||
},
|
||||
.enum_decl => |ed| {
|
||||
@@ -1057,3 +1068,63 @@ test "sema: enum and struct declarations" {
|
||||
try std.testing.expectEqual(SymbolKind.struct_type, result.symbols[1].kind);
|
||||
try std.testing.expectEqualStrings("main", result.symbols[2].name);
|
||||
}
|
||||
|
||||
test "sema: var_decl infers struct type from parameterized struct literal" {
|
||||
const parser_mod = @import("parser.zig");
|
||||
|
||||
const source = "List :: struct { len: s64; } main :: () { list := List.{}; }";
|
||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
var parser = parser_mod.Parser.init(alloc, source);
|
||||
const root = try parser.parse();
|
||||
|
||||
var analyzer = Analyzer.init(alloc);
|
||||
const result = try analyzer.analyze(root);
|
||||
|
||||
// Find the 'list' variable symbol
|
||||
var found_list = false;
|
||||
for (result.symbols) |sym| {
|
||||
if (std.mem.eql(u8, sym.name, "list")) {
|
||||
found_list = true;
|
||||
try std.testing.expectEqual(SymbolKind.variable, sym.kind);
|
||||
// Must have inferred struct type
|
||||
const ty = sym.ty orelse return error.TestUnexpectedResult;
|
||||
try std.testing.expect(ty == .struct_type);
|
||||
try std.testing.expectEqualStrings("List", ty.struct_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
try std.testing.expect(found_list);
|
||||
}
|
||||
|
||||
test "sema: var_decl infers struct type from parameterized call literal" {
|
||||
const parser_mod = @import("parser.zig");
|
||||
|
||||
// List(s32).{} — parser produces struct_literal with type_expr = call node
|
||||
const source = "List :: struct { len: s64; } main :: () { list := List(s32).{}; }";
|
||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
var parser = parser_mod.Parser.init(alloc, source);
|
||||
const root = try parser.parse();
|
||||
|
||||
var analyzer = Analyzer.init(alloc);
|
||||
const result = try analyzer.analyze(root);
|
||||
|
||||
// Find the 'list' variable symbol
|
||||
var found_list = false;
|
||||
for (result.symbols) |sym| {
|
||||
if (std.mem.eql(u8, sym.name, "list")) {
|
||||
found_list = true;
|
||||
try std.testing.expectEqual(SymbolKind.variable, sym.kind);
|
||||
const ty = sym.ty orelse return error.TestUnexpectedResult;
|
||||
try std.testing.expect(ty == .struct_type);
|
||||
try std.testing.expectEqualStrings("List", ty.struct_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
try std.testing.expect(found_list);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user