Skip to content

Commit c7485d7

Browse files
committed
compiler: introduce ZonGen and make ast-check run it for ZON inputs
Currently, `zig ast-check` fails on ZON files, because it tries to interpret the file as Zig source code. This commit introduces a new verification pass, `std.zig.ZonGen`, which applies to an AST in ZON mode. Like `AstGen`, this pass also converts the AST into a more helpful format. Rather than a sequence of instructions like `Zir`, the output format of `ZonGen` is a new datastructure called `Zoir`. This type is essentially a simpler form of AST, containing only the information required for consumers of ZON. It is also far more compact than `std.zig.Ast`, with the size generally being comparable to the size of the well-formatted source file. The emitted `Zoir` is currently not used aside from the `-t` option to `ast-check` which causes it to be dumped to stdout. However, in future, it can be used for comptime `@import` of ZON files, as well as for simpler handling of files like `build.zig.zon`, and even by other parts of the Zig Standard Library. Resolves: #22078
1 parent d12c0bf commit c7485d7

File tree

9 files changed

+1531
-191
lines changed

9 files changed

+1531
-191
lines changed

lib/std/zig.zig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ pub const isPrimitive = primitives.isPrimitive;
1414
pub const Ast = @import("zig/Ast.zig");
1515
pub const AstGen = @import("zig/AstGen.zig");
1616
pub const Zir = @import("zig/Zir.zig");
17+
pub const Zoir = @import("zig/Zoir.zig");
18+
pub const ZonGen = @import("zig/ZonGen.zig");
1719
pub const system = @import("zig/system.zig");
1820
pub const CrossTarget = @compileError("deprecated; use std.Target.Query");
1921
pub const BuiltinFn = @import("zig/BuiltinFn.zig");

lib/std/zig/AstGen.zig

Lines changed: 25 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ fn appendRefsAssumeCapacity(astgen: *AstGen, refs: []const Zir.Inst.Ref) void {
130130
}
131131

132132
pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {
133+
assert(tree.mode == .zig);
134+
133135
var arena = std.heap.ArenaAllocator.init(gpa);
134136
defer arena.deinit();
135137

@@ -11413,83 +11415,7 @@ fn parseStrLit(
1141311415

1141411416
fn failWithStrLitError(astgen: *AstGen, err: std.zig.string_literal.Error, token: Ast.TokenIndex, bytes: []const u8, offset: u32) InnerError {
1141511417
const raw_string = bytes[offset..];
11416-
switch (err) {
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-
}
11418+
return err.lower(raw_string, offset, AstGen.failOff, .{ astgen, token });
1149311419
}
1149411420

1149511421
fn failNode(
@@ -14019,30 +13945,40 @@ fn emitDbgStmtForceCurrentIndex(gz: *GenZir, lc: LineColumn) !void {
1401913945
}
1402013946

1402113947
fn lowerAstErrors(astgen: *AstGen) !void {
13948+
const gpa = astgen.gpa;
1402213949
const tree = astgen.tree;
1402313950
assert(tree.errors.len > 0);
1402413951

14025-
const gpa = astgen.gpa;
14026-
const parse_err = tree.errors[0];
14027-
1402813952
var msg: std.ArrayListUnmanaged(u8) = .empty;
1402913953
defer msg.deinit(gpa);
1403013954

1403113955
var notes: std.ArrayListUnmanaged(u32) = .empty;
1403213956
defer notes.deinit(gpa);
1403313957

14034-
for (tree.errors[1..]) |note| {
14035-
if (!note.is_note) break;
14036-
13958+
var cur_err = tree.errors[0];
13959+
for (tree.errors[1..]) |err| {
13960+
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+
}
1403713975
msg.clearRetainingCapacity();
14038-
try tree.renderError(note, msg.writer(gpa));
14039-
try notes.append(gpa, try astgen.errNoteTok(note.token, "{s}", .{msg.items}));
1404013976
}
1404113977

14042-
const extra_offset = tree.errorOffset(parse_err);
14043-
msg.clearRetainingCapacity();
14044-
try tree.renderError(parse_err, msg.writer(gpa));
14045-
try astgen.appendErrorTokNotesOff(parse_err.token, extra_offset, "{s}", .{msg.items}, notes.items);
13978+
// Flush error
13979+
const extra_offset = tree.errorOffset(cur_err);
13980+
try tree.renderError(cur_err, msg.writer(gpa));
13981+
try astgen.appendErrorTokNotesOff(cur_err.token, extra_offset, "{s}", .{msg.items}, notes.items);
1404613982
}
1404713983

1404813984
const DeclarationName = union(enum) {

lib/std/zig/ErrorBundle.zig

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,7 @@ pub const Wip = struct {
507507
}
508508

509509
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));
511511
const block = zir.extraData(Zir.Inst.Block, item.data.notes);
512512
const body = zir.extra[block.end..][0..block.data.body_len];
513513
for (notes_start.., body) |note_i, body_elem| {
@@ -547,6 +547,77 @@ pub const Wip = struct {
547547
}
548548
}
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+
550621
fn addOtherMessage(wip: *Wip, other: ErrorBundle, msg_index: MessageIndex) !MessageIndex {
551622
const other_msg = other.getErrorMessage(msg_index);
552623
const src_loc = try wip.addOtherSourceLocation(other, other_msg.src_loc);

0 commit comments

Comments
 (0)