lib/chant/src/parse/auto.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const ast = @import("../ast/root.zig");
3 const expression = @import("expression.zig");
4 const state = @import("state/root.zig");
5
6 const diagnostic = state.diagnostic;
7 const memory = state.memory;
8 const Error = @import("error.zig").Error;
9 const Parser = state.Parser;
10
11 pub fn resolve(parser: *Parser, declared: *const ast.Type, initializer: ?*ast.Expr) Error!*const ast.Type {
12 if (!contains(declared)) return declared;
13 const value = initializer orelse
14 return diagnostic.fail(parser, error.ExpectedType, "auto declarations require an initializer");
15 const inferred = (try expression.inferType(parser, value)) orelse
16 return diagnostic.fail(parser, error.UnsupportedConstruct, "auto initializer type cannot be inferred");
17 const resolved = (try replace(parser, declared, inferred)) orelse
18 return diagnostic.fail(parser, error.UnsupportedConstruct, "auto declarator shape does not match initializer");
19 return resolved;
20 }
21
22 pub fn contains(c_type: *const ast.Type) bool {
23 if (c_type.kind == .auto_type) return true;
24 if (c_type.child) |child| return contains(child);
25 return false;
26 }
27
28 fn replace(parser: *Parser, declared: *const ast.Type, inferred: *const ast.Type) Error!?*const ast.Type {
29 if (declared.kind == .auto_type) return inferred;
30 if (!contains(declared)) return declared;
31 if (declared.kind != inferred.kind) return null;
32 const type_origin = parser.nodes.typeOrigin(declared) orelse unreachable;
33 const node = try memory.createType(
34 parser,
35 declared.*,
36 memory.derivedType(type_origin, false),
37 );
38 if (declared.child) |declared_child| {
39 const inferred_child = inferred.child orelse return null;
40 node.child = (try replace(parser, declared_child, inferred_child)) orelse return null;
41 }
42 node.is_unsigned = inferred.is_unsigned;
43 node.array_len = inferred.array_len;
44 node.vla_len = inferred.vla_len;
45 node.params = inferred.params;
46 node.variadic = inferred.variadic;
47 return node;
48 }
49
50 test "auto resolves simple and pointer declarations" {
51 const lexer = @import("../lexer/root.zig");
52 var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);
53 defer arena_state.deinit();
54 const arena = arena_state.allocator();
55
56 const source = "1 + 0";
57 const token_survey = try lexer.survey(source, "auto.c");
58 const capacity = try lexer.Capacity.derive(token_survey.limits);
59 const bytes = try arena.alignedAlloc(
60 u8,
61 .fromByteUnits(lexer.Storage.storage_alignment),
62 capacity.storage_bytes,
63 );
64 var storage = try lexer.Storage.init(bytes, token_survey.limits);
65 storage.activate();
66 defer _ = storage.deinit();
67 const tokens = try storage.fill(token_survey, source, "auto.c");
68 var parser = try @import("state/test.zig").initParser(arena, tokens);
69 const expr = try expression.parseExpression(&parser);
70 const scalar = try resolve(&parser, &ast.types.auto_type, expr);
71 try std.testing.expectEqual(ast.types.Kind.int_type, scalar.kind);
72
73 const pointer = try memory.createType(
74 &parser,
75 .{ .kind = .pointer, .child = &ast.types.auto_type },
76 memory.directType(0, null),
77 );
78 const inferred = try memory.createType(
79 &parser,
80 .{ .kind = .pointer, .child = &ast.types.double_type },
81 memory.directType(1, null),
82 );
83 const resolved = (try replace(&parser, pointer, inferred)).?;
84 try std.testing.expectEqual(ast.types.Kind.double_type, resolved.child.?.kind);
85 }