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 }