authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-12-16 23:01:24+00:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-12-16 23:01:24+00:00
log7e8be213631821b0b767aec03c22a937541b7d0a
treeedbfa24f29e3788a81e92532aefad74d61c2b43b
parent32354d1190a9d319f0f7970d6e39d2d951f2d3f4
parentc7485d73ac3e6dd105c6ee8fcf774493cc9eb31e
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #22250 from mlugg/zon-ast

compiler: introduce ZonGen and make `ast-check` run it for ZON inputs

9 files changed, 1531 insertions(+), 191 deletions(-)

lib/std/zig.zig+2
...@@ -14,6 +14,8 @@ pub const isPrimitive = primitives.isPrimitive;...@@ -14,6 +14,8 @@ pub const isPrimitive = primitives.isPrimitive;
14pub const Ast = @import("zig/Ast.zig");14pub const Ast = @import("zig/Ast.zig");
15pub const AstGen = @import("zig/AstGen.zig");15pub const AstGen = @import("zig/AstGen.zig");
16pub const Zir = @import("zig/Zir.zig");16pub const Zir = @import("zig/Zir.zig");
17pub const Zoir = @import("zig/Zoir.zig");
18pub const ZonGen = @import("zig/ZonGen.zig");
17pub const system = @import("zig/system.zig");19pub const system = @import("zig/system.zig");
18pub const CrossTarget = @compileError("deprecated; use std.Target.Query");20pub const CrossTarget = @compileError("deprecated; use std.Target.Query");
19pub const BuiltinFn = @import("zig/BuiltinFn.zig");21pub const BuiltinFn = @import("zig/BuiltinFn.zig");
lib/std/zig/AstGen.zig+25-89
...@@ -130,6 +130,8 @@ fn appendRefsAssumeCapacity(astgen: *AstGen, refs: []const Zir.Inst.Ref) void {...@@ -130,6 +130,8 @@ fn appendRefsAssumeCapacity(astgen: *AstGen, refs: []const Zir.Inst.Ref) void {
130}130}
131131
132pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {132pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {
133 assert(tree.mode == .zig);
134
133 var arena = std.heap.ArenaAllocator.init(gpa);135 var arena = std.heap.ArenaAllocator.init(gpa);
134 defer arena.deinit();136 defer arena.deinit();
135137
...@@ -11413,83 +11415,7 @@ fn parseStrLit(...@@ -11413,83 +11415,7 @@ fn parseStrLit(
1141311415
11414fn failWithStrLitError(astgen: *AstGen, err: std.zig.string_literal.Error, token: Ast.TokenIndex, bytes: []const u8, offset: u32) InnerError {11416fn failWithStrLitError(astgen: *AstGen, err: std.zig.string_literal.Error, token: Ast.TokenIndex, bytes: []const u8, offset: u32) InnerError {
11415 const raw_string = bytes[offset..];11417 const raw_string = bytes[offset..];
11416 switch (err) {11418 return err.lower(raw_string, offset, AstGen.failOff, .{ astgen, token });
11417 .invalid_escape_character => |bad_index| {
11418 return astgen.failOff(
11419 token,
11420 offset + @as(u32, @intCast(bad_index)),
11421 "invalid escape character: '{c}'",
11422 .{raw_string[bad_index]},
11423 );
11424 },
11425 .expected_hex_digit => |bad_index| {
11426 return astgen.failOff(
11427 token,
11428 offset + @as(u32, @intCast(bad_index)),
11429 "expected hex digit, found '{c}'",
11430 .{raw_string[bad_index]},
11431 );
11432 },
11433 .empty_unicode_escape_sequence => |bad_index| {
11434 return astgen.failOff(
11435 token,
11436 offset + @as(u32, @intCast(bad_index)),
11437 "empty unicode escape sequence",
11438 .{},
11439 );
11440 },
11441 .expected_hex_digit_or_rbrace => |bad_index| {
11442 return astgen.failOff(
11443 token,
11444 offset + @as(u32, @intCast(bad_index)),
11445 "expected hex digit or '}}', found '{c}'",
11446 .{raw_string[bad_index]},
11447 );
11448 },
11449 .invalid_unicode_codepoint => |bad_index| {
11450 return astgen.failOff(
11451 token,
11452 offset + @as(u32, @intCast(bad_index)),
11453 "unicode escape does not correspond to a valid unicode scalar value",
11454 .{},
11455 );
11456 },
11457 .expected_lbrace => |bad_index| {
11458 return astgen.failOff(
11459 token,
11460 offset + @as(u32, @intCast(bad_index)),
11461 "expected '{{', found '{c}",
11462 .{raw_string[bad_index]},
11463 );
11464 },
11465 .expected_rbrace => |bad_index| {
11466 return astgen.failOff(
11467 token,
11468 offset + @as(u32, @intCast(bad_index)),
11469 "expected '}}', found '{c}",
11470 .{raw_string[bad_index]},
11471 );
11472 },
11473 .expected_single_quote => |bad_index| {
11474 return astgen.failOff(
11475 token,
11476 offset + @as(u32, @intCast(bad_index)),
11477 "expected single quote ('), found '{c}",
11478 .{raw_string[bad_index]},
11479 );
11480 },
11481 .invalid_character => |bad_index| {
11482 return astgen.failOff(
11483 token,
11484 offset + @as(u32, @intCast(bad_index)),
11485 "invalid byte in string or character literal: '{c}'",
11486 .{raw_string[bad_index]},
11487 );
11488 },
11489 .empty_char_literal => {
11490 return astgen.failOff(token, offset, "empty character literal", .{});
11491 },
11492 }
11493}11419}
1149411420
11495fn failNode(11421fn failNode(
...@@ -14019,30 +13945,40 @@ fn emitDbgStmtForceCurrentIndex(gz: *GenZir, lc: LineColumn) !void {...@@ -14019,30 +13945,40 @@ fn emitDbgStmtForceCurrentIndex(gz: *GenZir, lc: LineColumn) !void {
14019}13945}
1402013946
14021fn lowerAstErrors(astgen: *AstGen) !void {13947fn lowerAstErrors(astgen: *AstGen) !void {
13948 const gpa = astgen.gpa;
14022 const tree = astgen.tree;13949 const tree = astgen.tree;
14023 assert(tree.errors.len > 0);13950 assert(tree.errors.len > 0);
1402413951
14025 const gpa = astgen.gpa;
14026 const parse_err = tree.errors[0];
14027
14028 var msg: std.ArrayListUnmanaged(u8) = .empty;13952 var msg: std.ArrayListUnmanaged(u8) = .empty;
14029 defer msg.deinit(gpa);13953 defer msg.deinit(gpa);
1403013954
14031 var notes: std.ArrayListUnmanaged(u32) = .empty;13955 var notes: std.ArrayListUnmanaged(u32) = .empty;
14032 defer notes.deinit(gpa);13956 defer notes.deinit(gpa);
1403313957
14034 for (tree.errors[1..]) |note| {13958 var cur_err = tree.errors[0];
14035 if (!note.is_note) break;13959 for (tree.errors[1..]) |err| {
1403613960 if (err.is_note) {
13961 try tree.renderError(err, msg.writer(gpa));
13962 try notes.append(gpa, try astgen.errNoteTok(err.token, "{s}", .{msg.items}));
13963 } else {
13964 // Flush error
13965 const extra_offset = tree.errorOffset(cur_err);
13966 try tree.renderError(cur_err, msg.writer(gpa));
13967 try astgen.appendErrorTokNotesOff(cur_err.token, extra_offset, "{s}", .{msg.items}, notes.items);
13968 notes.clearRetainingCapacity();
13969 cur_err = err;
13970
13971 // TODO: `Parse` currently does not have good error recovery mechanisms, so the remaining errors could be bogus.
13972 // As such, we'll ignore all remaining errors for now. We should improve `Parse` so that we can report all the errors.
13973 return;
13974 }
14037 msg.clearRetainingCapacity();13975 msg.clearRetainingCapacity();
14038 try tree.renderError(note, msg.writer(gpa));
14039 try notes.append(gpa, try astgen.errNoteTok(note.token, "{s}", .{msg.items}));
14040 }13976 }
1404113977
14042 const extra_offset = tree.errorOffset(parse_err);13978 // Flush error
14043 msg.clearRetainingCapacity();13979 const extra_offset = tree.errorOffset(cur_err);
14044 try tree.renderError(parse_err, msg.writer(gpa));13980 try tree.renderError(cur_err, msg.writer(gpa));
14045 try astgen.appendErrorTokNotesOff(parse_err.token, extra_offset, "{s}", .{msg.items}, notes.items);13981 try astgen.appendErrorTokNotesOff(cur_err.token, extra_offset, "{s}", .{msg.items}, notes.items);
14046}13982}
1404713983
14048const DeclarationName = union(enum) {13984const DeclarationName = union(enum) {
lib/std/zig/ErrorBundle.zig+72-1
...@@ -507,7 +507,7 @@ pub const Wip = struct {...@@ -507,7 +507,7 @@ pub const Wip = struct {
507 }507 }
508508
509 if (item.data.notes != 0) {509 if (item.data.notes != 0) {
510 const notes_start = try eb.reserveNotes(item.data.notes);510 const notes_start = try eb.reserveNotes(item.data.notesLen(zir));
511 const block = zir.extraData(Zir.Inst.Block, item.data.notes);511 const block = zir.extraData(Zir.Inst.Block, item.data.notes);
512 const body = zir.extra[block.end..][0..block.data.body_len];512 const body = zir.extra[block.end..][0..block.data.body_len];
513 for (notes_start.., body) |note_i, body_elem| {513 for (notes_start.., body) |note_i, body_elem| {
...@@ -547,6 +547,77 @@ pub const Wip = struct {...@@ -547,6 +547,77 @@ pub const Wip = struct {
547 }547 }
548 }548 }
549549
550 pub fn addZoirErrorMessages(
551 eb: *ErrorBundle.Wip,
552 zoir: std.zig.Zoir,
553 tree: std.zig.Ast,
554 source: [:0]const u8,
555 src_path: []const u8,
556 ) !void {
557 assert(zoir.hasCompileErrors());
558
559 for (zoir.compile_errors) |err| {
560 const err_span: std.zig.Ast.Span = span: {
561 if (err.token == std.zig.Zoir.CompileError.invalid_token) {
562 break :span tree.nodeToSpan(err.node_or_offset);
563 }
564 const token_start = tree.tokens.items(.start)[err.token];
565 const start = token_start + err.node_or_offset;
566 const end = token_start + @as(u32, @intCast(tree.tokenSlice(err.token).len));
567 break :span .{ .start = start, .end = end, .main = start };
568 };
569 const err_loc = std.zig.findLineColumn(source, err_span.main);
570
571 try eb.addRootErrorMessage(.{
572 .msg = try eb.addString(err.msg.get(zoir)),
573 .src_loc = try eb.addSourceLocation(.{
574 .src_path = try eb.addString(src_path),
575 .span_start = err_span.start,
576 .span_main = err_span.main,
577 .span_end = err_span.end,
578 .line = @intCast(err_loc.line),
579 .column = @intCast(err_loc.column),
580 .source_line = try eb.addString(err_loc.source_line),
581 }),
582 .notes_len = err.note_count,
583 });
584
585 const notes_start = try eb.reserveNotes(err.note_count);
586 for (notes_start.., err.first_note.., 0..err.note_count) |eb_note_idx, zoir_note_idx, _| {
587 const note = zoir.error_notes[zoir_note_idx];
588 const note_span: std.zig.Ast.Span = span: {
589 if (note.token == std.zig.Zoir.CompileError.invalid_token) {
590 break :span tree.nodeToSpan(note.node_or_offset);
591 }
592 const token_start = tree.tokens.items(.start)[note.token];
593 const start = token_start + note.node_or_offset;
594 const end = token_start + @as(u32, @intCast(tree.tokenSlice(note.token).len));
595 break :span .{ .start = start, .end = end, .main = start };
596 };
597 const note_loc = std.zig.findLineColumn(source, note_span.main);
598
599 // This line can cause `wip.extra.items` to be resized.
600 const note_index = @intFromEnum(try eb.addErrorMessage(.{
601 .msg = try eb.addString(note.msg.get(zoir)),
602 .src_loc = try eb.addSourceLocation(.{
603 .src_path = try eb.addString(src_path),
604 .span_start = note_span.start,
605 .span_main = note_span.main,
606 .span_end = note_span.end,
607 .line = @intCast(note_loc.line),
608 .column = @intCast(note_loc.column),
609 .source_line = if (note_loc.eql(err_loc))
610 0
611 else
612 try eb.addString(note_loc.source_line),
613 }),
614 .notes_len = 0,
615 }));
616 eb.extra.items[eb_note_idx] = note_index;
617 }
618 }
619 }
620
550 fn addOtherMessage(wip: *Wip, other: ErrorBundle, msg_index: MessageIndex) !MessageIndex {621 fn addOtherMessage(wip: *Wip, other: ErrorBundle, msg_index: MessageIndex) !MessageIndex {
551 const other_msg = other.getErrorMessage(msg_index);622 const other_msg = other.getErrorMessage(msg_index);
552 const src_loc = try wip.addOtherSourceLocation(other, other_msg.src_loc);623 const src_loc = try wip.addOtherSourceLocation(other, other_msg.src_loc);
lib/std/zig/Zoir.zig created+239
...@@ -0,0 +1,239 @@
1//! Zig Object Intermediate Representation.
2//! Simplified AST for the ZON (Zig Object Notation) format.
3//! `ZonGen` converts `Ast` to `Zoir`.
4
5nodes: std.MultiArrayList(Node.Repr).Slice,
6extra: []u32,
7limbs: []std.math.big.Limb,
8string_bytes: []u8,
9
10compile_errors: []Zoir.CompileError,
11error_notes: []Zoir.CompileError.Note,
12
13pub fn hasCompileErrors(zoir: Zoir) bool {
14 if (zoir.compile_errors.len > 0) {
15 assert(zoir.nodes.len == 0);
16 assert(zoir.extra.len == 0);
17 assert(zoir.limbs.len == 0);
18 return true;
19 } else {
20 assert(zoir.error_notes.len == 0);
21 return false;
22 }
23}
24
25pub fn deinit(zoir: Zoir, gpa: Allocator) void {
26 var nodes = zoir.nodes;
27 nodes.deinit(gpa);
28
29 gpa.free(zoir.extra);
30 gpa.free(zoir.limbs);
31 gpa.free(zoir.string_bytes);
32 gpa.free(zoir.compile_errors);
33 gpa.free(zoir.error_notes);
34}
35
36pub const Node = union(enum) {
37 /// A literal `true` value.
38 true,
39 /// A literal `false` value.
40 false,
41 /// A literal `null` value.
42 null,
43 /// A literal `inf` value.
44 pos_inf,
45 /// A literal `-inf` value.
46 neg_inf,
47 /// A literal `nan` value.
48 nan,
49 /// An integer literal.
50 int_literal: union(enum) {
51 small: i32,
52 big: std.math.big.int.Const,
53 },
54 /// A floating-point literal.
55 float_literal: f128,
56 /// A Unicode codepoint literal.
57 char_literal: u32,
58 /// An enum literal. The string is the literal, i.e. `foo` for `.foo`.
59 enum_literal: NullTerminatedString,
60 /// A string literal.
61 string_literal: []const u8,
62 /// An empty struct/array literal, i.e. `.{}`.
63 empty_literal,
64 /// An array literal. The `Range` gives the elements of the array literal.
65 array_literal: Node.Index.Range,
66 /// A struct literal. `names.len` is always equal to `vals.len`.
67 struct_literal: struct {
68 names: []const NullTerminatedString,
69 vals: Node.Index.Range,
70 },
71
72 pub const Index = enum(u32) {
73 root = 0,
74 _,
75
76 pub fn get(idx: Index, zoir: Zoir) Node {
77 const repr = zoir.nodes.get(@intFromEnum(idx));
78 return switch (repr.tag) {
79 .true => .true,
80 .false => .false,
81 .null => .null,
82 .pos_inf => .pos_inf,
83 .neg_inf => .neg_inf,
84 .nan => .nan,
85 .int_literal_small => .{ .int_literal = .{ .small = @bitCast(repr.data) } },
86 .int_literal_pos, .int_literal_neg => .{ .int_literal = .{ .big = .{
87 .limbs = l: {
88 const limb_count, const limbs_idx = zoir.extra[repr.data..][0..2].*;
89 break :l zoir.limbs[limbs_idx..][0..limb_count];
90 },
91 .positive = switch (repr.tag) {
92 .int_literal_pos => true,
93 .int_literal_neg => false,
94 else => unreachable,
95 },
96 } } },
97 .float_literal_small => .{ .float_literal = @as(f32, @bitCast(repr.data)) },
98 .float_literal => .{ .float_literal = @bitCast(zoir.extra[repr.data..][0..4].*) },
99 .char_literal => .{ .char_literal = repr.data },
100 .enum_literal => .{ .enum_literal = @enumFromInt(repr.data) },
101 .string_literal => .{ .string_literal = s: {
102 const start, const len = zoir.extra[repr.data..][0..2].*;
103 break :s zoir.string_bytes[start..][0..len];
104 } },
105 .string_literal_null => .{ .string_literal = NullTerminatedString.get(@enumFromInt(repr.data), zoir) },
106 .empty_literal => .empty_literal,
107 .array_literal => .{ .array_literal = a: {
108 const elem_count, const first_elem = zoir.extra[repr.data..][0..2].*;
109 break :a .{ .start = @enumFromInt(first_elem), .len = elem_count };
110 } },
111 .struct_literal => .{ .struct_literal = s: {
112 const elem_count, const first_elem = zoir.extra[repr.data..][0..2].*;
113 const field_names = zoir.extra[repr.data + 2 ..][0..elem_count];
114 break :s .{
115 .names = @ptrCast(field_names),
116 .vals = .{ .start = @enumFromInt(first_elem), .len = elem_count },
117 };
118 } },
119 };
120 }
121
122 pub fn getAstNode(idx: Index, zoir: Zoir) std.zig.Ast.Node.Index {
123 return zoir.nodes.items(.ast_node)[@intFromEnum(idx)];
124 }
125
126 pub const Range = struct {
127 start: Index,
128 len: u32,
129
130 pub fn at(r: Range, i: u32) Index {
131 assert(i < r.len);
132 return @enumFromInt(@intFromEnum(r.start) + i);
133 }
134 };
135 };
136
137 pub const Repr = struct {
138 tag: Tag,
139 data: u32,
140 ast_node: std.zig.Ast.Node.Index,
141
142 pub const Tag = enum(u8) {
143 /// `data` is ignored.
144 true,
145 /// `data` is ignored.
146 false,
147 /// `data` is ignored.
148 null,
149 /// `data` is ignored.
150 pos_inf,
151 /// `data` is ignored.
152 neg_inf,
153 /// `data` is ignored.
154 nan,
155 /// `data` is the `i32` value.
156 int_literal_small,
157 /// `data` is index into `extra` of:
158 /// * `limb_count: u32`
159 /// * `limbs_idx: u32`
160 int_literal_pos,
161 /// Identical to `int_literal_pos`, except the value is negative.
162 int_literal_neg,
163 /// `data` is the `f32` value.
164 float_literal_small,
165 /// `data` is index into `extra` of 4 elements which are a bitcast `f128`.
166 float_literal,
167 /// `data` is the `u32` value.
168 char_literal,
169 /// `data` is a `NullTerminatedString`.
170 enum_literal,
171 /// `data` is index into `extra` of:
172 /// * `start: u32`
173 /// * `len: u32`
174 string_literal,
175 /// Null-terminated string literal,
176 /// `data` is a `NullTerminatedString`.
177 string_literal_null,
178 /// An empty struct/array literal, `.{}`.
179 /// `data` is ignored.
180 empty_literal,
181 /// `data` is index into `extra` of:
182 /// * `elem_count: u32`
183 /// * `first_elem: Node.Index`
184 /// The nodes `first_elem .. first_elem + elem_count` are the children.
185 array_literal,
186 /// `data` is index into `extra` of:
187 /// * `elem_count: u32`
188 /// * `first_elem: Node.Index`
189 /// * `field_name: NullTerminatedString` for each `elem_count`
190 /// The nodes `first_elem .. first_elem + elem_count` are the children.
191 struct_literal,
192 };
193 };
194};
195
196pub const NullTerminatedString = enum(u32) {
197 _,
198 pub fn get(nts: NullTerminatedString, zoir: Zoir) [:0]const u8 {
199 const idx = std.mem.indexOfScalar(u8, zoir.string_bytes[@intFromEnum(nts)..], 0).?;
200 return zoir.string_bytes[@intFromEnum(nts)..][0..idx :0];
201 }
202};
203
204pub const CompileError = extern struct {
205 msg: NullTerminatedString,
206 token: Ast.TokenIndex,
207 /// If `token == invalid_token`, this is an `Ast.Node.Index`.
208 /// Otherwise, this is a byte offset into `token`.
209 node_or_offset: u32,
210
211 /// Ignored if `note_count == 0`.
212 first_note: u32,
213 note_count: u32,
214
215 pub fn getNotes(err: CompileError, zoir: Zoir) []const Note {
216 return zoir.error_notes[err.first_note..][0..err.note_count];
217 }
218
219 pub const Note = extern struct {
220 msg: NullTerminatedString,
221 token: Ast.TokenIndex,
222 /// If `token == invalid_token`, this is an `Ast.Node.Index`.
223 /// Otherwise, this is a byte offset into `token`.
224 node_or_offset: u32,
225 };
226
227 pub const invalid_token: Ast.TokenIndex = std.math.maxInt(Ast.TokenIndex);
228
229 comptime {
230 assert(std.meta.hasUniqueRepresentation(CompileError));
231 assert(std.meta.hasUniqueRepresentation(Note));
232 }
233};
234
235const std = @import("std");
236const assert = std.debug.assert;
237const Allocator = std.mem.Allocator;
238const Ast = std.zig.Ast;
239const Zoir = @This();
lib/std/zig/ZonGen.zig created+835
...@@ -0,0 +1,835 @@
1//! Ingests an `Ast` and produces a `Zoir`.
2
3gpa: Allocator,
4tree: Ast,
5
6nodes: std.MultiArrayList(Zoir.Node.Repr),
7extra: std.ArrayListUnmanaged(u32),
8limbs: std.ArrayListUnmanaged(std.math.big.Limb),
9string_bytes: std.ArrayListUnmanaged(u8),
10string_table: std.HashMapUnmanaged(u32, void, StringIndexContext, std.hash_map.default_max_load_percentage),
11
12compile_errors: std.ArrayListUnmanaged(Zoir.CompileError),
13error_notes: std.ArrayListUnmanaged(Zoir.CompileError.Note),
14
15pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zoir {
16 assert(tree.mode == .zon);
17
18 var zg: ZonGen = .{
19 .gpa = gpa,
20 .tree = tree,
21 .nodes = .empty,
22 .extra = .empty,
23 .limbs = .empty,
24 .string_bytes = .empty,
25 .string_table = .empty,
26 .compile_errors = .empty,
27 .error_notes = .empty,
28 };
29 defer {
30 zg.nodes.deinit(gpa);
31 zg.extra.deinit(gpa);
32 zg.limbs.deinit(gpa);
33 zg.string_bytes.deinit(gpa);
34 zg.string_table.deinit(gpa);
35 zg.compile_errors.deinit(gpa);
36 zg.error_notes.deinit(gpa);
37 }
38
39 if (tree.errors.len == 0) {
40 const root_ast_node = tree.nodes.items(.data)[0].lhs;
41 try zg.nodes.append(gpa, undefined); // index 0; root node
42 try zg.expr(root_ast_node, .root);
43 } else {
44 try zg.lowerAstErrors();
45 }
46
47 if (zg.compile_errors.items.len > 0) {
48 const string_bytes = try zg.string_bytes.toOwnedSlice(gpa);
49 errdefer gpa.free(string_bytes);
50 const compile_errors = try zg.compile_errors.toOwnedSlice(gpa);
51 errdefer gpa.free(compile_errors);
52 const error_notes = try zg.error_notes.toOwnedSlice(gpa);
53 errdefer gpa.free(error_notes);
54
55 return .{
56 .nodes = .empty,
57 .extra = &.{},
58 .limbs = &.{},
59 .string_bytes = string_bytes,
60 .compile_errors = compile_errors,
61 .error_notes = error_notes,
62 };
63 } else {
64 assert(zg.error_notes.items.len == 0);
65
66 var nodes = zg.nodes.toOwnedSlice();
67 errdefer nodes.deinit(gpa);
68 const extra = try zg.extra.toOwnedSlice(gpa);
69 errdefer gpa.free(extra);
70 const limbs = try zg.limbs.toOwnedSlice(gpa);
71 errdefer gpa.free(limbs);
72 const string_bytes = try zg.string_bytes.toOwnedSlice(gpa);
73 errdefer gpa.free(string_bytes);
74
75 return .{
76 .nodes = nodes,
77 .extra = extra,
78 .limbs = limbs,
79 .string_bytes = string_bytes,
80 .compile_errors = &.{},
81 .error_notes = &.{},
82 };
83 }
84}
85
86fn expr(zg: *ZonGen, node: Ast.Node.Index, dest_node: Zoir.Node.Index) Allocator.Error!void {
87 const gpa = zg.gpa;
88 const tree = zg.tree;
89 const node_tags = tree.nodes.items(.tag);
90 const node_datas = tree.nodes.items(.data);
91 const main_tokens = tree.nodes.items(.main_token);
92
93 switch (node_tags[node]) {
94 .root => unreachable,
95 .@"usingnamespace" => unreachable,
96 .test_decl => unreachable,
97 .container_field_init => unreachable,
98 .container_field_align => unreachable,
99 .container_field => unreachable,
100 .fn_decl => unreachable,
101 .global_var_decl => unreachable,
102 .local_var_decl => unreachable,
103 .simple_var_decl => unreachable,
104 .aligned_var_decl => unreachable,
105 .@"defer" => unreachable,
106 .@"errdefer" => unreachable,
107 .switch_case => unreachable,
108 .switch_case_inline => unreachable,
109 .switch_case_one => unreachable,
110 .switch_case_inline_one => unreachable,
111 .switch_range => unreachable,
112 .asm_output => unreachable,
113 .asm_input => unreachable,
114 .for_range => unreachable,
115 .assign => unreachable,
116 .assign_destructure => unreachable,
117 .assign_shl => unreachable,
118 .assign_shl_sat => unreachable,
119 .assign_shr => unreachable,
120 .assign_bit_and => unreachable,
121 .assign_bit_or => unreachable,
122 .assign_bit_xor => unreachable,
123 .assign_div => unreachable,
124 .assign_sub => unreachable,
125 .assign_sub_wrap => unreachable,
126 .assign_sub_sat => unreachable,
127 .assign_mod => unreachable,
128 .assign_add => unreachable,
129 .assign_add_wrap => unreachable,
130 .assign_add_sat => unreachable,
131 .assign_mul => unreachable,
132 .assign_mul_wrap => unreachable,
133 .assign_mul_sat => unreachable,
134
135 .shl,
136 .shr,
137 .add,
138 .add_wrap,
139 .add_sat,
140 .sub,
141 .sub_wrap,
142 .sub_sat,
143 .mul,
144 .mul_wrap,
145 .mul_sat,
146 .div,
147 .mod,
148 .shl_sat,
149 .bit_and,
150 .bit_or,
151 .bit_xor,
152 .bang_equal,
153 .equal_equal,
154 .greater_than,
155 .greater_or_equal,
156 .less_than,
157 .less_or_equal,
158 .array_cat,
159 .array_mult,
160 .bool_and,
161 .bool_or,
162 .bool_not,
163 .bit_not,
164 .negation_wrap,
165 => try zg.addErrorTok(main_tokens[node], "operator '{s}' is not allowed in ZON", .{tree.tokenSlice(main_tokens[node])}),
166
167 .error_union,
168 .merge_error_sets,
169 .optional_type,
170 .anyframe_literal,
171 .anyframe_type,
172 .ptr_type_aligned,
173 .ptr_type_sentinel,
174 .ptr_type,
175 .ptr_type_bit_range,
176 .container_decl,
177 .container_decl_trailing,
178 .container_decl_arg,
179 .container_decl_arg_trailing,
180 .container_decl_two,
181 .container_decl_two_trailing,
182 .tagged_union,
183 .tagged_union_trailing,
184 .tagged_union_enum_tag,
185 .tagged_union_enum_tag_trailing,
186 .tagged_union_two,
187 .tagged_union_two_trailing,
188 .array_type,
189 .array_type_sentinel,
190 .error_set_decl,
191 .fn_proto_simple,
192 .fn_proto_multi,
193 .fn_proto_one,
194 .fn_proto,
195 => try zg.addErrorNode(node, "types are not available in ZON", .{}),
196
197 .call_one,
198 .call_one_comma,
199 .async_call_one,
200 .async_call_one_comma,
201 .call,
202 .call_comma,
203 .async_call,
204 .async_call_comma,
205 .@"return",
206 .if_simple,
207 .@"if",
208 .while_simple,
209 .while_cont,
210 .@"while",
211 .for_simple,
212 .@"for",
213 .@"catch",
214 .@"orelse",
215 .@"break",
216 .@"continue",
217 .@"switch",
218 .switch_comma,
219 .@"nosuspend",
220 .@"suspend",
221 .@"await",
222 .@"resume",
223 .@"try",
224 .unreachable_literal,
225 => try zg.addErrorNode(node, "control flow is not allowed in ZON", .{}),
226
227 .@"comptime" => try zg.addErrorNode(node, "keyword 'comptime' is not allowed in ZON", .{}),
228 .asm_simple, .@"asm" => try zg.addErrorNode(node, "inline asm is not allowed in ZON", .{}),
229
230 .builtin_call_two,
231 .builtin_call_two_comma,
232 .builtin_call,
233 .builtin_call_comma,
234 => try zg.addErrorNode(node, "builtin function calls are not allowed in ZON", .{}),
235
236 .field_access => try zg.addErrorNode(node, "field accesses are not allowed in ZON", .{}),
237
238 .slice_open,
239 .slice,
240 .slice_sentinel,
241 => try zg.addErrorNode(node, "slice operator is not allowed in ZON", .{}),
242
243 .deref, .address_of => try zg.addErrorTok(main_tokens[node], "pointers are not available in ZON", .{}),
244 .unwrap_optional => try zg.addErrorTok(main_tokens[node], "optionals are not available in ZON", .{}),
245 .error_value => try zg.addErrorNode(node, "errors are not available in ZON", .{}),
246
247 .array_access => try zg.addErrorTok(node, "array indexing is not allowed in ZON", .{}),
248
249 .block_two,
250 .block_two_semicolon,
251 .block,
252 .block_semicolon,
253 => try zg.addErrorNode(node, "blocks are not allowed in ZON", .{}),
254
255 .array_init_one,
256 .array_init_one_comma,
257 .array_init,
258 .array_init_comma,
259 .struct_init_one,
260 .struct_init_one_comma,
261 .struct_init,
262 .struct_init_comma,
263 => {
264 var buf: [2]Ast.Node.Index = undefined;
265
266 const type_node = if (tree.fullArrayInit(&buf, node)) |full|
267 full.ast.type_expr
268 else if (tree.fullStructInit(&buf, node)) |full|
269 full.ast.type_expr
270 else
271 unreachable;
272
273 try zg.addErrorNodeNotes(type_node, "types are not available in ZON", .{}, &.{
274 try zg.errNoteNode(type_node, "replace the type with '.'", .{}),
275 });
276 },
277
278 .grouped_expression => {
279 try zg.addErrorTokNotes(main_tokens[node], "expression grouping is not allowed in ZON", .{}, &.{
280 try zg.errNoteTok(main_tokens[node], "these parentheses are always redundant", .{}),
281 });
282 return zg.expr(node_datas[node].lhs, dest_node);
283 },
284
285 .negation => {
286 const child_node = node_datas[node].lhs;
287 switch (node_tags[child_node]) {
288 .number_literal => return zg.numberLiteral(child_node, node, dest_node, .negative),
289 .identifier => {
290 const child_ident = tree.tokenSlice(main_tokens[child_node]);
291 if (mem.eql(u8, child_ident, "inf")) {
292 zg.setNode(dest_node, .{
293 .tag = .neg_inf,
294 .data = 0, // ignored
295 .ast_node = node,
296 });
297 return;
298 }
299 },
300 else => {},
301 }
302 try zg.addErrorTok(main_tokens[node], "expected number or 'inf' after '-'", .{});
303 },
304 .number_literal => try zg.numberLiteral(node, node, dest_node, .positive),
305 .char_literal => try zg.charLiteral(node, dest_node),
306
307 .identifier => try zg.identifier(node, dest_node),
308
309 .enum_literal => {
310 const str_index = zg.identAsString(main_tokens[node]) catch |err| switch (err) {
311 error.BadString => undefined, // doesn't matter, there's an error
312 error.OutOfMemory => |e| return e,
313 };
314 zg.setNode(dest_node, .{
315 .tag = .enum_literal,
316 .data = @intFromEnum(str_index),
317 .ast_node = node,
318 });
319 },
320 .string_literal, .multiline_string_literal => if (zg.strLitAsString(node)) |result| switch (result) {
321 .nts => |nts| zg.setNode(dest_node, .{
322 .tag = .string_literal_null,
323 .data = @intFromEnum(nts),
324 .ast_node = node,
325 }),
326 .slice => |slice| {
327 const extra_index: u32 = @intCast(zg.extra.items.len);
328 try zg.extra.appendSlice(zg.gpa, &.{ slice.start, slice.len });
329 zg.setNode(dest_node, .{
330 .tag = .string_literal,
331 .data = extra_index,
332 .ast_node = node,
333 });
334 },
335 } else |err| switch (err) {
336 error.BadString => {},
337 error.OutOfMemory => |e| return e,
338 },
339
340 .array_init_dot_two,
341 .array_init_dot_two_comma,
342 .array_init_dot,
343 .array_init_dot_comma,
344 => {
345 var buf: [2]Ast.Node.Index = undefined;
346 const full = tree.fullArrayInit(&buf, node).?;
347 assert(full.ast.elements.len != 0); // Otherwise it would be a struct init
348 assert(full.ast.type_expr == 0); // The tag was `array_init_dot_*`
349
350 const first_elem: u32 = @intCast(zg.nodes.len);
351 try zg.nodes.resize(gpa, zg.nodes.len + full.ast.elements.len);
352
353 const extra_index: u32 = @intCast(zg.extra.items.len);
354 try zg.extra.appendSlice(gpa, &.{
355 @intCast(full.ast.elements.len),
356 first_elem,
357 });
358
359 zg.setNode(dest_node, .{
360 .tag = .array_literal,
361 .data = extra_index,
362 .ast_node = node,
363 });
364
365 for (full.ast.elements, first_elem..) |elem_node, elem_dest_node| {
366 try zg.expr(elem_node, @enumFromInt(elem_dest_node));
367 }
368 },
369
370 .struct_init_dot_two,
371 .struct_init_dot_two_comma,
372 .struct_init_dot,
373 .struct_init_dot_comma,
374 => {
375 var buf: [2]Ast.Node.Index = undefined;
376 const full = tree.fullStructInit(&buf, node).?;
377 assert(full.ast.type_expr == 0); // The tag was `struct_init_dot_*`
378
379 if (full.ast.fields.len == 0) {
380 zg.setNode(dest_node, .{
381 .tag = .empty_literal,
382 .data = 0, // ignored
383 .ast_node = node,
384 });
385 return;
386 }
387
388 const first_elem: u32 = @intCast(zg.nodes.len);
389 try zg.nodes.resize(gpa, zg.nodes.len + full.ast.fields.len);
390
391 const extra_index: u32 = @intCast(zg.extra.items.len);
392 try zg.extra.ensureUnusedCapacity(gpa, 2 + full.ast.fields.len);
393 zg.extra.appendSliceAssumeCapacity(&.{
394 @intCast(full.ast.fields.len),
395 first_elem,
396 });
397 const names_start = extra_index + 2;
398 zg.extra.appendNTimesAssumeCapacity(undefined, full.ast.fields.len);
399
400 zg.setNode(dest_node, .{
401 .tag = .struct_literal,
402 .data = extra_index,
403 .ast_node = node,
404 });
405
406 for (full.ast.fields, names_start.., first_elem..) |elem_node, extra_name_idx, elem_dest_node| {
407 const name_token = tree.firstToken(elem_node) - 2;
408 zg.extra.items[extra_name_idx] = @intFromEnum(zg.identAsString(name_token) catch |err| switch (err) {
409 error.BadString => undefined, // doesn't matter, there's an error
410 error.OutOfMemory => |e| return e,
411 });
412 try zg.expr(elem_node, @enumFromInt(elem_dest_node));
413 }
414 },
415 }
416}
417
418fn parseStrLit(zg: *ZonGen, token: Ast.TokenIndex, offset: u32) !u32 {
419 const raw_string = zg.tree.tokenSlice(token)[offset..];
420 const start = zg.string_bytes.items.len;
421 switch (try std.zig.string_literal.parseWrite(zg.string_bytes.writer(zg.gpa), raw_string)) {
422 .success => return @intCast(start),
423 .failure => |err| {
424 try zg.lowerStrLitError(err, token, raw_string, offset);
425 return error.BadString;
426 },
427 }
428}
429
430fn parseMultilineStrLit(zg: *ZonGen, node: Ast.Node.Index) !u32 {
431 const gpa = zg.gpa;
432 const tree = zg.tree;
433 const string_bytes = &zg.string_bytes;
434
435 const first_tok, const last_tok = bounds: {
436 const node_data = tree.nodes.items(.data)[node];
437 break :bounds .{ node_data.lhs, node_data.rhs };
438 };
439
440 const str_index: u32 = @intCast(string_bytes.items.len);
441
442 // First line: do not append a newline.
443 {
444 const line_bytes = tree.tokenSlice(first_tok)[2..];
445 try string_bytes.appendSlice(gpa, line_bytes);
446 }
447 // Following lines: each line prepends a newline.
448 for (first_tok + 1..last_tok + 1) |tok_idx| {
449 const line_bytes = tree.tokenSlice(@intCast(tok_idx))[2..];
450 try string_bytes.ensureUnusedCapacity(gpa, line_bytes.len + 1);
451 string_bytes.appendAssumeCapacity('\n');
452 string_bytes.appendSliceAssumeCapacity(line_bytes);
453 }
454
455 return @intCast(str_index);
456}
457
458fn appendIdentStr(zg: *ZonGen, ident_token: Ast.TokenIndex) !u32 {
459 const tree = zg.tree;
460 assert(tree.tokens.items(.tag)[ident_token] == .identifier);
461 const ident_name = tree.tokenSlice(ident_token);
462 if (!mem.startsWith(u8, ident_name, "@")) {
463 const start = zg.string_bytes.items.len;
464 try zg.string_bytes.appendSlice(zg.gpa, ident_name);
465 return @intCast(start);
466 } else {
467 const start = try zg.parseStrLit(ident_token, 1);
468 const slice = zg.string_bytes.items[start..];
469 if (mem.indexOfScalar(u8, slice, 0) != null) {
470 try zg.addErrorTok(ident_token, "identifier cannot contain null bytes", .{});
471 return error.BadString;
472 } else if (slice.len == 0) {
473 try zg.addErrorTok(ident_token, "identifier cannot be empty", .{});
474 return error.BadString;
475 }
476 return start;
477 }
478}
479
480const StringLiteralResult = union(enum) {
481 nts: Zoir.NullTerminatedString,
482 slice: struct { start: u32, len: u32 },
483};
484
485fn strLitAsString(zg: *ZonGen, str_node: Ast.Node.Index) !StringLiteralResult {
486 const gpa = zg.gpa;
487 const string_bytes = &zg.string_bytes;
488 const str_index = switch (zg.tree.nodes.items(.tag)[str_node]) {
489 .string_literal => try zg.parseStrLit(zg.tree.nodes.items(.main_token)[str_node], 0),
490 .multiline_string_literal => try zg.parseMultilineStrLit(str_node),
491 else => unreachable,
492 };
493 const key: []const u8 = string_bytes.items[str_index..];
494 if (std.mem.indexOfScalar(u8, key, 0) != null) return .{ .slice = .{
495 .start = str_index,
496 .len = @intCast(key.len),
497 } };
498 const gop = try zg.string_table.getOrPutContextAdapted(
499 gpa,
500 key,
501 StringIndexAdapter{ .bytes = string_bytes },
502 StringIndexContext{ .bytes = string_bytes },
503 );
504 if (gop.found_existing) {
505 string_bytes.shrinkRetainingCapacity(str_index);
506 return .{ .nts = @enumFromInt(gop.key_ptr.*) };
507 }
508 gop.key_ptr.* = str_index;
509 try string_bytes.append(gpa, 0);
510 return .{ .nts = @enumFromInt(str_index) };
511}
512
513fn identAsString(zg: *ZonGen, ident_token: Ast.TokenIndex) !Zoir.NullTerminatedString {
514 const gpa = zg.gpa;
515 const string_bytes = &zg.string_bytes;
516 const str_index = try zg.appendIdentStr(ident_token);
517 const key: []const u8 = string_bytes.items[str_index..];
518 const gop = try zg.string_table.getOrPutContextAdapted(
519 gpa,
520 key,
521 StringIndexAdapter{ .bytes = string_bytes },
522 StringIndexContext{ .bytes = string_bytes },
523 );
524 if (gop.found_existing) {
525 string_bytes.shrinkRetainingCapacity(str_index);
526 return @enumFromInt(gop.key_ptr.*);
527 }
528 gop.key_ptr.* = str_index;
529 try string_bytes.append(gpa, 0);
530 return @enumFromInt(str_index);
531}
532
533fn numberLiteral(zg: *ZonGen, num_node: Ast.Node.Index, src_node: Ast.Node.Index, dest_node: Zoir.Node.Index, sign: enum { negative, positive }) !void {
534 const tree = zg.tree;
535 const num_token = tree.nodes.items(.main_token)[num_node];
536 const num_bytes = tree.tokenSlice(num_token);
537
538 switch (std.zig.parseNumberLiteral(num_bytes)) {
539 .int => |unsigned_num| {
540 if (unsigned_num == 0 and sign == .negative) {
541 try zg.addErrorTokNotes(num_token, "integer literal '-0' is ambiguous", .{}, &.{
542 try zg.errNoteTok(num_token, "use '0' for an integer zero", .{}),
543 try zg.errNoteTok(num_token, "use '-0.0' for a flaoting-point signed zero", .{}),
544 });
545 return;
546 }
547 const num: i65 = switch (sign) {
548 .positive => unsigned_num,
549 .negative => -@as(i65, unsigned_num),
550 };
551 if (std.math.cast(i32, num)) |x| {
552 zg.setNode(dest_node, .{
553 .tag = .int_literal_small,
554 .data = @bitCast(x),
555 .ast_node = src_node,
556 });
557 return;
558 }
559 const max_limbs = comptime std.math.big.int.calcTwosCompLimbCount(@bitSizeOf(@TypeOf(num)));
560 var limbs: [max_limbs]std.math.big.Limb = undefined;
561 var big_int: std.math.big.int.Mutable = .init(&limbs, num);
562 try zg.setBigIntLiteralNode(dest_node, src_node, big_int.toConst());
563 },
564 .big_int => |base| {
565 const gpa = zg.gpa;
566 const num_without_prefix = switch (base) {
567 .decimal => num_bytes,
568 .hex, .binary, .octal => num_bytes[2..],
569 };
570 var big_int: std.math.big.int.Managed = try .init(gpa);
571 defer big_int.deinit();
572 big_int.setString(@intFromEnum(base), num_without_prefix) catch |err| switch (err) {
573 error.InvalidCharacter => unreachable, // caught in `parseNumberLiteral`
574 error.InvalidBase => unreachable, // we only pass 16, 8, 2, see above
575 error.OutOfMemory => return error.OutOfMemory,
576 };
577 switch (sign) {
578 .positive => {},
579 .negative => big_int.negate(),
580 }
581 try zg.setBigIntLiteralNode(dest_node, src_node, big_int.toConst());
582 },
583 .float => {
584 const unsigned_num = std.fmt.parseFloat(f128, num_bytes) catch |err| switch (err) {
585 error.InvalidCharacter => unreachable, // validated by tokenizer
586 };
587 const num: f128 = switch (sign) {
588 .positive => unsigned_num,
589 .negative => -unsigned_num,
590 };
591
592 {
593 // If the value fits into an f32 without losing any precision, store it that way.
594 @setFloatMode(.strict);
595 const smaller_float: f32 = @floatCast(num);
596 const bigger_again: f128 = smaller_float;
597 if (bigger_again == num) {
598 zg.setNode(dest_node, .{
599 .tag = .float_literal_small,
600 .data = @bitCast(smaller_float),
601 .ast_node = src_node,
602 });
603 return;
604 }
605 }
606
607 const elems: [4]u32 = @bitCast(num);
608 const extra_index: u32 = @intCast(zg.extra.items.len);
609 try zg.extra.appendSlice(zg.gpa, &elems);
610 zg.setNode(dest_node, .{
611 .tag = .float_literal,
612 .data = extra_index,
613 .ast_node = src_node,
614 });
615 },
616 .failure => |err| try zg.lowerNumberError(err, num_token, num_bytes),
617 }
618}
619
620fn setBigIntLiteralNode(zg: *ZonGen, dest_node: Zoir.Node.Index, src_node: Ast.Node.Index, val: std.math.big.int.Const) !void {
621 try zg.extra.ensureUnusedCapacity(zg.gpa, 2);
622 try zg.limbs.ensureUnusedCapacity(zg.gpa, val.limbs.len);
623
624 const limbs_idx: u32 = @intCast(zg.limbs.items.len);
625 zg.limbs.appendSliceAssumeCapacity(val.limbs);
626
627 const extra_idx: u32 = @intCast(zg.extra.items.len);
628 zg.extra.appendSliceAssumeCapacity(&.{ @intCast(val.limbs.len), limbs_idx });
629
630 zg.setNode(dest_node, .{
631 .tag = if (val.positive) .int_literal_pos else .int_literal_neg,
632 .data = extra_idx,
633 .ast_node = src_node,
634 });
635}
636
637fn charLiteral(zg: *ZonGen, node: Ast.Node.Index, dest_node: Zoir.Node.Index) !void {
638 const tree = zg.tree;
639 assert(tree.nodes.items(.tag)[node] == .char_literal);
640 const main_token = tree.nodes.items(.main_token)[node];
641 const slice = tree.tokenSlice(main_token);
642 switch (std.zig.parseCharLiteral(slice)) {
643 .success => |codepoint| zg.setNode(dest_node, .{
644 .tag = .char_literal,
645 .data = codepoint,
646 .ast_node = node,
647 }),
648 .failure => |err| try zg.lowerStrLitError(err, main_token, slice, 0),
649 }
650}
651
652fn identifier(zg: *ZonGen, node: Ast.Node.Index, dest_node: Zoir.Node.Index) !void {
653 const tree = zg.tree;
654 assert(tree.nodes.items(.tag)[node] == .identifier);
655 const main_token = tree.nodes.items(.main_token)[node];
656 const ident = tree.tokenSlice(main_token);
657
658 const tag: Zoir.Node.Repr.Tag = t: {
659 if (mem.eql(u8, ident, "true")) break :t .true;
660 if (mem.eql(u8, ident, "false")) break :t .false;
661 if (mem.eql(u8, ident, "null")) break :t .null;
662 if (mem.eql(u8, ident, "inf")) break :t .pos_inf;
663 if (mem.eql(u8, ident, "nan")) break :t .nan;
664 try zg.addErrorNodeNotes(node, "invalid expression", .{}, &.{
665 try zg.errNoteNode(node, "ZON allows identifiers 'true', 'false', 'null', 'inf', and 'nan'", .{}),
666 try zg.errNoteNode(node, "precede identifier with '.' for an enum literal", .{}),
667 });
668 return;
669 };
670
671 zg.setNode(dest_node, .{
672 .tag = tag,
673 .data = 0, // ignored
674 .ast_node = node,
675 });
676}
677
678fn setNode(zg: *ZonGen, dest: Zoir.Node.Index, repr: Zoir.Node.Repr) void {
679 zg.nodes.set(@intFromEnum(dest), repr);
680}
681
682fn lowerStrLitError(zg: *ZonGen, err: std.zig.string_literal.Error, token: Ast.TokenIndex, raw_string: []const u8, offset: u32) Allocator.Error!void {
683 return err.lower(raw_string, offset, ZonGen.addErrorTokOff, .{ zg, token });
684}
685
686fn lowerNumberError(zg: *ZonGen, err: std.zig.number_literal.Error, token: Ast.TokenIndex, bytes: []const u8) Allocator.Error!void {
687 const is_float = std.mem.indexOfScalar(u8, bytes, '.') != null;
688 switch (err) {
689 .leading_zero => if (is_float) {
690 try zg.addErrorTok(token, "number '{s}' has leading zero", .{bytes});
691 } else {
692 try zg.addErrorTokNotes(token, "number '{s}' has leading zero", .{bytes}, &.{
693 try zg.errNoteTok(token, "use '0o' prefix for octal literals", .{}),
694 });
695 },
696 .digit_after_base => try zg.addErrorTok(token, "expected a digit after base prefix", .{}),
697 .upper_case_base => |i| try zg.addErrorTokOff(token, @intCast(i), "base prefix must be lowercase", .{}),
698 .invalid_float_base => |i| try zg.addErrorTokOff(token, @intCast(i), "invalid base for float literal", .{}),
699 .repeated_underscore => |i| try zg.addErrorTokOff(token, @intCast(i), "repeated digit separator", .{}),
700 .invalid_underscore_after_special => |i| try zg.addErrorTokOff(token, @intCast(i), "expected digit before digit separator", .{}),
701 .invalid_digit => |info| try zg.addErrorTokOff(token, @intCast(info.i), "invalid digit '{c}' for {s} base", .{ bytes[info.i], @tagName(info.base) }),
702 .invalid_digit_exponent => |i| try zg.addErrorTokOff(token, @intCast(i), "invalid digit '{c}' in exponent", .{bytes[i]}),
703 .duplicate_exponent => |i| try zg.addErrorTokOff(token, @intCast(i), "duplicate exponent", .{}),
704 .exponent_after_underscore => |i| try zg.addErrorTokOff(token, @intCast(i), "expected digit before exponent", .{}),
705 .special_after_underscore => |i| try zg.addErrorTokOff(token, @intCast(i), "expected digit before '{c}'", .{bytes[i]}),
706 .trailing_special => |i| try zg.addErrorTokOff(token, @intCast(i), "expected digit after '{c}'", .{bytes[i - 1]}),
707 .trailing_underscore => |i| try zg.addErrorTokOff(token, @intCast(i), "trailing digit separator", .{}),
708 .duplicate_period => unreachable, // Validated by tokenizer
709 .invalid_character => unreachable, // Validated by tokenizer
710 .invalid_exponent_sign => |i| {
711 assert(bytes.len >= 2 and bytes[0] == '0' and bytes[1] == 'x'); // Validated by tokenizer
712 try zg.addErrorTokOff(token, @intCast(i), "sign '{c}' cannot follow digit '{c}' in hex base", .{ bytes[i], bytes[i - 1] });
713 },
714 .period_after_exponent => |i| try zg.addErrorTokOff(token, @intCast(i), "unexpected period after exponent", .{}),
715 }
716}
717
718fn errNoteNode(zg: *ZonGen, node: Ast.Node.Index, comptime format: []const u8, args: anytype) Allocator.Error!Zoir.CompileError.Note {
719 const message_idx: u32 = @intCast(zg.string_bytes.items.len);
720 const writer = zg.string_bytes.writer(zg.gpa);
721 try writer.print(format, args);
722 try writer.writeByte(0);
723
724 return .{
725 .msg = @enumFromInt(message_idx),
726 .token = Zoir.CompileError.invalid_token,
727 .node_or_offset = node,
728 };
729}
730
731fn errNoteTok(zg: *ZonGen, tok: Ast.TokenIndex, comptime format: []const u8, args: anytype) Allocator.Error!Zoir.CompileError.Note {
732 const message_idx: u32 = @intCast(zg.string_bytes.items.len);
733 const writer = zg.string_bytes.writer(zg.gpa);
734 try writer.print(format, args);
735 try writer.writeByte(0);
736
737 return .{
738 .msg = @enumFromInt(message_idx),
739 .token = tok,
740 .node_or_offset = 0,
741 };
742}
743
744fn addErrorNode(zg: *ZonGen, node: Ast.Node.Index, comptime format: []const u8, args: anytype) Allocator.Error!void {
745 return zg.addErrorInner(Zoir.CompileError.invalid_token, node, format, args, &.{});
746}
747fn addErrorTok(zg: *ZonGen, tok: Ast.TokenIndex, comptime format: []const u8, args: anytype) Allocator.Error!void {
748 return zg.addErrorInner(tok, 0, format, args, &.{});
749}
750fn addErrorNodeNotes(zg: *ZonGen, node: Ast.Node.Index, comptime format: []const u8, args: anytype, notes: []const Zoir.CompileError.Note) Allocator.Error!void {
751 return zg.addErrorInner(Zoir.CompileError.invalid_token, node, format, args, notes);
752}
753fn addErrorTokNotes(zg: *ZonGen, tok: Ast.TokenIndex, comptime format: []const u8, args: anytype, notes: []const Zoir.CompileError.Note) Allocator.Error!void {
754 return zg.addErrorInner(tok, 0, format, args, notes);
755}
756fn addErrorTokOff(zg: *ZonGen, tok: Ast.TokenIndex, offset: u32, comptime format: []const u8, args: anytype) Allocator.Error!void {
757 return zg.addErrorInner(tok, offset, format, args, &.{});
758}
759fn addErrorTokNotesOff(zg: *ZonGen, tok: Ast.TokenIndex, offset: u32, comptime format: []const u8, args: anytype, notes: []const Zoir.CompileError.Note) Allocator.Error!void {
760 return zg.addErrorInner(tok, offset, format, args, notes);
761}
762
763fn addErrorInner(
764 zg: *ZonGen,
765 token: Ast.TokenIndex,
766 node_or_offset: u32,
767 comptime format: []const u8,
768 args: anytype,
769 notes: []const Zoir.CompileError.Note,
770) Allocator.Error!void {
771 const gpa = zg.gpa;
772
773 const first_note: u32 = @intCast(zg.error_notes.items.len);
774 try zg.error_notes.appendSlice(gpa, notes);
775
776 const message_idx: u32 = @intCast(zg.string_bytes.items.len);
777 const writer = zg.string_bytes.writer(zg.gpa);
778 try writer.print(format, args);
779 try writer.writeByte(0);
780
781 try zg.compile_errors.append(gpa, .{
782 .msg = @enumFromInt(message_idx),
783 .token = token,
784 .node_or_offset = node_or_offset,
785 .first_note = first_note,
786 .note_count = @intCast(notes.len),
787 });
788}
789
790fn lowerAstErrors(zg: *ZonGen) Allocator.Error!void {
791 const gpa = zg.gpa;
792 const tree = zg.tree;
793 assert(tree.errors.len > 0);
794
795 var msg: std.ArrayListUnmanaged(u8) = .empty;
796 defer msg.deinit(gpa);
797
798 var notes: std.ArrayListUnmanaged(Zoir.CompileError.Note) = .empty;
799 defer notes.deinit(gpa);
800
801 var cur_err = tree.errors[0];
802 for (tree.errors[1..]) |err| {
803 if (err.is_note) {
804 try tree.renderError(err, msg.writer(gpa));
805 try notes.append(gpa, try zg.errNoteTok(err.token, "{s}", .{msg.items}));
806 } else {
807 // Flush error
808 try tree.renderError(cur_err, msg.writer(gpa));
809 const extra_offset = tree.errorOffset(cur_err);
810 try zg.addErrorTokNotesOff(cur_err.token, extra_offset, "{s}", .{msg.items}, notes.items);
811 notes.clearRetainingCapacity();
812 cur_err = err;
813
814 // TODO: `Parse` currently does not have good error recovery mechanisms, so the remaining errors could be bogus.
815 // As such, we'll ignore all remaining errors for now. We should improve `Parse` so that we can report all the errors.
816 return;
817 }
818 msg.clearRetainingCapacity();
819 }
820
821 // Flush error
822 const extra_offset = tree.errorOffset(cur_err);
823 try tree.renderError(cur_err, msg.writer(gpa));
824 try zg.addErrorTokNotesOff(cur_err.token, extra_offset, "{s}", .{msg.items}, notes.items);
825}
826
827const std = @import("std");
828const assert = std.debug.assert;
829const mem = std.mem;
830const Allocator = mem.Allocator;
831const StringIndexAdapter = std.hash_map.StringIndexAdapter;
832const StringIndexContext = std.hash_map.StringIndexContext;
833const ZonGen = @This();
834const Zoir = @import("Zoir.zig");
835const Ast = @import("Ast.zig");
lib/std/zig/string_literal.zig+35
...@@ -38,6 +38,41 @@ pub const Error = union(enum) {...@@ -38,6 +38,41 @@ pub const Error = union(enum) {
38 invalid_character: usize,38 invalid_character: usize,
39 /// `''`. Not returned for string literals.39 /// `''`. Not returned for string literals.
40 empty_char_literal,40 empty_char_literal,
41
42 /// Returns `func(first_args[0], ..., first_args[n], offset + bad_idx, format, args)`.
43 pub fn lower(
44 err: Error,
45 raw_string: []const u8,
46 offset: u32,
47 comptime func: anytype,
48 first_args: anytype,
49 ) @typeInfo(@TypeOf(func)).@"fn".return_type.? {
50 switch (err) {
51 inline else => |bad_index_or_void, tag| {
52 const bad_index: u32 = switch (@TypeOf(bad_index_or_void)) {
53 void => 0,
54 else => @intCast(bad_index_or_void),
55 };
56 const fmt_str: []const u8, const args = switch (tag) {
57 .invalid_escape_character => .{ "invalid escape character: '{c}'", .{raw_string[bad_index]} },
58 .expected_hex_digit => .{ "expected hex digit, found '{c}'", .{raw_string[bad_index]} },
59 .empty_unicode_escape_sequence => .{ "empty unicode escape sequence", .{} },
60 .expected_hex_digit_or_rbrace => .{ "expected hex digit or '}}', found '{c}'", .{raw_string[bad_index]} },
61 .invalid_unicode_codepoint => .{ "unicode escape does not correspond to a valid unicode scalar value", .{} },
62 .expected_lbrace => .{ "expected '{{', found '{c}'", .{raw_string[bad_index]} },
63 .expected_rbrace => .{ "expected '}}', found '{c}'", .{raw_string[bad_index]} },
64 .expected_single_quote => .{ "expected singel quote ('), found '{c}'", .{raw_string[bad_index]} },
65 .invalid_character => .{ "invalid byte in string or character literal: '{c}'", .{raw_string[bad_index]} },
66 .empty_char_literal => .{ "empty character literal", .{} },
67 };
68 return @call(.auto, func, first_args ++ .{
69 offset + bad_index,
70 fmt_str,
71 args,
72 });
73 },
74 }
75 }
41};76};
4277
43/// Asserts the slice starts and ends with single-quotes.78/// Asserts the slice starts and ends with single-quotes.
src/fmt.zig+78-30
...@@ -13,6 +13,7 @@ const usage_fmt =...@@ -13,6 +13,7 @@ const usage_fmt =
13 \\ if the list is non-empty13 \\ if the list is non-empty
14 \\ --ast-check Run zig ast-check on every file14 \\ --ast-check Run zig ast-check on every file
15 \\ --exclude [file] Exclude file or directory from formatting15 \\ --exclude [file] Exclude file or directory from formatting
16 \\ --zon Treat all input files as ZON, regardless of file extension
16 \\17 \\
17 \\18 \\
18;19;
...@@ -21,6 +22,7 @@ const Fmt = struct {...@@ -21,6 +22,7 @@ const Fmt = struct {
21 seen: SeenMap,22 seen: SeenMap,
22 any_error: bool,23 any_error: bool,
23 check_ast: bool,24 check_ast: bool,
25 force_zon: bool,
24 color: Color,26 color: Color,
25 gpa: Allocator,27 gpa: Allocator,
26 arena: Allocator,28 arena: Allocator,
...@@ -35,9 +37,10 @@ pub fn run(...@@ -35,9 +37,10 @@ pub fn run(
35 args: []const []const u8,37 args: []const []const u8,
36) !void {38) !void {
37 var color: Color = .auto;39 var color: Color = .auto;
38 var stdin_flag: bool = false;40 var stdin_flag = false;
39 var check_flag: bool = false;41 var check_flag = false;
40 var check_ast_flag: bool = false;42 var check_ast_flag = false;
43 var force_zon = false;
41 var input_files = std.ArrayList([]const u8).init(gpa);44 var input_files = std.ArrayList([]const u8).init(gpa);
42 defer input_files.deinit();45 defer input_files.deinit();
43 var excluded_files = std.ArrayList([]const u8).init(gpa);46 var excluded_files = std.ArrayList([]const u8).init(gpa);
...@@ -74,6 +77,8 @@ pub fn run(...@@ -74,6 +77,8 @@ pub fn run(
74 i += 1;77 i += 1;
75 const next_arg = args[i];78 const next_arg = args[i];
76 try excluded_files.append(next_arg);79 try excluded_files.append(next_arg);
80 } else if (mem.eql(u8, arg, "--zon")) {
81 force_zon = true;
77 } else {82 } else {
78 fatal("unrecognized parameter: '{s}'", .{arg});83 fatal("unrecognized parameter: '{s}'", .{arg});
79 }84 }
...@@ -94,23 +99,40 @@ pub fn run(...@@ -94,23 +99,40 @@ pub fn run(
94 };99 };
95 defer gpa.free(source_code);100 defer gpa.free(source_code);
96101
97 var tree = std.zig.Ast.parse(gpa, source_code, .zig) catch |err| {102 var tree = std.zig.Ast.parse(gpa, source_code, if (force_zon) .zon else .zig) catch |err| {
98 fatal("error parsing stdin: {}", .{err});103 fatal("error parsing stdin: {}", .{err});
99 };104 };
100 defer tree.deinit(gpa);105 defer tree.deinit(gpa);
101106
102 if (check_ast_flag) {107 if (check_ast_flag) {
103 var zir = try std.zig.AstGen.generate(gpa, tree);108 if (!force_zon) {
104109 var zir = try std.zig.AstGen.generate(gpa, tree);
105 if (zir.hasCompileErrors()) {110 defer zir.deinit(gpa);
106 var wip_errors: std.zig.ErrorBundle.Wip = undefined;111
107 try wip_errors.init(gpa);112 if (zir.hasCompileErrors()) {
108 defer wip_errors.deinit();113 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
109 try wip_errors.addZirErrorMessages(zir, tree, source_code, "<stdin>");114 try wip_errors.init(gpa);
110 var error_bundle = try wip_errors.toOwnedBundle("");115 defer wip_errors.deinit();
111 defer error_bundle.deinit(gpa);116 try wip_errors.addZirErrorMessages(zir, tree, source_code, "<stdin>");
112 error_bundle.renderToStdErr(color.renderOptions());117 var error_bundle = try wip_errors.toOwnedBundle("");
113 process.exit(2);118 defer error_bundle.deinit(gpa);
119 error_bundle.renderToStdErr(color.renderOptions());
120 process.exit(2);
121 }
122 } else {
123 const zoir = try std.zig.ZonGen.generate(gpa, tree);
124 defer zoir.deinit(gpa);
125
126 if (zoir.hasCompileErrors()) {
127 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
128 try wip_errors.init(gpa);
129 defer wip_errors.deinit();
130 try wip_errors.addZoirErrorMessages(zoir, tree, source_code, "<stdin>");
131 var error_bundle = try wip_errors.toOwnedBundle("");
132 defer error_bundle.deinit(gpa);
133 error_bundle.renderToStdErr(color.renderOptions());
134 process.exit(2);
135 }
114 }136 }
115 } else if (tree.errors.len != 0) {137 } else if (tree.errors.len != 0) {
116 try std.zig.printAstErrorsToStderr(gpa, tree, "<stdin>", color);138 try std.zig.printAstErrorsToStderr(gpa, tree, "<stdin>", color);
...@@ -131,12 +153,13 @@ pub fn run(...@@ -131,12 +153,13 @@ pub fn run(
131 fatal("expected at least one source file argument", .{});153 fatal("expected at least one source file argument", .{});
132 }154 }
133155
134 var fmt = Fmt{156 var fmt: Fmt = .{
135 .gpa = gpa,157 .gpa = gpa,
136 .arena = arena,158 .arena = arena,
137 .seen = Fmt.SeenMap.init(gpa),159 .seen = .init(gpa),
138 .any_error = false,160 .any_error = false,
139 .check_ast = check_ast_flag,161 .check_ast = check_ast_flag,
162 .force_zon = force_zon,
140 .color = color,163 .color = color,
141 .out_buffer = std.ArrayList(u8).init(gpa),164 .out_buffer = std.ArrayList(u8).init(gpa),
142 };165 };
...@@ -276,7 +299,13 @@ fn fmtPathFile(...@@ -276,7 +299,13 @@ fn fmtPathFile(
276 // Add to set after no longer possible to get error.IsDir.299 // Add to set after no longer possible to get error.IsDir.
277 if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;300 if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;
278301
279 var tree = try std.zig.Ast.parse(gpa, source_code, .zig);302 const mode: std.zig.Ast.Mode = mode: {
303 if (fmt.force_zon) break :mode .zon;
304 if (mem.endsWith(u8, sub_path, ".zon")) break :mode .zon;
305 break :mode .zig;
306 };
307
308 var tree = try std.zig.Ast.parse(gpa, source_code, mode);
280 defer tree.deinit(gpa);309 defer tree.deinit(gpa);
281310
282 if (tree.errors.len != 0) {311 if (tree.errors.len != 0) {
...@@ -289,18 +318,37 @@ fn fmtPathFile(...@@ -289,18 +318,37 @@ fn fmtPathFile(
289 if (stat.size > std.zig.max_src_size)318 if (stat.size > std.zig.max_src_size)
290 return error.FileTooBig;319 return error.FileTooBig;
291320
292 var zir = try std.zig.AstGen.generate(gpa, tree);321 switch (mode) {
293 defer zir.deinit(gpa);322 .zig => {
294323 var zir = try std.zig.AstGen.generate(gpa, tree);
295 if (zir.hasCompileErrors()) {324 defer zir.deinit(gpa);
296 var wip_errors: std.zig.ErrorBundle.Wip = undefined;325
297 try wip_errors.init(gpa);326 if (zir.hasCompileErrors()) {
298 defer wip_errors.deinit();327 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
299 try wip_errors.addZirErrorMessages(zir, tree, source_code, file_path);328 try wip_errors.init(gpa);
300 var error_bundle = try wip_errors.toOwnedBundle("");329 defer wip_errors.deinit();
301 defer error_bundle.deinit(gpa);330 try wip_errors.addZirErrorMessages(zir, tree, source_code, file_path);
302 error_bundle.renderToStdErr(fmt.color.renderOptions());331 var error_bundle = try wip_errors.toOwnedBundle("");
303 fmt.any_error = true;332 defer error_bundle.deinit(gpa);
333 error_bundle.renderToStdErr(fmt.color.renderOptions());
334 fmt.any_error = true;
335 }
336 },
337 .zon => {
338 var zoir = try std.zig.ZonGen.generate(gpa, tree);
339 defer zoir.deinit(gpa);
340
341 if (zoir.hasCompileErrors()) {
342 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
343 try wip_errors.init(gpa);
344 defer wip_errors.deinit();
345 try wip_errors.addZoirErrorMessages(zoir, tree, source_code, file_path);
346 var error_bundle = try wip_errors.toOwnedBundle("");
347 defer error_bundle.deinit(gpa);
348 error_bundle.renderToStdErr(fmt.color.renderOptions());
349 fmt.any_error = true;
350 }
351 },
304 }352 }
305 }353 }
306354
src/main.zig+123-71
...@@ -19,6 +19,7 @@ const Directory = std.Build.Cache.Directory;...@@ -19,6 +19,7 @@ const Directory = std.Build.Cache.Directory;
19const EnvVar = std.zig.EnvVar;19const EnvVar = std.zig.EnvVar;
20const LibCInstallation = std.zig.LibCInstallation;20const LibCInstallation = std.zig.LibCInstallation;
21const AstGen = std.zig.AstGen;21const AstGen = std.zig.AstGen;
22const ZonGen = std.zig.ZonGen;
22const Server = std.zig.Server;23const Server = std.zig.Server;
2324
24const tracy = @import("tracy.zig");25const tracy = @import("tracy.zig");
...@@ -6007,15 +6008,16 @@ fn parseCodeModel(arg: []const u8) std.builtin.CodeModel {...@@ -6007,15 +6008,16 @@ fn parseCodeModel(arg: []const u8) std.builtin.CodeModel {
6007const usage_ast_check =6008const usage_ast_check =
6008 \\Usage: zig ast-check [file]6009 \\Usage: zig ast-check [file]
6009 \\6010 \\
6010 \\ Given a .zig source file, reports any compile errors that can be6011 \\ Given a .zig source file or .zon file, reports any compile errors
6011 \\ ascertained on the basis of the source code alone, without target6012 \\ that can be ascertained on the basis of the source code alone,
6012 \\ information or type checking.6013 \\ without target information or type checking.
6013 \\6014 \\
6014 \\ If [file] is omitted, stdin is used.6015 \\ If [file] is omitted, stdin is used.
6015 \\6016 \\
6016 \\Options:6017 \\Options:
6017 \\ -h, --help Print this help and exit6018 \\ -h, --help Print this help and exit
6018 \\ --color [auto|off|on] Enable or disable colored error messages6019 \\ --color [auto|off|on] Enable or disable colored error messages
6020 \\ --zon Treat the input file as ZON, regardless of file extension
6019 \\ -t (debug option) Output ZIR in text form to stdout6021 \\ -t (debug option) Output ZIR in text form to stdout
6020 \\6022 \\
6021 \\6023 \\
...@@ -6032,6 +6034,7 @@ fn cmdAstCheck(...@@ -6032,6 +6034,7 @@ fn cmdAstCheck(
60326034
6033 var color: Color = .auto;6035 var color: Color = .auto;
6034 var want_output_text = false;6036 var want_output_text = false;
6037 var force_zon = false;
6035 var zig_source_file: ?[]const u8 = null;6038 var zig_source_file: ?[]const u8 = null;
60366039
6037 var i: usize = 0;6040 var i: usize = 0;
...@@ -6043,6 +6046,8 @@ fn cmdAstCheck(...@@ -6043,6 +6046,8 @@ fn cmdAstCheck(
6043 return cleanExit();6046 return cleanExit();
6044 } else if (mem.eql(u8, arg, "-t")) {6047 } else if (mem.eql(u8, arg, "-t")) {
6045 want_output_text = true;6048 want_output_text = true;
6049 } else if (mem.eql(u8, arg, "--zon")) {
6050 force_zon = true;
6046 } else if (mem.eql(u8, arg, "--color")) {6051 } else if (mem.eql(u8, arg, "--color")) {
6047 if (i + 1 >= args.len) {6052 if (i + 1 >= args.len) {
6048 fatal("expected [auto|on|off] after --color", .{});6053 fatal("expected [auto|on|off] after --color", .{});
...@@ -6110,89 +6115,136 @@ fn cmdAstCheck(...@@ -6110,89 +6115,136 @@ fn cmdAstCheck(
6110 file.stat.size = source.len;6115 file.stat.size = source.len;
6111 }6116 }
61126117
6118 const mode: Ast.Mode = mode: {
6119 if (force_zon) break :mode .zon;
6120 if (zig_source_file) |name| {
6121 if (mem.endsWith(u8, name, ".zon")) {
6122 break :mode .zon;
6123 }
6124 }
6125 break :mode .zig;
6126 };
6127
6113 file.mod = try Package.Module.createLimited(arena, .{6128 file.mod = try Package.Module.createLimited(arena, .{
6114 .root = Path.cwd(),6129 .root = Path.cwd(),
6115 .root_src_path = file.sub_file_path,6130 .root_src_path = file.sub_file_path,
6116 .fully_qualified_name = "root",6131 .fully_qualified_name = "root",
6117 });6132 });
61186133
6119 file.tree = try Ast.parse(gpa, file.source, .zig);6134 file.tree = try Ast.parse(gpa, file.source, mode);
6120 file.tree_loaded = true;6135 file.tree_loaded = true;
6121 defer file.tree.deinit(gpa);6136 defer file.tree.deinit(gpa);
61226137
6123 file.zir = try AstGen.generate(gpa, file.tree);6138 switch (mode) {
6124 file.zir_loaded = true;6139 .zig => {
6125 defer file.zir.deinit(gpa);6140 file.zir = try AstGen.generate(gpa, file.tree);
6141 file.zir_loaded = true;
6142 defer file.zir.deinit(gpa);
6143
6144 if (file.zir.hasCompileErrors()) {
6145 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
6146 try wip_errors.init(gpa);
6147 defer wip_errors.deinit();
6148 try Compilation.addZirErrorMessages(&wip_errors, &file);
6149 var error_bundle = try wip_errors.toOwnedBundle("");
6150 defer error_bundle.deinit(gpa);
6151 error_bundle.renderToStdErr(color.renderOptions());
6152
6153 if (file.zir.loweringFailed()) {
6154 process.exit(1);
6155 }
6156 }
61266157
6127 if (file.zir.hasCompileErrors()) {6158 if (!want_output_text) {
6128 var wip_errors: std.zig.ErrorBundle.Wip = undefined;6159 if (file.zir.hasCompileErrors()) {
6129 try wip_errors.init(gpa);6160 process.exit(1);
6130 defer wip_errors.deinit();6161 } else {
6131 try Compilation.addZirErrorMessages(&wip_errors, &file);6162 return cleanExit();
6132 var error_bundle = try wip_errors.toOwnedBundle("");6163 }
6133 defer error_bundle.deinit(gpa);6164 }
6134 error_bundle.renderToStdErr(color.renderOptions());6165 if (!build_options.enable_debug_extensions) {
6166 fatal("-t option only available in builds of zig with debug extensions", .{});
6167 }
61356168
6136 if (file.zir.loweringFailed()) {6169 {
6137 process.exit(1);6170 const token_bytes = @sizeOf(Ast.TokenList) +
6138 }6171 file.tree.tokens.len * (@sizeOf(std.zig.Token.Tag) + @sizeOf(Ast.ByteOffset));
6139 }6172 const tree_bytes = @sizeOf(Ast) + file.tree.nodes.len *
6173 (@sizeOf(Ast.Node.Tag) +
6174 @sizeOf(Ast.Node.Data) +
6175 @sizeOf(Ast.TokenIndex));
6176 const instruction_bytes = file.zir.instructions.len *
6177 // Here we don't use @sizeOf(Zir.Inst.Data) because it would include
6178 // the debug safety tag but we want to measure release size.
6179 (@sizeOf(Zir.Inst.Tag) + 8);
6180 const extra_bytes = file.zir.extra.len * @sizeOf(u32);
6181 const total_bytes = @sizeOf(Zir) + instruction_bytes + extra_bytes +
6182 file.zir.string_bytes.len * @sizeOf(u8);
6183 const stdout = io.getStdOut();
6184 const fmtIntSizeBin = std.fmt.fmtIntSizeBin;
6185 // zig fmt: off
6186 try stdout.writer().print(
6187 \\# Source bytes: {}
6188 \\# Tokens: {} ({})
6189 \\# AST Nodes: {} ({})
6190 \\# Total ZIR bytes: {}
6191 \\# Instructions: {d} ({})
6192 \\# String Table Bytes: {}
6193 \\# Extra Data Items: {d} ({})
6194 \\
6195 , .{
6196 fmtIntSizeBin(file.source.len),
6197 file.tree.tokens.len, fmtIntSizeBin(token_bytes),
6198 file.tree.nodes.len, fmtIntSizeBin(tree_bytes),
6199 fmtIntSizeBin(total_bytes),
6200 file.zir.instructions.len, fmtIntSizeBin(instruction_bytes),
6201 fmtIntSizeBin(file.zir.string_bytes.len),
6202 file.zir.extra.len, fmtIntSizeBin(extra_bytes),
6203 });
6204 // zig fmt: on
6205 }
61406206
6141 if (!want_output_text) {6207 try @import("print_zir.zig").renderAsTextToFile(gpa, &file, io.getStdOut());
6142 if (file.zir.hasCompileErrors()) {
6143 process.exit(1);
6144 } else {
6145 return cleanExit();
6146 }
6147 }
6148 if (!build_options.enable_debug_extensions) {
6149 fatal("-t option only available in builds of zig with debug extensions", .{});
6150 }
61516208
6152 {6209 if (file.zir.hasCompileErrors()) {
6153 const token_bytes = @sizeOf(Ast.TokenList) +6210 process.exit(1);
6154 file.tree.tokens.len * (@sizeOf(std.zig.Token.Tag) + @sizeOf(Ast.ByteOffset));6211 } else {
6155 const tree_bytes = @sizeOf(Ast) + file.tree.nodes.len *6212 return cleanExit();
6156 (@sizeOf(Ast.Node.Tag) +6213 }
6157 @sizeOf(Ast.Node.Data) +6214 },
6158 @sizeOf(Ast.TokenIndex));6215 .zon => {
6159 const instruction_bytes = file.zir.instructions.len *6216 const zoir = try ZonGen.generate(gpa, file.tree);
6160 // Here we don't use @sizeOf(Zir.Inst.Data) because it would include6217 defer zoir.deinit(gpa);
6161 // the debug safety tag but we want to measure release size.
6162 (@sizeOf(Zir.Inst.Tag) + 8);
6163 const extra_bytes = file.zir.extra.len * @sizeOf(u32);
6164 const total_bytes = @sizeOf(Zir) + instruction_bytes + extra_bytes +
6165 file.zir.string_bytes.len * @sizeOf(u8);
6166 const stdout = io.getStdOut();
6167 const fmtIntSizeBin = std.fmt.fmtIntSizeBin;
6168 // zig fmt: off
6169 try stdout.writer().print(
6170 \\# Source bytes: {}
6171 \\# Tokens: {} ({})
6172 \\# AST Nodes: {} ({})
6173 \\# Total ZIR bytes: {}
6174 \\# Instructions: {d} ({})
6175 \\# String Table Bytes: {}
6176 \\# Extra Data Items: {d} ({})
6177 \\
6178 , .{
6179 fmtIntSizeBin(file.source.len),
6180 file.tree.tokens.len, fmtIntSizeBin(token_bytes),
6181 file.tree.nodes.len, fmtIntSizeBin(tree_bytes),
6182 fmtIntSizeBin(total_bytes),
6183 file.zir.instructions.len, fmtIntSizeBin(instruction_bytes),
6184 fmtIntSizeBin(file.zir.string_bytes.len),
6185 file.zir.extra.len, fmtIntSizeBin(extra_bytes),
6186 });
6187 // zig fmt: on
6188 }
61896218
6190 try @import("print_zir.zig").renderAsTextToFile(gpa, &file, io.getStdOut());6219 if (zoir.hasCompileErrors()) {
6220 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
6221 try wip_errors.init(gpa);
6222 defer wip_errors.deinit();
61916223
6192 if (file.zir.hasCompileErrors()) {6224 {
6193 process.exit(1);6225 const src_path = try file.fullPath(gpa);
6194 } else {6226 defer gpa.free(src_path);
6195 return cleanExit();6227 try wip_errors.addZoirErrorMessages(zoir, file.tree, file.source, src_path);
6228 }
6229
6230 var error_bundle = try wip_errors.toOwnedBundle("");
6231 defer error_bundle.deinit(gpa);
6232 error_bundle.renderToStdErr(color.renderOptions());
6233
6234 process.exit(1);
6235 }
6236
6237 if (!want_output_text) {
6238 return cleanExit();
6239 }
6240
6241 if (!build_options.enable_debug_extensions) {
6242 fatal("-t option only available in builds of zig with debug extensions", .{});
6243 }
6244
6245 try @import("print_zoir.zig").renderToFile(zoir, arena, io.getStdOut());
6246 return cleanExit();
6247 },
6196 }6248 }
6197}6249}
61986250
src/print_zoir.zig created+122
...@@ -0,0 +1,122 @@
1pub fn renderToFile(zoir: Zoir, arena: Allocator, f: std.fs.File) (std.fs.File.WriteError || Allocator.Error)!void {
2 var bw = std.io.bufferedWriter(f.writer());
3 try renderToWriter(zoir, arena, bw.writer());
4 try bw.flush();
5}
6
7pub fn renderToWriter(zoir: Zoir, arena: Allocator, w: anytype) (@TypeOf(w).Error || Allocator.Error)!void {
8 assert(!zoir.hasCompileErrors());
9
10 const fmtIntSizeBin = std.fmt.fmtIntSizeBin;
11 const bytes_per_node = comptime n: {
12 var n: usize = 0;
13 for (@typeInfo(Zoir.Node.Repr).@"struct".fields) |f| {
14 n += @sizeOf(f.type);
15 }
16 break :n n;
17 };
18
19 const node_bytes = zoir.nodes.len * bytes_per_node;
20 const extra_bytes = zoir.extra.len * @sizeOf(u32);
21 const limb_bytes = zoir.limbs.len * @sizeOf(std.math.big.Limb);
22 const string_bytes = zoir.string_bytes.len;
23
24 // zig fmt: off
25 try w.print(
26 \\# Nodes: {} ({})
27 \\# Extra Data Items: {} ({})
28 \\# BigInt Limbs: {} ({})
29 \\# String Table Bytes: {}
30 \\# Total ZON Bytes: {}
31 \\
32 , .{
33 zoir.nodes.len, fmtIntSizeBin(node_bytes),
34 zoir.extra.len, fmtIntSizeBin(extra_bytes),
35 zoir.limbs.len, fmtIntSizeBin(limb_bytes),
36 fmtIntSizeBin(string_bytes),
37 fmtIntSizeBin(node_bytes + extra_bytes + limb_bytes + string_bytes),
38 });
39 // zig fmt: on
40 var pz: PrintZon = .{
41 .w = w.any(),
42 .arena = arena,
43 .zoir = zoir,
44 .indent = 0,
45 };
46
47 return @errorCast(pz.renderRoot());
48}
49
50const PrintZon = struct {
51 w: std.io.AnyWriter,
52 arena: Allocator,
53 zoir: Zoir,
54 indent: u32,
55
56 fn renderRoot(pz: *PrintZon) anyerror!void {
57 try pz.renderNode(.root);
58 try pz.w.writeByte('\n');
59 }
60
61 fn renderNode(pz: *PrintZon, node: Zoir.Node.Index) anyerror!void {
62 const zoir = pz.zoir;
63 try pz.w.print("%{d} = ", .{@intFromEnum(node)});
64 switch (node.get(zoir)) {
65 .true => try pz.w.writeAll("true"),
66 .false => try pz.w.writeAll("false"),
67 .null => try pz.w.writeAll("null"),
68 .pos_inf => try pz.w.writeAll("inf"),
69 .neg_inf => try pz.w.writeAll("-inf"),
70 .nan => try pz.w.writeAll("nan"),
71 .int_literal => |storage| switch (storage) {
72 .small => |x| try pz.w.print("int({d})", .{x}),
73 .big => |x| {
74 const str = try x.toStringAlloc(pz.arena, 10, .lower);
75 try pz.w.print("int(big {s})", .{str});
76 },
77 },
78 .float_literal => |x| try pz.w.print("float({d})", .{x}),
79 .char_literal => |x| try pz.w.print("char({d})", .{x}),
80 .enum_literal => |x| try pz.w.print("enum_literal({p})", .{std.zig.fmtId(x.get(zoir))}),
81 .string_literal => |x| try pz.w.print("str(\"{}\")", .{std.zig.fmtEscapes(x)}),
82 .empty_literal => try pz.w.writeAll("empty_literal(.{})"),
83 .array_literal => |vals| {
84 try pz.w.writeAll("array_literal({");
85 pz.indent += 1;
86 for (0..vals.len) |idx| {
87 try pz.newline();
88 try pz.renderNode(vals.at(@intCast(idx)));
89 try pz.w.writeByte(',');
90 }
91 pz.indent -= 1;
92 try pz.newline();
93 try pz.w.writeAll("})");
94 },
95 .struct_literal => |s| {
96 try pz.w.writeAll("struct_literal({");
97 pz.indent += 1;
98 for (s.names, 0..s.vals.len) |name, idx| {
99 try pz.newline();
100 try pz.w.print("[{p}] ", .{std.zig.fmtId(name.get(zoir))});
101 try pz.renderNode(s.vals.at(@intCast(idx)));
102 try pz.w.writeByte(',');
103 }
104 pz.indent -= 1;
105 try pz.newline();
106 try pz.w.writeAll("})");
107 },
108 }
109 }
110
111 fn newline(pz: *PrintZon) !void {
112 try pz.w.writeByte('\n');
113 for (0..pz.indent) |_| {
114 try pz.w.writeByteNTimes(' ', 2);
115 }
116 }
117};
118
119const std = @import("std");
120const assert = std.debug.assert;
121const Allocator = std.mem.Allocator;
122const Zoir = std.zig.Zoir;