authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-02-24 18:49:07-08:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-02-24 18:49:07-08:00
logd7049fc8e0709619b8aa6766b37abeae946703b2
treefd575717d5ca9e585b7d94e2028f80614dbf6c2d
parent8b9434871ea437840d25f073b945466359f402f9
parent9ada7638a5dbb535ba37223a14478691dd60cf6a
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #7920 from ziglang/ast-memory-layout

Rework AST memory layout for better memory usage and performance

42 files changed, 20800 insertions(+), 16573 deletions(-)

CMakeLists.txt+2-1
......@@ -370,7 +370,6 @@ set(ZIG_STAGE2_SOURCES
370370 "${CMAKE_SOURCE_DIR}/lib/std/heap.zig"
371371 "${CMAKE_SOURCE_DIR}/lib/std/heap/arena_allocator.zig"
372372 "${CMAKE_SOURCE_DIR}/lib/std/io.zig"
373 "${CMAKE_SOURCE_DIR}/lib/std/io/auto_indenting_stream.zig"
374373 "${CMAKE_SOURCE_DIR}/lib/std/io/buffered_atomic_file.zig"
375374 "${CMAKE_SOURCE_DIR}/lib/std/io/buffered_writer.zig"
376375 "${CMAKE_SOURCE_DIR}/lib/std/io/change_detection_stream.zig"
......@@ -408,6 +407,7 @@ set(ZIG_STAGE2_SOURCES
408407 "${CMAKE_SOURCE_DIR}/lib/std/meta.zig"
409408 "${CMAKE_SOURCE_DIR}/lib/std/meta/trailer_flags.zig"
410409 "${CMAKE_SOURCE_DIR}/lib/std/meta/trait.zig"
410 "${CMAKE_SOURCE_DIR}/lib/std/multi_array_list.zig"
411411 "${CMAKE_SOURCE_DIR}/lib/std/os.zig"
412412 "${CMAKE_SOURCE_DIR}/lib/std/os/bits.zig"
413413 "${CMAKE_SOURCE_DIR}/lib/std/os/bits/linux.zig"
......@@ -573,6 +573,7 @@ set(ZIG_STAGE2_SOURCES
573573 "${CMAKE_SOURCE_DIR}/src/target.zig"
574574 "${CMAKE_SOURCE_DIR}/src/tracy.zig"
575575 "${CMAKE_SOURCE_DIR}/src/translate_c.zig"
576 "${CMAKE_SOURCE_DIR}/src/translate_c/ast.zig"
576577 "${CMAKE_SOURCE_DIR}/src/type.zig"
577578 "${CMAKE_SOURCE_DIR}/src/value.zig"
578579 "${CMAKE_SOURCE_DIR}/src/windows_sdk.zig"
README.md+1-1
......@@ -5,7 +5,7 @@ A general-purpose programming language and toolchain for maintaining
55
66## Resources
77
8 * [Introduction](https://ziglang.org/#Introduction)
8 * [Introduction](https://ziglang.org/learn/#introduction)
99 * [Download & Documentation](https://ziglang.org/download)
1010 * [Chapter 0 - Getting Started | ZigLearn.org](https://ziglearn.org/)
1111 * [Community](https://github.com/ziglang/zig/wiki/Community)
build.zig+2
......@@ -77,10 +77,12 @@ pub fn build(b: *Builder) !void {
7777
7878 const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source");
7979 const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse enable_llvm;
80 const strip = b.option(bool, "strip", "Omit debug information") orelse false;
8081
8182 const main_file = if (is_stage1) "src/stage1.zig" else "src/main.zig";
8283
8384 var exe = b.addExecutable("zig", main_file);
85 exe.strip = strip;
8486 exe.install();
8587 exe.setBuildMode(mode);
8688 exe.setTarget(target);
doc/docgen.zig+134-121
......@@ -781,106 +781,119 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: anytype, source_token:
781781 next_tok_is_fn = false;
782782
783783 const token = tokenizer.next();
784 if (mem.indexOf(u8, src[index..token.loc.start], "//")) |comment_start_off| {
785 // render one comment
786 const comment_start = index + comment_start_off;
787 const comment_end_off = mem.indexOf(u8, src[comment_start .. token.loc.start], "\n");
788 const comment_end = if (comment_end_off) |o| comment_start + o else token.loc.start;
789
790 try writeEscaped(out, src[index..comment_start]);
791 try out.writeAll("<span class=\"tok-comment\">");
792 try writeEscaped(out, src[comment_start .. comment_end]);
793 try out.writeAll("</span>");
794 index = comment_end;
795 tokenizer.index = index;
796 continue;
797 }
798
784799 try writeEscaped(out, src[index..token.loc.start]);
785 switch (token.id) {
786 .Eof => break,
787
788 .Keyword_align,
789 .Keyword_and,
790 .Keyword_asm,
791 .Keyword_async,
792 .Keyword_await,
793 .Keyword_break,
794 .Keyword_catch,
795 .Keyword_comptime,
796 .Keyword_const,
797 .Keyword_continue,
798 .Keyword_defer,
799 .Keyword_else,
800 .Keyword_enum,
801 .Keyword_errdefer,
802 .Keyword_error,
803 .Keyword_export,
804 .Keyword_extern,
805 .Keyword_for,
806 .Keyword_if,
807 .Keyword_inline,
808 .Keyword_noalias,
809 .Keyword_noinline,
810 .Keyword_nosuspend,
811 .Keyword_opaque,
812 .Keyword_or,
813 .Keyword_orelse,
814 .Keyword_packed,
815 .Keyword_anyframe,
816 .Keyword_pub,
817 .Keyword_resume,
818 .Keyword_return,
819 .Keyword_linksection,
820 .Keyword_callconv,
821 .Keyword_struct,
822 .Keyword_suspend,
823 .Keyword_switch,
824 .Keyword_test,
825 .Keyword_threadlocal,
826 .Keyword_try,
827 .Keyword_union,
828 .Keyword_unreachable,
829 .Keyword_usingnamespace,
830 .Keyword_var,
831 .Keyword_volatile,
832 .Keyword_allowzero,
833 .Keyword_while,
834 .Keyword_anytype,
800 switch (token.tag) {
801 .eof => break,
802
803 .keyword_align,
804 .keyword_and,
805 .keyword_asm,
806 .keyword_async,
807 .keyword_await,
808 .keyword_break,
809 .keyword_catch,
810 .keyword_comptime,
811 .keyword_const,
812 .keyword_continue,
813 .keyword_defer,
814 .keyword_else,
815 .keyword_enum,
816 .keyword_errdefer,
817 .keyword_error,
818 .keyword_export,
819 .keyword_extern,
820 .keyword_for,
821 .keyword_if,
822 .keyword_inline,
823 .keyword_noalias,
824 .keyword_noinline,
825 .keyword_nosuspend,
826 .keyword_opaque,
827 .keyword_or,
828 .keyword_orelse,
829 .keyword_packed,
830 .keyword_anyframe,
831 .keyword_pub,
832 .keyword_resume,
833 .keyword_return,
834 .keyword_linksection,
835 .keyword_callconv,
836 .keyword_struct,
837 .keyword_suspend,
838 .keyword_switch,
839 .keyword_test,
840 .keyword_threadlocal,
841 .keyword_try,
842 .keyword_union,
843 .keyword_unreachable,
844 .keyword_usingnamespace,
845 .keyword_var,
846 .keyword_volatile,
847 .keyword_allowzero,
848 .keyword_while,
849 .keyword_anytype,
835850 => {
836851 try out.writeAll("<span class=\"tok-kw\">");
837852 try writeEscaped(out, src[token.loc.start..token.loc.end]);
838853 try out.writeAll("</span>");
839854 },
840855
841 .Keyword_fn => {
856 .keyword_fn => {
842857 try out.writeAll("<span class=\"tok-kw\">");
843858 try writeEscaped(out, src[token.loc.start..token.loc.end]);
844859 try out.writeAll("</span>");
845860 next_tok_is_fn = true;
846861 },
847862
848 .Keyword_undefined,
849 .Keyword_null,
850 .Keyword_true,
851 .Keyword_false,
863 .keyword_undefined,
864 .keyword_null,
865 .keyword_true,
866 .keyword_false,
852867 => {
853868 try out.writeAll("<span class=\"tok-null\">");
854869 try writeEscaped(out, src[token.loc.start..token.loc.end]);
855870 try out.writeAll("</span>");
856871 },
857872
858 .StringLiteral,
859 .MultilineStringLiteralLine,
860 .CharLiteral,
873 .string_literal,
874 .multiline_string_literal_line,
875 .char_literal,
861876 => {
862877 try out.writeAll("<span class=\"tok-str\">");
863878 try writeEscaped(out, src[token.loc.start..token.loc.end]);
864879 try out.writeAll("</span>");
865880 },
866881
867 .Builtin => {
882 .builtin => {
868883 try out.writeAll("<span class=\"tok-builtin\">");
869884 try writeEscaped(out, src[token.loc.start..token.loc.end]);
870885 try out.writeAll("</span>");
871886 },
872887
873 .LineComment,
874 .DocComment,
875 .ContainerDocComment,
876 .ShebangLine,
888 .doc_comment,
889 .container_doc_comment,
877890 => {
878891 try out.writeAll("<span class=\"tok-comment\">");
879892 try writeEscaped(out, src[token.loc.start..token.loc.end]);
880893 try out.writeAll("</span>");
881894 },
882895
883 .Identifier => {
896 .identifier => {
884897 if (prev_tok_was_fn) {
885898 try out.writeAll("<span class=\"tok-fn\">");
886899 try writeEscaped(out, src[token.loc.start..token.loc.end]);
......@@ -908,71 +921,71 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: anytype, source_token:
908921 }
909922 },
910923
911 .IntegerLiteral,
912 .FloatLiteral,
924 .integer_literal,
925 .float_literal,
913926 => {
914927 try out.writeAll("<span class=\"tok-number\">");
915928 try writeEscaped(out, src[token.loc.start..token.loc.end]);
916929 try out.writeAll("</span>");
917930 },
918931
919 .Bang,
920 .Pipe,
921 .PipePipe,
922 .PipeEqual,
923 .Equal,
924 .EqualEqual,
925 .EqualAngleBracketRight,
926 .BangEqual,
927 .LParen,
928 .RParen,
929 .Semicolon,
930 .Percent,
931 .PercentEqual,
932 .LBrace,
933 .RBrace,
934 .LBracket,
935 .RBracket,
936 .Period,
937 .PeriodAsterisk,
938 .Ellipsis2,
939 .Ellipsis3,
940 .Caret,
941 .CaretEqual,
942 .Plus,
943 .PlusPlus,
944 .PlusEqual,
945 .PlusPercent,
946 .PlusPercentEqual,
947 .Minus,
948 .MinusEqual,
949 .MinusPercent,
950 .MinusPercentEqual,
951 .Asterisk,
952 .AsteriskEqual,
953 .AsteriskAsterisk,
954 .AsteriskPercent,
955 .AsteriskPercentEqual,
956 .Arrow,
957 .Colon,
958 .Slash,
959 .SlashEqual,
960 .Comma,
961 .Ampersand,
962 .AmpersandEqual,
963 .QuestionMark,
964 .AngleBracketLeft,
965 .AngleBracketLeftEqual,
966 .AngleBracketAngleBracketLeft,
967 .AngleBracketAngleBracketLeftEqual,
968 .AngleBracketRight,
969 .AngleBracketRightEqual,
970 .AngleBracketAngleBracketRight,
971 .AngleBracketAngleBracketRightEqual,
972 .Tilde,
932 .bang,
933 .pipe,
934 .pipe_pipe,
935 .pipe_equal,
936 .equal,
937 .equal_equal,
938 .equal_angle_bracket_right,
939 .bang_equal,
940 .l_paren,
941 .r_paren,
942 .semicolon,
943 .percent,
944 .percent_equal,
945 .l_brace,
946 .r_brace,
947 .l_bracket,
948 .r_bracket,
949 .period,
950 .period_asterisk,
951 .ellipsis2,
952 .ellipsis3,
953 .caret,
954 .caret_equal,
955 .plus,
956 .plus_plus,
957 .plus_equal,
958 .plus_percent,
959 .plus_percent_equal,
960 .minus,
961 .minus_equal,
962 .minus_percent,
963 .minus_percent_equal,
964 .asterisk,
965 .asterisk_equal,
966 .asterisk_asterisk,
967 .asterisk_percent,
968 .asterisk_percent_equal,
969 .arrow,
970 .colon,
971 .slash,
972 .slash_equal,
973 .comma,
974 .ampersand,
975 .ampersand_equal,
976 .question_mark,
977 .angle_bracket_left,
978 .angle_bracket_left_equal,
979 .angle_bracket_angle_bracket_left,
980 .angle_bracket_angle_bracket_left_equal,
981 .angle_bracket_right,
982 .angle_bracket_right_equal,
983 .angle_bracket_angle_bracket_right,
984 .angle_bracket_angle_bracket_right_equal,
985 .tilde,
973986 => try writeEscaped(out, src[token.loc.start..token.loc.end]),
974987
975 .Invalid, .Invalid_ampersands, .Invalid_periodasterisks => return parseError(
988 .invalid, .invalid_ampersands, .invalid_periodasterisks => return parseError(
976989 docgen_tokenizer,
977990 source_token,
978991 "syntax error",
lib/std/heap/general_purpose_allocator.zig+27-1
......@@ -98,7 +98,7 @@
9898//! in a `std.HashMap` using the backing allocator.
9999
100100const std = @import("std");
101const log = std.log.scoped(.std);
101const log = std.log.scoped(.gpa);
102102const math = std.math;
103103const assert = std.debug.assert;
104104const mem = std.mem;
......@@ -162,6 +162,9 @@ pub const Config = struct {
162162 /// logged error messages with stack trace details. The downside is that every allocation
163163 /// will be leaked!
164164 never_unmap: bool = false,
165
166 /// Enables emitting info messages with the size and address of every allocation.
167 verbose_log: bool = false,
165168};
166169
167170pub fn GeneralPurposeAllocator(comptime config: Config) type {
......@@ -454,10 +457,19 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
454457 const result_len = try self.backing_allocator.resizeFn(self.backing_allocator, old_mem, old_align, new_size, len_align, ret_addr);
455458
456459 if (result_len == 0) {
460 if (config.verbose_log) {
461 log.info("large free {d} bytes at {*}", .{ old_mem.len, old_mem.ptr });
462 }
463
457464 self.large_allocations.removeAssertDiscard(@ptrToInt(old_mem.ptr));
458465 return 0;
459466 }
460467
468 if (config.verbose_log) {
469 log.info("large resize {d} bytes at {*} to {d}", .{
470 old_mem.len, old_mem.ptr, new_size,
471 });
472 }
461473 entry.value.bytes = old_mem.ptr[0..result_len];
462474 collectStackTrace(ret_addr, &entry.value.stack_addresses);
463475 return result_len;
......@@ -568,6 +580,9 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
568580 } else {
569581 @memset(old_mem.ptr, undefined, old_mem.len);
570582 }
583 if (config.verbose_log) {
584 log.info("small free {d} bytes at {*}", .{ old_mem.len, old_mem.ptr });
585 }
571586 return @as(usize, 0);
572587 }
573588 const new_aligned_size = math.max(new_size, old_align);
......@@ -576,6 +591,11 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
576591 if (old_mem.len > new_size) {
577592 @memset(old_mem.ptr + new_size, undefined, old_mem.len - new_size);
578593 }
594 if (config.verbose_log) {
595 log.info("small resize {d} bytes at {*} to {d}", .{
596 old_mem.len, old_mem.ptr, new_size,
597 });
598 }
579599 return new_size;
580600 }
581601 return error.OutOfMemory;
......@@ -623,6 +643,9 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
623643 gop.entry.value.bytes = slice;
624644 collectStackTrace(ret_addr, &gop.entry.value.stack_addresses);
625645
646 if (config.verbose_log) {
647 log.info("large alloc {d} bytes at {*}", .{ slice.len, slice.ptr });
648 }
626649 return slice;
627650 }
628651
......@@ -632,6 +655,9 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
632655
633656 const new_size_class = math.ceilPowerOfTwoAssert(usize, new_aligned_size);
634657 const ptr = try self.allocSlot(new_size_class, ret_addr);
658 if (config.verbose_log) {
659 log.info("small alloc {d} bytes at {*}", .{ len, ptr });
660 }
635661 return ptr[0..len];
636662 }
637663
lib/std/io.zig-3
......@@ -142,9 +142,6 @@ pub const bitReader = @import("io/bit_reader.zig").bitReader;
142142pub const BitWriter = @import("io/bit_writer.zig").BitWriter;
143143pub const bitWriter = @import("io/bit_writer.zig").bitWriter;
144144
145pub const AutoIndentingStream = @import("io/auto_indenting_stream.zig").AutoIndentingStream;
146pub const autoIndentingStream = @import("io/auto_indenting_stream.zig").autoIndentingStream;
147
148145pub const ChangeDetectionStream = @import("io/change_detection_stream.zig").ChangeDetectionStream;
149146pub const changeDetectionStream = @import("io/change_detection_stream.zig").changeDetectionStream;
150147
lib/std/io/auto_indenting_stream.zig deleted-154
......@@ -1,154 +0,0 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2021 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6
7const std = @import("../std.zig");
8const io = std.io;
9const mem = std.mem;
10const assert = std.debug.assert;
11
12/// Automatically inserts indentation of written data by keeping
13/// track of the current indentation level
14pub fn AutoIndentingStream(comptime UnderlyingWriter: type) type {
15 return struct {
16 const Self = @This();
17 pub const Error = UnderlyingWriter.Error;
18 pub const Writer = io.Writer(*Self, Error, write);
19
20 underlying_writer: UnderlyingWriter,
21
22 indent_count: usize = 0,
23 indent_delta: usize,
24 current_line_empty: bool = true,
25 indent_one_shot_count: usize = 0, // automatically popped when applied
26 applied_indent: usize = 0, // the most recently applied indent
27 indent_next_line: usize = 0, // not used until the next line
28
29 pub fn writer(self: *Self) Writer {
30 return .{ .context = self };
31 }
32
33 pub fn write(self: *Self, bytes: []const u8) Error!usize {
34 if (bytes.len == 0)
35 return @as(usize, 0);
36
37 try self.applyIndent();
38 return self.writeNoIndent(bytes);
39 }
40
41 // Change the indent delta without changing the final indentation level
42 pub fn setIndentDelta(self: *Self, indent_delta: usize) void {
43 if (self.indent_delta == indent_delta) {
44 return;
45 } else if (self.indent_delta > indent_delta) {
46 assert(self.indent_delta % indent_delta == 0);
47 self.indent_count = self.indent_count * (self.indent_delta / indent_delta);
48 } else {
49 // assert that the current indentation (in spaces) in a multiple of the new delta
50 assert((self.indent_count * self.indent_delta) % indent_delta == 0);
51 self.indent_count = self.indent_count / (indent_delta / self.indent_delta);
52 }
53 self.indent_delta = indent_delta;
54 }
55
56 fn writeNoIndent(self: *Self, bytes: []const u8) Error!usize {
57 if (bytes.len == 0)
58 return @as(usize, 0);
59
60 try self.underlying_writer.writeAll(bytes);
61 if (bytes[bytes.len - 1] == '\n')
62 self.resetLine();
63 return bytes.len;
64 }
65
66 pub fn insertNewline(self: *Self) Error!void {
67 _ = try self.writeNoIndent("\n");
68 }
69
70 fn resetLine(self: *Self) void {
71 self.current_line_empty = true;
72 self.indent_next_line = 0;
73 }
74
75 /// Insert a newline unless the current line is blank
76 pub fn maybeInsertNewline(self: *Self) Error!void {
77 if (!self.current_line_empty)
78 try self.insertNewline();
79 }
80
81 /// Push default indentation
82 pub fn pushIndent(self: *Self) void {
83 // Doesn't actually write any indentation.
84 // Just primes the stream to be able to write the correct indentation if it needs to.
85 self.indent_count += 1;
86 }
87
88 /// Push an indent that is automatically popped after being applied
89 pub fn pushIndentOneShot(self: *Self) void {
90 self.indent_one_shot_count += 1;
91 self.pushIndent();
92 }
93
94 /// Turns all one-shot indents into regular indents
95 /// Returns number of indents that must now be manually popped
96 pub fn lockOneShotIndent(self: *Self) usize {
97 var locked_count = self.indent_one_shot_count;
98 self.indent_one_shot_count = 0;
99 return locked_count;
100 }
101
102 /// Push an indent that should not take effect until the next line
103 pub fn pushIndentNextLine(self: *Self) void {
104 self.indent_next_line += 1;
105 self.pushIndent();
106 }
107
108 pub fn popIndent(self: *Self) void {
109 assert(self.indent_count != 0);
110 self.indent_count -= 1;
111
112 if (self.indent_next_line > 0)
113 self.indent_next_line -= 1;
114 }
115
116 /// Writes ' ' bytes if the current line is empty
117 fn applyIndent(self: *Self) Error!void {
118 const current_indent = self.currentIndent();
119 if (self.current_line_empty and current_indent > 0) {
120 try self.underlying_writer.writeByteNTimes(' ', current_indent);
121 self.applied_indent = current_indent;
122 }
123
124 self.indent_count -= self.indent_one_shot_count;
125 self.indent_one_shot_count = 0;
126 self.current_line_empty = false;
127 }
128
129 /// Checks to see if the most recent indentation exceeds the currently pushed indents
130 pub fn isLineOverIndented(self: *Self) bool {
131 if (self.current_line_empty) return false;
132 return self.applied_indent > self.currentIndent();
133 }
134
135 fn currentIndent(self: *Self) usize {
136 var indent_current: usize = 0;
137 if (self.indent_count > 0) {
138 const indent_count = self.indent_count - self.indent_next_line;
139 indent_current = indent_count * self.indent_delta;
140 }
141 return indent_current;
142 }
143 };
144}
145
146pub fn autoIndentingStream(
147 indent_delta: usize,
148 underlying_writer: anytype,
149) AutoIndentingStream(@TypeOf(underlying_writer)) {
150 return AutoIndentingStream(@TypeOf(underlying_writer)){
151 .underlying_writer = underlying_writer,
152 .indent_delta = indent_delta,
153 };
154}
lib/std/multi_array_list.zig created+446
......@@ -0,0 +1,446 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2021 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6const std = @import("std.zig");
7const assert = std.debug.assert;
8const meta = std.meta;
9const mem = std.mem;
10const Allocator = mem.Allocator;
11
12pub fn MultiArrayList(comptime S: type) type {
13 return struct {
14 bytes: [*]align(@alignOf(S)) u8 = undefined,
15 len: usize = 0,
16 capacity: usize = 0,
17
18 pub const Elem = S;
19
20 pub const Field = meta.FieldEnum(S);
21
22 pub const Slice = struct {
23 /// This array is indexed by the field index which can be obtained
24 /// by using @enumToInt() on the Field enum
25 ptrs: [fields.len][*]u8,
26 len: usize,
27 capacity: usize,
28
29 pub fn items(self: Slice, comptime field: Field) []FieldType(field) {
30 const byte_ptr = self.ptrs[@enumToInt(field)];
31 const F = FieldType(field);
32 const casted_ptr = @ptrCast([*]F, @alignCast(@alignOf(F), byte_ptr));
33 return casted_ptr[0..self.len];
34 }
35
36 pub fn toMultiArrayList(self: Slice) Self {
37 if (self.ptrs.len == 0) {
38 return .{};
39 }
40 const unaligned_ptr = self.ptrs[sizes.fields[0]];
41 const aligned_ptr = @alignCast(@alignOf(S), unaligned_ptr);
42 const casted_ptr = @ptrCast([*]align(@alignOf(S)) u8, aligned_ptr);
43 return .{
44 .bytes = casted_ptr,
45 .len = self.len,
46 .capacity = self.capacity,
47 };
48 }
49
50 pub fn deinit(self: *Slice, gpa: *Allocator) void {
51 var other = self.toMultiArrayList();
52 other.deinit(gpa);
53 self.* = undefined;
54 }
55 };
56
57 const Self = @This();
58
59 const fields = meta.fields(S);
60 /// `sizes.bytes` is an array of @sizeOf each S field. Sorted by alignment, descending.
61 /// `sizes.fields` is an array mapping from `sizes.bytes` array index to field index.
62 const sizes = blk: {
63 const Data = struct {
64 size: usize,
65 size_index: usize,
66 alignment: usize,
67 };
68 var data: [fields.len]Data = undefined;
69 for (fields) |field_info, i| {
70 data[i] = .{
71 .size = @sizeOf(field_info.field_type),
72 .size_index = i,
73 .alignment = field_info.alignment,
74 };
75 }
76 const Sort = struct {
77 fn lessThan(trash: *i32, lhs: Data, rhs: Data) bool {
78 return lhs.alignment >= rhs.alignment;
79 }
80 };
81 var trash: i32 = undefined; // workaround for stage1 compiler bug
82 std.sort.sort(Data, &data, &trash, Sort.lessThan);
83 var sizes_bytes: [fields.len]usize = undefined;
84 var field_indexes: [fields.len]usize = undefined;
85 for (data) |elem, i| {
86 sizes_bytes[i] = elem.size;
87 field_indexes[i] = elem.size_index;
88 }
89 break :blk .{
90 .bytes = sizes_bytes,
91 .fields = field_indexes,
92 };
93 };
94
95 /// Release all allocated memory.
96 pub fn deinit(self: *Self, gpa: *Allocator) void {
97 gpa.free(self.allocatedBytes());
98 self.* = undefined;
99 }
100
101 /// The caller owns the returned memory. Empties this MultiArrayList.
102 pub fn toOwnedSlice(self: *Self) Slice {
103 const result = self.slice();
104 self.* = .{};
105 return result;
106 }
107
108 pub fn slice(self: Self) Slice {
109 var result: Slice = .{
110 .ptrs = undefined,
111 .len = self.len,
112 .capacity = self.capacity,
113 };
114 var ptr: [*]u8 = self.bytes;
115 for (sizes.bytes) |field_size, i| {
116 result.ptrs[sizes.fields[i]] = ptr;
117 ptr += field_size * self.capacity;
118 }
119 return result;
120 }
121
122 pub fn items(self: Self, comptime field: Field) []FieldType(field) {
123 return self.slice().items(field);
124 }
125
126 /// Overwrite one array element with new data.
127 pub fn set(self: *Self, index: usize, elem: S) void {
128 const slices = self.slice();
129 inline for (fields) |field_info, i| {
130 slices.items(@intToEnum(Field, i))[index] = @field(elem, field_info.name);
131 }
132 }
133
134 /// Obtain all the data for one array element.
135 pub fn get(self: *Self, index: usize) S {
136 const slices = self.slice();
137 var result: S = undefined;
138 inline for (fields) |field_info, i| {
139 @field(elem, field_info.name) = slices.items(@intToEnum(Field, i))[index];
140 }
141 return result;
142 }
143
144 /// Extend the list by 1 element. Allocates more memory as necessary.
145 pub fn append(self: *Self, gpa: *Allocator, elem: S) !void {
146 try self.ensureCapacity(gpa, self.len + 1);
147 self.appendAssumeCapacity(elem);
148 }
149
150 /// Extend the list by 1 element, but asserting `self.capacity`
151 /// is sufficient to hold an additional item.
152 pub fn appendAssumeCapacity(self: *Self, elem: S) void {
153 assert(self.len < self.capacity);
154 self.len += 1;
155 self.set(self.len - 1, elem);
156 }
157
158 /// Adjust the list's length to `new_len`.
159 /// Does not initialize added items, if any.
160 pub fn resize(self: *Self, gpa: *Allocator, new_len: usize) !void {
161 try self.ensureCapacity(gpa, new_len);
162 self.len = new_len;
163 }
164
165 /// Attempt to reduce allocated capacity to `new_len`.
166 /// If `new_len` is greater than zero, this may fail to reduce the capacity,
167 /// but the data remains intact and the length is updated to new_len.
168 pub fn shrinkAndFree(self: *Self, gpa: *Allocator, new_len: usize) void {
169 if (new_len == 0) {
170 gpa.free(self.allocatedBytes());
171 self.* = .{};
172 return;
173 }
174 assert(new_len <= self.capacity);
175 assert(new_len <= self.len);
176
177 const other_bytes = gpa.allocAdvanced(
178 u8,
179 @alignOf(S),
180 capacityInBytes(new_len),
181 .exact,
182 ) catch {
183 const self_slice = self.slice();
184 inline for (fields) |field_info, i| {
185 const field = @intToEnum(Field, i);
186 const dest_slice = self_slice.items(field)[new_len..];
187 const byte_count = dest_slice.len * @sizeOf(field_info.field_type);
188 // We use memset here for more efficient codegen in safety-checked,
189 // valgrind-enabled builds. Otherwise the valgrind client request
190 // will be repeated for every element.
191 @memset(@ptrCast([*]u8, dest_slice.ptr), undefined, byte_count);
192 }
193 self.len = new_len;
194 return;
195 };
196 var other = Self{
197 .bytes = other_bytes.ptr,
198 .capacity = new_len,
199 .len = new_len,
200 };
201 self.len = new_len;
202 const self_slice = self.slice();
203 const other_slice = other.slice();
204 inline for (fields) |field_info, i| {
205 const field = @intToEnum(Field, i);
206 // TODO we should be able to use std.mem.copy here but it causes a
207 // test failure on aarch64 with -OReleaseFast
208 const src_slice = mem.sliceAsBytes(self_slice.items(field));
209 const dst_slice = mem.sliceAsBytes(other_slice.items(field));
210 @memcpy(dst_slice.ptr, src_slice.ptr, src_slice.len);
211 }
212 gpa.free(self.allocatedBytes());
213 self.* = other;
214 }
215
216 /// Reduce length to `new_len`.
217 /// Invalidates pointers to elements `items[new_len..]`.
218 /// Keeps capacity the same.
219 pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void {
220 self.len = new_len;
221 }
222
223 /// Modify the array so that it can hold at least `new_capacity` items.
224 /// Implements super-linear growth to achieve amortized O(1) append operations.
225 /// Invalidates pointers if additional memory is needed.
226 pub fn ensureCapacity(self: *Self, gpa: *Allocator, new_capacity: usize) !void {
227 var better_capacity = self.capacity;
228 if (better_capacity >= new_capacity) return;
229
230 while (true) {
231 better_capacity += better_capacity / 2 + 8;
232 if (better_capacity >= new_capacity) break;
233 }
234
235 return self.setCapacity(gpa, better_capacity);
236 }
237
238 /// Modify the array so that it can hold exactly `new_capacity` items.
239 /// Invalidates pointers if additional memory is needed.
240 /// `new_capacity` must be greater or equal to `len`.
241 pub fn setCapacity(self: *Self, gpa: *Allocator, new_capacity: usize) !void {
242 assert(new_capacity >= self.len);
243 const new_bytes = try gpa.allocAdvanced(
244 u8,
245 @alignOf(S),
246 capacityInBytes(new_capacity),
247 .exact,
248 );
249 if (self.len == 0) {
250 self.bytes = new_bytes.ptr;
251 self.capacity = new_capacity;
252 return;
253 }
254 var other = Self{
255 .bytes = new_bytes.ptr,
256 .capacity = new_capacity,
257 .len = self.len,
258 };
259 const self_slice = self.slice();
260 const other_slice = other.slice();
261 inline for (fields) |field_info, i| {
262 const field = @intToEnum(Field, i);
263 // TODO we should be able to use std.mem.copy here but it causes a
264 // test failure on aarch64 with -OReleaseFast
265 const src_slice = mem.sliceAsBytes(self_slice.items(field));
266 const dst_slice = mem.sliceAsBytes(other_slice.items(field));
267 @memcpy(dst_slice.ptr, src_slice.ptr, src_slice.len);
268 }
269 gpa.free(self.allocatedBytes());
270 self.* = other;
271 }
272
273 fn capacityInBytes(capacity: usize) usize {
274 const sizes_vector: std.meta.Vector(sizes.bytes.len, usize) = sizes.bytes;
275 const capacity_vector = @splat(sizes.bytes.len, capacity);
276 return @reduce(.Add, capacity_vector * sizes_vector);
277 }
278
279 fn allocatedBytes(self: Self) []align(@alignOf(S)) u8 {
280 return self.bytes[0..capacityInBytes(self.capacity)];
281 }
282
283 fn FieldType(field: Field) type {
284 return meta.fieldInfo(S, field).field_type;
285 }
286 };
287}
288
289test "basic usage" {
290 const testing = std.testing;
291 const ally = testing.allocator;
292
293 const Foo = struct {
294 a: u32,
295 b: []const u8,
296 c: u8,
297 };
298
299 var list = MultiArrayList(Foo){};
300 defer list.deinit(ally);
301
302 try list.ensureCapacity(ally, 2);
303
304 list.appendAssumeCapacity(.{
305 .a = 1,
306 .b = "foobar",
307 .c = 'a',
308 });
309
310 list.appendAssumeCapacity(.{
311 .a = 2,
312 .b = "zigzag",
313 .c = 'b',
314 });
315
316 testing.expectEqualSlices(u32, list.items(.a), &[_]u32{ 1, 2 });
317 testing.expectEqualSlices(u8, list.items(.c), &[_]u8{ 'a', 'b' });
318
319 testing.expectEqual(@as(usize, 2), list.items(.b).len);
320 testing.expectEqualStrings("foobar", list.items(.b)[0]);
321 testing.expectEqualStrings("zigzag", list.items(.b)[1]);
322
323 try list.append(ally, .{
324 .a = 3,
325 .b = "fizzbuzz",
326 .c = 'c',
327 });
328
329 testing.expectEqualSlices(u32, list.items(.a), &[_]u32{ 1, 2, 3 });
330 testing.expectEqualSlices(u8, list.items(.c), &[_]u8{ 'a', 'b', 'c' });
331
332 testing.expectEqual(@as(usize, 3), list.items(.b).len);
333 testing.expectEqualStrings("foobar", list.items(.b)[0]);
334 testing.expectEqualStrings("zigzag", list.items(.b)[1]);
335 testing.expectEqualStrings("fizzbuzz", list.items(.b)[2]);
336
337 // Add 6 more things to force a capacity increase.
338 var i: usize = 0;
339 while (i < 6) : (i += 1) {
340 try list.append(ally, .{
341 .a = @intCast(u32, 4 + i),
342 .b = "whatever",
343 .c = @intCast(u8, 'd' + i),
344 });
345 }
346
347 testing.expectEqualSlices(
348 u32,
349 &[_]u32{ 1, 2, 3, 4, 5, 6, 7, 8, 9 },
350 list.items(.a),
351 );
352 testing.expectEqualSlices(
353 u8,
354 &[_]u8{ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i' },
355 list.items(.c),
356 );
357
358 list.shrinkAndFree(ally, 3);
359
360 testing.expectEqualSlices(u32, list.items(.a), &[_]u32{ 1, 2, 3 });
361 testing.expectEqualSlices(u8, list.items(.c), &[_]u8{ 'a', 'b', 'c' });
362
363 testing.expectEqual(@as(usize, 3), list.items(.b).len);
364 testing.expectEqualStrings("foobar", list.items(.b)[0]);
365 testing.expectEqualStrings("zigzag", list.items(.b)[1]);
366 testing.expectEqualStrings("fizzbuzz", list.items(.b)[2]);
367}
368
369// This was observed to fail on aarch64 with LLVM 11, when the capacityInBytes
370// function used the @reduce code path.
371test "regression test for @reduce bug" {
372 const ally = std.testing.allocator;
373 var list = MultiArrayList(struct {
374 tag: std.zig.Token.Tag,
375 start: u32,
376 }){};
377 defer list.deinit(ally);
378
379 try list.ensureCapacity(ally, 20);
380
381 try list.append(ally, .{ .tag = .keyword_const, .start = 0 });
382 try list.append(ally, .{ .tag = .identifier, .start = 6 });
383 try list.append(ally, .{ .tag = .equal, .start = 10 });
384 try list.append(ally, .{ .tag = .builtin, .start = 12 });
385 try list.append(ally, .{ .tag = .l_paren, .start = 19 });
386 try list.append(ally, .{ .tag = .string_literal, .start = 20 });
387 try list.append(ally, .{ .tag = .r_paren, .start = 25 });
388 try list.append(ally, .{ .tag = .semicolon, .start = 26 });
389 try list.append(ally, .{ .tag = .keyword_pub, .start = 29 });
390 try list.append(ally, .{ .tag = .keyword_fn, .start = 33 });
391 try list.append(ally, .{ .tag = .identifier, .start = 36 });
392 try list.append(ally, .{ .tag = .l_paren, .start = 40 });
393 try list.append(ally, .{ .tag = .r_paren, .start = 41 });
394 try list.append(ally, .{ .tag = .identifier, .start = 43 });
395 try list.append(ally, .{ .tag = .bang, .start = 51 });
396 try list.append(ally, .{ .tag = .identifier, .start = 52 });
397 try list.append(ally, .{ .tag = .l_brace, .start = 57 });
398 try list.append(ally, .{ .tag = .identifier, .start = 63 });
399 try list.append(ally, .{ .tag = .period, .start = 66 });
400 try list.append(ally, .{ .tag = .identifier, .start = 67 });
401 try list.append(ally, .{ .tag = .period, .start = 70 });
402 try list.append(ally, .{ .tag = .identifier, .start = 71 });
403 try list.append(ally, .{ .tag = .l_paren, .start = 75 });
404 try list.append(ally, .{ .tag = .string_literal, .start = 76 });
405 try list.append(ally, .{ .tag = .comma, .start = 113 });
406 try list.append(ally, .{ .tag = .period, .start = 115 });
407 try list.append(ally, .{ .tag = .l_brace, .start = 116 });
408 try list.append(ally, .{ .tag = .r_brace, .start = 117 });
409 try list.append(ally, .{ .tag = .r_paren, .start = 118 });
410 try list.append(ally, .{ .tag = .semicolon, .start = 119 });
411 try list.append(ally, .{ .tag = .r_brace, .start = 121 });
412 try list.append(ally, .{ .tag = .eof, .start = 123 });
413
414 const tags = list.items(.tag);
415 std.testing.expectEqual(tags[1], .identifier);
416 std.testing.expectEqual(tags[2], .equal);
417 std.testing.expectEqual(tags[3], .builtin);
418 std.testing.expectEqual(tags[4], .l_paren);
419 std.testing.expectEqual(tags[5], .string_literal);
420 std.testing.expectEqual(tags[6], .r_paren);
421 std.testing.expectEqual(tags[7], .semicolon);
422 std.testing.expectEqual(tags[8], .keyword_pub);
423 std.testing.expectEqual(tags[9], .keyword_fn);
424 std.testing.expectEqual(tags[10], .identifier);
425 std.testing.expectEqual(tags[11], .l_paren);
426 std.testing.expectEqual(tags[12], .r_paren);
427 std.testing.expectEqual(tags[13], .identifier);
428 std.testing.expectEqual(tags[14], .bang);
429 std.testing.expectEqual(tags[15], .identifier);
430 std.testing.expectEqual(tags[16], .l_brace);
431 std.testing.expectEqual(tags[17], .identifier);
432 std.testing.expectEqual(tags[18], .period);
433 std.testing.expectEqual(tags[19], .identifier);
434 std.testing.expectEqual(tags[20], .period);
435 std.testing.expectEqual(tags[21], .identifier);
436 std.testing.expectEqual(tags[22], .l_paren);
437 std.testing.expectEqual(tags[23], .string_literal);
438 std.testing.expectEqual(tags[24], .comma);
439 std.testing.expectEqual(tags[25], .period);
440 std.testing.expectEqual(tags[26], .l_brace);
441 std.testing.expectEqual(tags[27], .r_brace);
442 std.testing.expectEqual(tags[28], .r_paren);
443 std.testing.expectEqual(tags[29], .semicolon);
444 std.testing.expectEqual(tags[30], .r_brace);
445 std.testing.expectEqual(tags[31], .eof);
446}
lib/std/std.zig+1
......@@ -20,6 +20,7 @@ pub const ComptimeStringMap = @import("comptime_string_map.zig").ComptimeStringM
2020pub const DynLib = @import("dynamic_library.zig").DynLib;
2121pub const HashMap = hash_map.HashMap;
2222pub const HashMapUnmanaged = hash_map.HashMapUnmanaged;
23pub const MultiArrayList = @import("multi_array_list.zig").MultiArrayList;
2324pub const PackedIntArray = @import("packed_int_array.zig").PackedIntArray;
2425pub const PackedIntArrayEndian = @import("packed_int_array.zig").PackedIntArrayEndian;
2526pub const PackedIntSlice = @import("packed_int_array.zig").PackedIntSlice;
lib/std/zig.zig-1
......@@ -12,7 +12,6 @@ pub const fmtId = @import("zig/fmt.zig").fmtId;
1212pub const fmtEscapes = @import("zig/fmt.zig").fmtEscapes;
1313pub const parse = @import("zig/parse.zig").parse;
1414pub const parseStringLiteral = @import("zig/string_literal.zig").parse;
15pub const render = @import("zig/render.zig").render;
1615pub const ast = @import("zig/ast.zig");
1716pub const system = @import("zig/system.zig");
1817pub const CrossTarget = @import("zig/cross_target.zig").CrossTarget;
lib/std/zig/ast.zig+2754-3074
......@@ -9,71 +9,78 @@ const testing = std.testing;
99const mem = std.mem;
1010const Token = std.zig.Token;
1111
12pub const TokenIndex = usize;
13pub const NodeIndex = usize;
12pub const TokenIndex = u32;
13pub const ByteOffset = u32;
14
15pub const TokenList = std.MultiArrayList(struct {
16 tag: Token.Tag,
17 start: ByteOffset,
18});
19pub const NodeList = std.MultiArrayList(Node);
1420
1521pub const Tree = struct {
1622 /// Reference to externally-owned data.
1723 source: []const u8,
18 token_ids: []const Token.Id,
19 token_locs: []const Token.Loc,
20 errors: []const Error,
21 root_node: *Node.Root,
2224
23 arena: std.heap.ArenaAllocator.State,
24 gpa: *mem.Allocator,
25 tokens: TokenList.Slice,
26 /// The root AST node is assumed to be index 0. Since there can be no
27 /// references to the root node, this means 0 is available to indicate null.
28 nodes: NodeList.Slice,
29 extra_data: []Node.Index,
2530
26 /// translate-c uses this to avoid having to emit correct newlines
27 /// TODO get rid of this hack
28 generated: bool = false,
31 errors: []const Error,
2932
30 pub fn deinit(self: *Tree) void {
31 self.gpa.free(self.token_ids);
32 self.gpa.free(self.token_locs);
33 self.gpa.free(self.errors);
34 self.arena.promote(self.gpa).deinit();
35 }
33 pub const Location = struct {
34 line: usize,
35 column: usize,
36 line_start: usize,
37 line_end: usize,
38 };
3639
37 pub fn renderError(self: *Tree, parse_error: *const Error, stream: anytype) !void {
38 return parse_error.render(self.token_ids, stream);
40 pub fn deinit(tree: *Tree, gpa: *mem.Allocator) void {
41 tree.tokens.deinit(gpa);
42 tree.nodes.deinit(gpa);
43 gpa.free(tree.extra_data);
44 gpa.free(tree.errors);
45 tree.* = undefined;
3946 }
4047
41 pub fn tokenSlice(self: *Tree, token_index: TokenIndex) []const u8 {
42 return self.tokenSliceLoc(self.token_locs[token_index]);
43 }
48 pub const RenderError = error{
49 /// Ran out of memory allocating call stack frames to complete rendering, or
50 /// ran out of memory allocating space in the output buffer.
51 OutOfMemory,
52 };
4453
45 pub fn tokenSliceLoc(self: *Tree, token: Token.Loc) []const u8 {
46 return self.source[token.start..token.end];
47 }
54 /// `gpa` is used for allocating the resulting formatted source code, as well as
55 /// for allocating extra stack memory if needed, because this function utilizes recursion.
56 /// Note: that's not actually true yet, see https://github.com/ziglang/zig/issues/1006.
57 /// Caller owns the returned slice of bytes, allocated with `gpa`.
58 pub fn render(tree: Tree, gpa: *mem.Allocator) RenderError![]u8 {
59 var buffer = std.ArrayList(u8).init(gpa);
60 defer buffer.deinit();
4861
49 pub fn getNodeSource(self: *const Tree, node: *const Node) []const u8 {
50 const first_token = self.token_locs[node.firstToken()];
51 const last_token = self.token_locs[node.lastToken()];
52 return self.source[first_token.start..last_token.end];
62 try tree.renderToArrayList(&buffer);
63 return buffer.toOwnedSlice();
5364 }
5465
55 pub const Location = struct {
56 line: usize,
57 column: usize,
58 line_start: usize,
59 line_end: usize,
60 };
66 pub fn renderToArrayList(tree: Tree, buffer: *std.ArrayList(u8)) RenderError!void {
67 return @import("./render.zig").renderTree(buffer, tree);
68 }
6169
62 /// Return the Location of the token relative to the offset specified by `start_index`.
63 pub fn tokenLocationLoc(self: *Tree, start_index: usize, token: Token.Loc) Location {
70 pub fn tokenLocation(self: Tree, start_offset: ByteOffset, token_index: TokenIndex) Location {
6471 var loc = Location{
6572 .line = 0,
6673 .column = 0,
67 .line_start = start_index,
74 .line_start = start_offset,
6875 .line_end = self.source.len,
6976 };
70 if (self.generated)
71 return loc;
72 const token_start = token.start;
73 for (self.source[start_index..]) |c, i| {
74 if (i + start_index == token_start) {
75 loc.line_end = i + start_index;
76 while (loc.line_end < self.source.len and self.source[loc.line_end] != '\n') : (loc.line_end += 1) {}
77 const token_start = self.tokens.items(.start)[token_index];
78 for (self.source[start_offset..]) |c, i| {
79 if (i + start_offset == token_start) {
80 loc.line_end = i + start_offset;
81 while (loc.line_end < self.source.len and self.source[loc.line_end] != '\n') {
82 loc.line_end += 1;
83 }
7784 return loc;
7885 }
7986 if (c == '\n') {
......@@ -87,3205 +94,2878 @@ pub const Tree = struct {
8794 return loc;
8895 }
8996
90 pub fn tokenLocation(self: *Tree, start_index: usize, token_index: TokenIndex) Location {
91 return self.tokenLocationLoc(start_index, self.token_locs[token_index]);
92 }
97 pub fn tokenSlice(tree: Tree, token_index: TokenIndex) []const u8 {
98 const token_starts = tree.tokens.items(.start);
99 const token_tags = tree.tokens.items(.tag);
100 const token_tag = token_tags[token_index];
101
102 // Many tokens can be determined entirely by their tag.
103 if (token_tag.lexeme()) |lexeme| {
104 return lexeme;
105 }
93106
94 pub fn tokensOnSameLine(self: *Tree, token1_index: TokenIndex, token2_index: TokenIndex) bool {
95 return self.tokensOnSameLineLoc(self.token_locs[token1_index], self.token_locs[token2_index]);
107 // For some tokens, re-tokenization is needed to find the end.
108 var tokenizer: std.zig.Tokenizer = .{
109 .buffer = tree.source,
110 .index = token_starts[token_index],
111 .pending_invalid_token = null,
112 };
113 const token = tokenizer.next();
114 assert(token.tag == token_tag);
115 return tree.source[token.loc.start..token.loc.end];
96116 }
97117
98 pub fn tokensOnSameLineLoc(self: *Tree, token1: Token.Loc, token2: Token.Loc) bool {
99 return mem.indexOfScalar(u8, self.source[token1.end..token2.start], '\n') == null;
118 pub fn extraData(tree: Tree, index: usize, comptime T: type) T {
119 const fields = std.meta.fields(T);
120 var result: T = undefined;
121 inline for (fields) |field, i| {
122 comptime assert(field.field_type == Node.Index);
123 @field(result, field.name) = tree.extra_data[index + i];
124 }
125 return result;
100126 }
101127
102 pub fn dump(self: *Tree) void {
103 self.root_node.base.dump(0);
128 pub fn rootDecls(tree: Tree) []const Node.Index {
129 // Root is always index 0.
130 const nodes_data = tree.nodes.items(.data);
131 return tree.extra_data[nodes_data[0].lhs..nodes_data[0].rhs];
104132 }
105133
106 /// Skips over comments
107 pub fn prevToken(self: *Tree, token_index: TokenIndex) TokenIndex {
108 var index = token_index - 1;
109 while (self.token_ids[index] == Token.Id.LineComment) {
110 index -= 1;
134 pub fn renderError(tree: Tree, parse_error: Error, stream: anytype) !void {
135 const token_tags = tree.tokens.items(.tag);
136 switch (parse_error.tag) {
137 .asterisk_after_ptr_deref => {
138 return stream.writeAll("'.*' cannot be followed by '*'. Are you missing a space?");
139 },
140 .decl_between_fields => {
141 return stream.writeAll("declarations are not allowed between container fields");
142 },
143 .expected_block => {
144 return stream.print("expected block or field, found '{s}'", .{
145 token_tags[parse_error.token].symbol(),
146 });
147 },
148 .expected_block_or_assignment => {
149 return stream.print("expected block or assignment, found '{s}'", .{
150 token_tags[parse_error.token].symbol(),
151 });
152 },
153 .expected_block_or_expr => {
154 return stream.print("expected block or expression, found '{s}'", .{
155 token_tags[parse_error.token].symbol(),
156 });
157 },
158 .expected_block_or_field => {
159 return stream.print("expected block or field, found '{s}'", .{
160 token_tags[parse_error.token].symbol(),
161 });
162 },
163 .expected_container_members => {
164 return stream.print("expected test, comptime, var decl, or container field, found '{s}'", .{
165 token_tags[parse_error.token].symbol(),
166 });
167 },
168 .expected_expr => {
169 return stream.print("expected expression, found '{s}'", .{
170 token_tags[parse_error.token].symbol(),
171 });
172 },
173 .expected_expr_or_assignment => {
174 return stream.print("expected expression or assignment, found '{s}'", .{
175 token_tags[parse_error.token].symbol(),
176 });
177 },
178 .expected_fn => {
179 return stream.print("expected function, found '{s}'", .{
180 token_tags[parse_error.token].symbol(),
181 });
182 },
183 .expected_inlinable => {
184 return stream.print("expected 'while' or 'for', found '{s}'", .{
185 token_tags[parse_error.token].symbol(),
186 });
187 },
188 .expected_labelable => {
189 return stream.print("expected 'while', 'for', 'inline', 'suspend', or '{{', found '{s}'", .{
190 token_tags[parse_error.token].symbol(),
191 });
192 },
193 .expected_param_list => {
194 return stream.print("expected parameter list, found '{s}'", .{
195 token_tags[parse_error.token].symbol(),
196 });
197 },
198 .expected_prefix_expr => {
199 return stream.print("expected prefix expression, found '{s}'", .{
200 token_tags[parse_error.token].symbol(),
201 });
202 },
203 .expected_primary_type_expr => {
204 return stream.print("expected primary type expression, found '{s}'", .{
205 token_tags[parse_error.token].symbol(),
206 });
207 },
208 .expected_pub_item => {
209 return stream.writeAll("expected function or variable declaration after pub");
210 },
211 .expected_return_type => {
212 return stream.print("expected return type expression, found '{s}'", .{
213 token_tags[parse_error.token].symbol(),
214 });
215 },
216 .expected_semi_or_else => {
217 return stream.print("expected ';' or 'else', found '{s}'", .{
218 token_tags[parse_error.token].symbol(),
219 });
220 },
221 .expected_semi_or_lbrace => {
222 return stream.print("expected ';' or '{{', found '{s}'", .{
223 token_tags[parse_error.token].symbol(),
224 });
225 },
226 .expected_statement => {
227 return stream.print("expected statement, found '{s}'", .{
228 token_tags[parse_error.token].symbol(),
229 });
230 },
231 .expected_string_literal => {
232 return stream.print("expected string literal, found '{s}'", .{
233 token_tags[parse_error.token].symbol(),
234 });
235 },
236 .expected_suffix_op => {
237 return stream.print("expected pointer dereference, optional unwrap, or field access, found '{s}'", .{
238 token_tags[parse_error.token].symbol(),
239 });
240 },
241 .expected_type_expr => {
242 return stream.print("expected type expression, found '{s}'", .{
243 token_tags[parse_error.token].symbol(),
244 });
245 },
246 .expected_var_decl => {
247 return stream.print("expected variable declaration, found '{s}'", .{
248 token_tags[parse_error.token].symbol(),
249 });
250 },
251 .expected_var_decl_or_fn => {
252 return stream.print("expected variable declaration or function, found '{s}'", .{
253 token_tags[parse_error.token].symbol(),
254 });
255 },
256 .expected_loop_payload => {
257 return stream.print("expected loop payload, found '{s}'", .{
258 token_tags[parse_error.token].symbol(),
259 });
260 },
261 .expected_container => {
262 return stream.print("expected a struct, enum or union, found '{s}'", .{
263 token_tags[parse_error.token].symbol(),
264 });
265 },
266 .extra_align_qualifier => {
267 return stream.writeAll("extra align qualifier");
268 },
269 .extra_allowzero_qualifier => {
270 return stream.writeAll("extra allowzero qualifier");
271 },
272 .extra_const_qualifier => {
273 return stream.writeAll("extra const qualifier");
274 },
275 .extra_volatile_qualifier => {
276 return stream.writeAll("extra volatile qualifier");
277 },
278 .invalid_align => {
279 return stream.writeAll("alignment not allowed on arrays");
280 },
281 .invalid_and => {
282 return stream.writeAll("`&&` is invalid; note that `and` is boolean AND");
283 },
284 .invalid_bit_range => {
285 return stream.writeAll("bit range not allowed on slices and arrays");
286 },
287 .invalid_token => {
288 return stream.print("invalid token '{s}'", .{
289 token_tags[parse_error.token].symbol(),
290 });
291 },
292 .same_line_doc_comment => {
293 return stream.writeAll("same line documentation comment");
294 },
295 .unattached_doc_comment => {
296 return stream.writeAll("unattached documentation comment");
297 },
298
299 .expected_token => {
300 const found_tag = token_tags[parse_error.token];
301 const expected_symbol = parse_error.extra.expected_tag.symbol();
302 switch (found_tag) {
303 .invalid => return stream.print("expected '{s}', found invalid bytes", .{
304 expected_symbol,
305 }),
306 else => return stream.print("expected '{s}', found '{s}'", .{
307 expected_symbol, found_tag.symbol(),
308 }),
309 }
310 },
111311 }
112 return index;
113312 }
114313
115 /// Skips over comments
116 pub fn nextToken(self: *Tree, token_index: TokenIndex) TokenIndex {
117 var index = token_index + 1;
118 while (self.token_ids[index] == Token.Id.LineComment) {
119 index += 1;
120 }
121 return index;
314 pub fn firstToken(tree: Tree, node: Node.Index) TokenIndex {
315 const tags = tree.nodes.items(.tag);
316 const datas = tree.nodes.items(.data);
317 const main_tokens = tree.nodes.items(.main_token);
318 const token_tags = tree.tokens.items(.tag);
319 var end_offset: TokenIndex = 0;
320 var n = node;
321 while (true) switch (tags[n]) {
322 .root => return 0,
323
324 .test_decl,
325 .@"errdefer",
326 .@"defer",
327 .bool_not,
328 .negation,
329 .bit_not,
330 .negation_wrap,
331 .address_of,
332 .@"try",
333 .@"await",
334 .optional_type,
335 .@"switch",
336 .switch_comma,
337 .if_simple,
338 .@"if",
339 .@"suspend",
340 .@"resume",
341 .@"continue",
342 .@"break",
343 .@"return",
344 .anyframe_type,
345 .identifier,
346 .anyframe_literal,
347 .char_literal,
348 .integer_literal,
349 .float_literal,
350 .false_literal,
351 .true_literal,
352 .null_literal,
353 .undefined_literal,
354 .unreachable_literal,
355 .string_literal,
356 .multiline_string_literal,
357 .grouped_expression,
358 .builtin_call_two,
359 .builtin_call_two_comma,
360 .builtin_call,
361 .builtin_call_comma,
362 .error_set_decl,
363 .@"anytype",
364 .@"comptime",
365 .@"nosuspend",
366 .asm_simple,
367 .@"asm",
368 .array_type,
369 .array_type_sentinel,
370 .error_value,
371 => return main_tokens[n] - end_offset,
372
373 .array_init_dot,
374 .array_init_dot_comma,
375 .array_init_dot_two,
376 .array_init_dot_two_comma,
377 .struct_init_dot,
378 .struct_init_dot_comma,
379 .struct_init_dot_two,
380 .struct_init_dot_two_comma,
381 .enum_literal,
382 => return main_tokens[n] - 1 - end_offset,
383
384 .@"catch",
385 .field_access,
386 .unwrap_optional,
387 .equal_equal,
388 .bang_equal,
389 .less_than,
390 .greater_than,
391 .less_or_equal,
392 .greater_or_equal,
393 .assign_mul,
394 .assign_div,
395 .assign_mod,
396 .assign_add,
397 .assign_sub,
398 .assign_bit_shift_left,
399 .assign_bit_shift_right,
400 .assign_bit_and,
401 .assign_bit_xor,
402 .assign_bit_or,
403 .assign_mul_wrap,
404 .assign_add_wrap,
405 .assign_sub_wrap,
406 .assign,
407 .merge_error_sets,
408 .mul,
409 .div,
410 .mod,
411 .array_mult,
412 .mul_wrap,
413 .add,
414 .sub,
415 .array_cat,
416 .add_wrap,
417 .sub_wrap,
418 .bit_shift_left,
419 .bit_shift_right,
420 .bit_and,
421 .bit_xor,
422 .bit_or,
423 .@"orelse",
424 .bool_and,
425 .bool_or,
426 .slice_open,
427 .slice,
428 .slice_sentinel,
429 .deref,
430 .array_access,
431 .array_init_one,
432 .array_init_one_comma,
433 .array_init,
434 .array_init_comma,
435 .struct_init_one,
436 .struct_init_one_comma,
437 .struct_init,
438 .struct_init_comma,
439 .call_one,
440 .call_one_comma,
441 .call,
442 .call_comma,
443 .switch_range,
444 .error_union,
445 => n = datas[n].lhs,
446
447 .fn_decl,
448 .fn_proto_simple,
449 .fn_proto_multi,
450 .fn_proto_one,
451 .fn_proto,
452 => {
453 var i = main_tokens[n]; // fn token
454 while (i > 0) {
455 i -= 1;
456 switch (token_tags[i]) {
457 .keyword_extern,
458 .keyword_export,
459 .keyword_pub,
460 .keyword_threadlocal,
461 .string_literal,
462 => continue,
463
464 else => return i + 1 - end_offset,
465 }
466 }
467 return i - end_offset;
468 },
469
470 .@"usingnamespace" => {
471 const main_token = main_tokens[n];
472 if (main_token > 0 and token_tags[main_token - 1] == .keyword_pub) {
473 end_offset += 1;
474 }
475 return main_token - end_offset;
476 },
477
478 .async_call_one,
479 .async_call_one_comma,
480 .async_call,
481 .async_call_comma,
482 => {
483 end_offset += 1; // async token
484 n = datas[n].lhs;
485 },
486
487 .container_field_init,
488 .container_field_align,
489 .container_field,
490 => {
491 const name_token = main_tokens[n];
492 if (name_token > 0 and token_tags[name_token - 1] == .keyword_comptime) {
493 end_offset += 1;
494 }
495 return name_token - end_offset;
496 },
497
498 .global_var_decl,
499 .local_var_decl,
500 .simple_var_decl,
501 .aligned_var_decl,
502 => {
503 var i = main_tokens[n]; // mut token
504 while (i > 0) {
505 i -= 1;
506 switch (token_tags[i]) {
507 .keyword_extern,
508 .keyword_export,
509 .keyword_comptime,
510 .keyword_pub,
511 .keyword_threadlocal,
512 .string_literal,
513 => continue,
514
515 else => return i + 1 - end_offset,
516 }
517 }
518 return i - end_offset;
519 },
520
521 .block,
522 .block_semicolon,
523 .block_two,
524 .block_two_semicolon,
525 => {
526 // Look for a label.
527 const lbrace = main_tokens[n];
528 if (token_tags[lbrace - 1] == .colon) {
529 end_offset += 2;
530 }
531 return lbrace - end_offset;
532 },
533
534 .container_decl,
535 .container_decl_trailing,
536 .container_decl_two,
537 .container_decl_two_trailing,
538 .container_decl_arg,
539 .container_decl_arg_trailing,
540 .tagged_union,
541 .tagged_union_trailing,
542 .tagged_union_two,
543 .tagged_union_two_trailing,
544 .tagged_union_enum_tag,
545 .tagged_union_enum_tag_trailing,
546 => {
547 const main_token = main_tokens[n];
548 switch (token_tags[main_token - 1]) {
549 .keyword_packed, .keyword_extern => end_offset += 1,
550 else => {},
551 }
552 return main_token - end_offset;
553 },
554
555 .ptr_type_aligned,
556 .ptr_type_sentinel,
557 .ptr_type,
558 .ptr_type_bit_range,
559 => {
560 const main_token = main_tokens[n];
561 return switch (token_tags[main_token]) {
562 .asterisk,
563 .asterisk_asterisk,
564 => switch (token_tags[main_token - 1]) {
565 .l_bracket => main_token - 1,
566 else => main_token,
567 },
568 .l_bracket => main_token,
569 else => unreachable,
570 } - end_offset;
571 },
572
573 .switch_case_one => {
574 if (datas[n].lhs == 0) {
575 return main_tokens[n] - 1 - end_offset; // else token
576 } else {
577 n = datas[n].lhs;
578 }
579 },
580 .switch_case => {
581 const extra = tree.extraData(datas[n].lhs, Node.SubRange);
582 assert(extra.end - extra.start > 0);
583 n = tree.extra_data[extra.start];
584 },
585
586 .asm_output, .asm_input => {
587 assert(token_tags[main_tokens[n] - 1] == .l_bracket);
588 return main_tokens[n] - 1 - end_offset;
589 },
590
591 .while_simple,
592 .while_cont,
593 .@"while",
594 .for_simple,
595 .@"for",
596 => {
597 // Look for a label and inline.
598 const main_token = main_tokens[n];
599 var result = main_token;
600 if (token_tags[result - 1] == .keyword_inline) {
601 result -= 1;
602 }
603 if (token_tags[result - 1] == .colon) {
604 result -= 2;
605 }
606 return result - end_offset;
607 },
608 };
122609 }
123};
124610
125pub const Error = union(enum) {
126 InvalidToken: InvalidToken,
127 ExpectedContainerMembers: ExpectedContainerMembers,
128 ExpectedStringLiteral: ExpectedStringLiteral,
129 ExpectedIntegerLiteral: ExpectedIntegerLiteral,
130 ExpectedPubItem: ExpectedPubItem,
131 ExpectedIdentifier: ExpectedIdentifier,
132 ExpectedStatement: ExpectedStatement,
133 ExpectedVarDeclOrFn: ExpectedVarDeclOrFn,
134 ExpectedVarDecl: ExpectedVarDecl,
135 ExpectedFn: ExpectedFn,
136 ExpectedReturnType: ExpectedReturnType,
137 ExpectedAggregateKw: ExpectedAggregateKw,
138 UnattachedDocComment: UnattachedDocComment,
139 ExpectedEqOrSemi: ExpectedEqOrSemi,
140 ExpectedSemiOrLBrace: ExpectedSemiOrLBrace,
141 ExpectedSemiOrElse: ExpectedSemiOrElse,
142 ExpectedLabelOrLBrace: ExpectedLabelOrLBrace,
143 ExpectedLBrace: ExpectedLBrace,
144 ExpectedColonOrRParen: ExpectedColonOrRParen,
145 ExpectedLabelable: ExpectedLabelable,
146 ExpectedInlinable: ExpectedInlinable,
147 ExpectedAsmOutputReturnOrType: ExpectedAsmOutputReturnOrType,
148 ExpectedCall: ExpectedCall,
149 ExpectedCallOrFnProto: ExpectedCallOrFnProto,
150 ExpectedSliceOrRBracket: ExpectedSliceOrRBracket,
151 ExtraAlignQualifier: ExtraAlignQualifier,
152 ExtraConstQualifier: ExtraConstQualifier,
153 ExtraVolatileQualifier: ExtraVolatileQualifier,
154 ExtraAllowZeroQualifier: ExtraAllowZeroQualifier,
155 ExpectedTypeExpr: ExpectedTypeExpr,
156 ExpectedPrimaryTypeExpr: ExpectedPrimaryTypeExpr,
157 ExpectedParamType: ExpectedParamType,
158 ExpectedExpr: ExpectedExpr,
159 ExpectedPrimaryExpr: ExpectedPrimaryExpr,
160 ExpectedToken: ExpectedToken,
161 ExpectedCommaOrEnd: ExpectedCommaOrEnd,
162 ExpectedParamList: ExpectedParamList,
163 ExpectedPayload: ExpectedPayload,
164 ExpectedBlockOrAssignment: ExpectedBlockOrAssignment,
165 ExpectedBlockOrExpression: ExpectedBlockOrExpression,
166 ExpectedExprOrAssignment: ExpectedExprOrAssignment,
167 ExpectedPrefixExpr: ExpectedPrefixExpr,
168 ExpectedLoopExpr: ExpectedLoopExpr,
169 ExpectedDerefOrUnwrap: ExpectedDerefOrUnwrap,
170 ExpectedSuffixOp: ExpectedSuffixOp,
171 ExpectedBlockOrField: ExpectedBlockOrField,
172 DeclBetweenFields: DeclBetweenFields,
173 InvalidAnd: InvalidAnd,
174 AsteriskAfterPointerDereference: AsteriskAfterPointerDereference,
175
176 pub fn render(self: *const Error, tokens: []const Token.Id, stream: anytype) !void {
177 switch (self.*) {
178 .InvalidToken => |*x| return x.render(tokens, stream),
179 .ExpectedContainerMembers => |*x| return x.render(tokens, stream),
180 .ExpectedStringLiteral => |*x| return x.render(tokens, stream),
181 .ExpectedIntegerLiteral => |*x| return x.render(tokens, stream),
182 .ExpectedPubItem => |*x| return x.render(tokens, stream),
183 .ExpectedIdentifier => |*x| return x.render(tokens, stream),
184 .ExpectedStatement => |*x| return x.render(tokens, stream),
185 .ExpectedVarDeclOrFn => |*x| return x.render(tokens, stream),
186 .ExpectedVarDecl => |*x| return x.render(tokens, stream),
187 .ExpectedFn => |*x| return x.render(tokens, stream),
188 .ExpectedReturnType => |*x| return x.render(tokens, stream),
189 .ExpectedAggregateKw => |*x| return x.render(tokens, stream),
190 .UnattachedDocComment => |*x| return x.render(tokens, stream),
191 .ExpectedEqOrSemi => |*x| return x.render(tokens, stream),
192 .ExpectedSemiOrLBrace => |*x| return x.render(tokens, stream),
193 .ExpectedSemiOrElse => |*x| return x.render(tokens, stream),
194 .ExpectedLabelOrLBrace => |*x| return x.render(tokens, stream),
195 .ExpectedLBrace => |*x| return x.render(tokens, stream),
196 .ExpectedColonOrRParen => |*x| return x.render(tokens, stream),
197 .ExpectedLabelable => |*x| return x.render(tokens, stream),
198 .ExpectedInlinable => |*x| return x.render(tokens, stream),
199 .ExpectedAsmOutputReturnOrType => |*x| return x.render(tokens, stream),
200 .ExpectedCall => |*x| return x.render(tokens, stream),
201 .ExpectedCallOrFnProto => |*x| return x.render(tokens, stream),
202 .ExpectedSliceOrRBracket => |*x| return x.render(tokens, stream),
203 .ExtraAlignQualifier => |*x| return x.render(tokens, stream),
204 .ExtraConstQualifier => |*x| return x.render(tokens, stream),
205 .ExtraVolatileQualifier => |*x| return x.render(tokens, stream),
206 .ExtraAllowZeroQualifier => |*x| return x.render(tokens, stream),
207 .ExpectedTypeExpr => |*x| return x.render(tokens, stream),
208 .ExpectedPrimaryTypeExpr => |*x| return x.render(tokens, stream),
209 .ExpectedParamType => |*x| return x.render(tokens, stream),
210 .ExpectedExpr => |*x| return x.render(tokens, stream),
211 .ExpectedPrimaryExpr => |*x| return x.render(tokens, stream),
212 .ExpectedToken => |*x| return x.render(tokens, stream),
213 .ExpectedCommaOrEnd => |*x| return x.render(tokens, stream),
214 .ExpectedParamList => |*x| return x.render(tokens, stream),
215 .ExpectedPayload => |*x| return x.render(tokens, stream),
216 .ExpectedBlockOrAssignment => |*x| return x.render(tokens, stream),
217 .ExpectedBlockOrExpression => |*x| return x.render(tokens, stream),
218 .ExpectedExprOrAssignment => |*x| return x.render(tokens, stream),
219 .ExpectedPrefixExpr => |*x| return x.render(tokens, stream),
220 .ExpectedLoopExpr => |*x| return x.render(tokens, stream),
221 .ExpectedDerefOrUnwrap => |*x| return x.render(tokens, stream),
222 .ExpectedSuffixOp => |*x| return x.render(tokens, stream),
223 .ExpectedBlockOrField => |*x| return x.render(tokens, stream),
224 .DeclBetweenFields => |*x| return x.render(tokens, stream),
225 .InvalidAnd => |*x| return x.render(tokens, stream),
226 .AsteriskAfterPointerDereference => |*x| return x.render(tokens, stream),
227 }
611 pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {
612 const tags = tree.nodes.items(.tag);
613 const datas = tree.nodes.items(.data);
614 const main_tokens = tree.nodes.items(.main_token);
615 const token_starts = tree.tokens.items(.start);
616 const token_tags = tree.tokens.items(.tag);
617 var n = node;
618 var end_offset: TokenIndex = 0;
619 while (true) switch (tags[n]) {
620 .root => return @intCast(TokenIndex, tree.tokens.len - 1),
621
622 .@"usingnamespace",
623 .bool_not,
624 .negation,
625 .bit_not,
626 .negation_wrap,
627 .address_of,
628 .@"try",
629 .@"await",
630 .optional_type,
631 .@"resume",
632 .@"nosuspend",
633 .@"comptime",
634 => n = datas[n].lhs,
635
636 .test_decl,
637 .@"errdefer",
638 .@"defer",
639 .@"catch",
640 .equal_equal,
641 .bang_equal,
642 .less_than,
643 .greater_than,
644 .less_or_equal,
645 .greater_or_equal,
646 .assign_mul,
647 .assign_div,
648 .assign_mod,
649 .assign_add,
650 .assign_sub,
651 .assign_bit_shift_left,
652 .assign_bit_shift_right,
653 .assign_bit_and,
654 .assign_bit_xor,
655 .assign_bit_or,
656 .assign_mul_wrap,
657 .assign_add_wrap,
658 .assign_sub_wrap,
659 .assign,
660 .merge_error_sets,
661 .mul,
662 .div,
663 .mod,
664 .array_mult,
665 .mul_wrap,
666 .add,
667 .sub,
668 .array_cat,
669 .add_wrap,
670 .sub_wrap,
671 .bit_shift_left,
672 .bit_shift_right,
673 .bit_and,
674 .bit_xor,
675 .bit_or,
676 .@"orelse",
677 .bool_and,
678 .bool_or,
679 .anyframe_type,
680 .error_union,
681 .if_simple,
682 .while_simple,
683 .for_simple,
684 .fn_proto_simple,
685 .fn_proto_multi,
686 .ptr_type_aligned,
687 .ptr_type_sentinel,
688 .ptr_type,
689 .ptr_type_bit_range,
690 .array_type,
691 .switch_case_one,
692 .switch_case,
693 .switch_range,
694 => n = datas[n].rhs,
695
696 .field_access,
697 .unwrap_optional,
698 .grouped_expression,
699 .multiline_string_literal,
700 .error_set_decl,
701 .asm_simple,
702 .asm_output,
703 .asm_input,
704 .error_value,
705 => return datas[n].rhs + end_offset,
706
707 .@"anytype",
708 .anyframe_literal,
709 .char_literal,
710 .integer_literal,
711 .float_literal,
712 .false_literal,
713 .true_literal,
714 .null_literal,
715 .undefined_literal,
716 .unreachable_literal,
717 .identifier,
718 .deref,
719 .enum_literal,
720 .string_literal,
721 => return main_tokens[n] + end_offset,
722
723 .@"return" => if (datas[n].lhs != 0) {
724 n = datas[n].lhs;
725 } else {
726 return main_tokens[n] + end_offset;
727 },
728
729 .call, .async_call => {
730 end_offset += 1; // for the rparen
731 const params = tree.extraData(datas[n].rhs, Node.SubRange);
732 if (params.end - params.start == 0) {
733 return main_tokens[n] + end_offset;
734 }
735 n = tree.extra_data[params.end - 1]; // last parameter
736 },
737 .tagged_union_enum_tag => {
738 const members = tree.extraData(datas[n].rhs, Node.SubRange);
739 if (members.end - members.start == 0) {
740 end_offset += 4; // for the rparen + rparen + lbrace + rbrace
741 n = datas[n].lhs;
742 } else {
743 end_offset += 1; // for the rbrace
744 n = tree.extra_data[members.end - 1]; // last parameter
745 }
746 },
747 .call_comma,
748 .async_call_comma,
749 .tagged_union_enum_tag_trailing,
750 => {
751 end_offset += 2; // for the comma/semicolon + rparen/rbrace
752 const params = tree.extraData(datas[n].rhs, Node.SubRange);
753 assert(params.end > params.start);
754 n = tree.extra_data[params.end - 1]; // last parameter
755 },
756 .@"switch" => {
757 const cases = tree.extraData(datas[n].rhs, Node.SubRange);
758 if (cases.end - cases.start == 0) {
759 end_offset += 3; // rparen, lbrace, rbrace
760 n = datas[n].lhs; // condition expression
761 } else {
762 end_offset += 1; // for the rbrace
763 n = tree.extra_data[cases.end - 1]; // last case
764 }
765 },
766 .container_decl_arg => {
767 const members = tree.extraData(datas[n].rhs, Node.SubRange);
768 if (members.end - members.start == 0) {
769 end_offset += 1; // for the rparen
770 n = datas[n].lhs;
771 } else {
772 end_offset += 1; // for the rbrace
773 n = tree.extra_data[members.end - 1]; // last parameter
774 }
775 },
776 .@"asm" => {
777 const extra = tree.extraData(datas[n].rhs, Node.Asm);
778 return extra.rparen + end_offset;
779 },
780 .array_init,
781 .struct_init,
782 => {
783 const elements = tree.extraData(datas[n].rhs, Node.SubRange);
784 assert(elements.end - elements.start > 0);
785 end_offset += 1; // for the rbrace
786 n = tree.extra_data[elements.end - 1]; // last element
787 },
788 .array_init_comma,
789 .struct_init_comma,
790 .container_decl_arg_trailing,
791 .switch_comma,
792 => {
793 const members = tree.extraData(datas[n].rhs, Node.SubRange);
794 assert(members.end - members.start > 0);
795 end_offset += 2; // for the comma + rbrace
796 n = tree.extra_data[members.end - 1]; // last parameter
797 },
798 .array_init_dot,
799 .struct_init_dot,
800 .block,
801 .container_decl,
802 .tagged_union,
803 .builtin_call,
804 => {
805 assert(datas[n].rhs - datas[n].lhs > 0);
806 end_offset += 1; // for the rbrace
807 n = tree.extra_data[datas[n].rhs - 1]; // last statement
808 },
809 .array_init_dot_comma,
810 .struct_init_dot_comma,
811 .block_semicolon,
812 .container_decl_trailing,
813 .tagged_union_trailing,
814 .builtin_call_comma,
815 => {
816 assert(datas[n].rhs - datas[n].lhs > 0);
817 end_offset += 2; // for the comma/semicolon + rbrace/rparen
818 n = tree.extra_data[datas[n].rhs - 1]; // last member
819 },
820 .call_one,
821 .async_call_one,
822 .array_access,
823 => {
824 end_offset += 1; // for the rparen/rbracket
825 if (datas[n].rhs == 0) {
826 return main_tokens[n] + end_offset;
827 }
828 n = datas[n].rhs;
829 },
830 .array_init_dot_two,
831 .block_two,
832 .builtin_call_two,
833 .struct_init_dot_two,
834 .container_decl_two,
835 .tagged_union_two,
836 => {
837 if (datas[n].rhs != 0) {
838 end_offset += 1; // for the rparen/rbrace
839 n = datas[n].rhs;
840 } else if (datas[n].lhs != 0) {
841 end_offset += 1; // for the rparen/rbrace
842 n = datas[n].lhs;
843 } else {
844 switch (tags[n]) {
845 .array_init_dot_two,
846 .block_two,
847 .struct_init_dot_two,
848 => end_offset += 1, // rbrace
849 .builtin_call_two => end_offset += 2, // lparen/lbrace + rparen/rbrace
850 .container_decl_two => {
851 var i: u32 = 2; // lbrace + rbrace
852 while (token_tags[main_tokens[n] + i] == .container_doc_comment) i += 1;
853 end_offset += i;
854 },
855 .tagged_union_two => {
856 var i: u32 = 5; // (enum) {}
857 while (token_tags[main_tokens[n] + i] == .container_doc_comment) i += 1;
858 end_offset += i;
859 },
860 else => unreachable,
861 }
862 return main_tokens[n] + end_offset;
863 }
864 },
865 .array_init_dot_two_comma,
866 .builtin_call_two_comma,
867 .block_two_semicolon,
868 .struct_init_dot_two_comma,
869 .container_decl_two_trailing,
870 .tagged_union_two_trailing,
871 => {
872 end_offset += 2; // for the comma/semicolon + rbrace/rparen
873 if (datas[n].rhs != 0) {
874 n = datas[n].rhs;
875 } else if (datas[n].lhs != 0) {
876 n = datas[n].lhs;
877 } else {
878 unreachable;
879 }
880 },
881 .simple_var_decl => {
882 if (datas[n].rhs != 0) {
883 n = datas[n].rhs;
884 } else if (datas[n].lhs != 0) {
885 n = datas[n].lhs;
886 } else {
887 end_offset += 1; // from mut token to name
888 return main_tokens[n] + end_offset;
889 }
890 },
891 .aligned_var_decl => {
892 if (datas[n].rhs != 0) {
893 n = datas[n].rhs;
894 } else if (datas[n].lhs != 0) {
895 end_offset += 1; // for the rparen
896 n = datas[n].lhs;
897 } else {
898 end_offset += 1; // from mut token to name
899 return main_tokens[n] + end_offset;
900 }
901 },
902 .global_var_decl => {
903 if (datas[n].rhs != 0) {
904 n = datas[n].rhs;
905 } else {
906 const extra = tree.extraData(datas[n].lhs, Node.GlobalVarDecl);
907 if (extra.section_node != 0) {
908 end_offset += 1; // for the rparen
909 n = extra.section_node;
910 } else if (extra.align_node != 0) {
911 end_offset += 1; // for the rparen
912 n = extra.align_node;
913 } else if (extra.type_node != 0) {
914 n = extra.type_node;
915 } else {
916 end_offset += 1; // from mut token to name
917 return main_tokens[n] + end_offset;
918 }
919 }
920 },
921 .local_var_decl => {
922 if (datas[n].rhs != 0) {
923 n = datas[n].rhs;
924 } else {
925 const extra = tree.extraData(datas[n].lhs, Node.LocalVarDecl);
926 if (extra.align_node != 0) {
927 end_offset += 1; // for the rparen
928 n = extra.align_node;
929 } else if (extra.type_node != 0) {
930 n = extra.type_node;
931 } else {
932 end_offset += 1; // from mut token to name
933 return main_tokens[n] + end_offset;
934 }
935 }
936 },
937 .container_field_init => {
938 if (datas[n].rhs != 0) {
939 n = datas[n].rhs;
940 } else if (datas[n].lhs != 0) {
941 n = datas[n].lhs;
942 } else {
943 return main_tokens[n] + end_offset;
944 }
945 },
946 .container_field_align => {
947 if (datas[n].rhs != 0) {
948 end_offset += 1; // for the rparen
949 n = datas[n].rhs;
950 } else if (datas[n].lhs != 0) {
951 n = datas[n].lhs;
952 } else {
953 return main_tokens[n] + end_offset;
954 }
955 },
956 .container_field => {
957 const extra = tree.extraData(datas[n].rhs, Node.ContainerField);
958 if (extra.value_expr != 0) {
959 n = extra.value_expr;
960 } else if (extra.align_expr != 0) {
961 end_offset += 1; // for the rparen
962 n = extra.align_expr;
963 } else if (datas[n].lhs != 0) {
964 n = datas[n].lhs;
965 } else {
966 return main_tokens[n] + end_offset;
967 }
968 },
969
970 .array_init_one,
971 .struct_init_one,
972 => {
973 end_offset += 1; // rbrace
974 if (datas[n].rhs == 0) {
975 return main_tokens[n] + end_offset;
976 } else {
977 n = datas[n].rhs;
978 }
979 },
980 .slice_open,
981 .call_one_comma,
982 .async_call_one_comma,
983 .array_init_one_comma,
984 .struct_init_one_comma,
985 => {
986 end_offset += 2; // ellipsis2 + rbracket, or comma + rparen
987 n = datas[n].rhs;
988 assert(n != 0);
989 },
990 .slice => {
991 const extra = tree.extraData(datas[n].rhs, Node.Slice);
992 assert(extra.end != 0); // should have used SliceOpen
993 end_offset += 1; // rbracket
994 n = extra.end;
995 },
996 .slice_sentinel => {
997 const extra = tree.extraData(datas[n].rhs, Node.SliceSentinel);
998 assert(extra.sentinel != 0); // should have used Slice
999 end_offset += 1; // rbracket
1000 n = extra.sentinel;
1001 },
1002
1003 .@"continue" => {
1004 if (datas[n].lhs != 0) {
1005 return datas[n].lhs + end_offset;
1006 } else {
1007 return main_tokens[n] + end_offset;
1008 }
1009 },
1010 .@"break" => {
1011 if (datas[n].rhs != 0) {
1012 n = datas[n].rhs;
1013 } else if (datas[n].lhs != 0) {
1014 return datas[n].lhs + end_offset;
1015 } else {
1016 return main_tokens[n] + end_offset;
1017 }
1018 },
1019 .fn_decl => {
1020 if (datas[n].rhs != 0) {
1021 n = datas[n].rhs;
1022 } else {
1023 n = datas[n].lhs;
1024 }
1025 },
1026 .fn_proto_one => {
1027 const extra = tree.extraData(datas[n].lhs, Node.FnProtoOne);
1028 // linksection, callconv, align can appear in any order, so we
1029 // find the last one here.
1030 var max_node: Node.Index = datas[n].rhs;
1031 var max_start = token_starts[main_tokens[max_node]];
1032 var max_offset: TokenIndex = 0;
1033 if (extra.align_expr != 0) {
1034 const start = token_starts[main_tokens[extra.align_expr]];
1035 if (start > max_start) {
1036 max_node = extra.align_expr;
1037 max_start = start;
1038 max_offset = 1; // for the rparen
1039 }
1040 }
1041 if (extra.section_expr != 0) {
1042 const start = token_starts[main_tokens[extra.section_expr]];
1043 if (start > max_start) {
1044 max_node = extra.section_expr;
1045 max_start = start;
1046 max_offset = 1; // for the rparen
1047 }
1048 }
1049 if (extra.callconv_expr != 0) {
1050 const start = token_starts[main_tokens[extra.callconv_expr]];
1051 if (start > max_start) {
1052 max_node = extra.callconv_expr;
1053 max_start = start;
1054 max_offset = 1; // for the rparen
1055 }
1056 }
1057 n = max_node;
1058 end_offset += max_offset;
1059 },
1060 .fn_proto => {
1061 const extra = tree.extraData(datas[n].lhs, Node.FnProto);
1062 // linksection, callconv, align can appear in any order, so we
1063 // find the last one here.
1064 var max_node: Node.Index = datas[n].rhs;
1065 var max_start = token_starts[main_tokens[max_node]];
1066 var max_offset: TokenIndex = 0;
1067 if (extra.align_expr != 0) {
1068 const start = token_starts[main_tokens[extra.align_expr]];
1069 if (start > max_start) {
1070 max_node = extra.align_expr;
1071 max_start = start;
1072 max_offset = 1; // for the rparen
1073 }
1074 }
1075 if (extra.section_expr != 0) {
1076 const start = token_starts[main_tokens[extra.section_expr]];
1077 if (start > max_start) {
1078 max_node = extra.section_expr;
1079 max_start = start;
1080 max_offset = 1; // for the rparen
1081 }
1082 }
1083 if (extra.callconv_expr != 0) {
1084 const start = token_starts[main_tokens[extra.callconv_expr]];
1085 if (start > max_start) {
1086 max_node = extra.callconv_expr;
1087 max_start = start;
1088 max_offset = 1; // for the rparen
1089 }
1090 }
1091 n = max_node;
1092 end_offset += max_offset;
1093 },
1094 .while_cont => {
1095 const extra = tree.extraData(datas[n].rhs, Node.WhileCont);
1096 assert(extra.then_expr != 0);
1097 n = extra.then_expr;
1098 },
1099 .@"while" => {
1100 const extra = tree.extraData(datas[n].rhs, Node.While);
1101 assert(extra.else_expr != 0);
1102 n = extra.else_expr;
1103 },
1104 .@"if", .@"for" => {
1105 const extra = tree.extraData(datas[n].rhs, Node.If);
1106 assert(extra.else_expr != 0);
1107 n = extra.else_expr;
1108 },
1109 .@"suspend" => {
1110 if (datas[n].lhs != 0) {
1111 n = datas[n].lhs;
1112 } else {
1113 return main_tokens[n] + end_offset;
1114 }
1115 },
1116 .array_type_sentinel => {
1117 const extra = tree.extraData(datas[n].rhs, Node.ArrayTypeSentinel);
1118 n = extra.elem_type;
1119 },
1120 };
2281121 }
2291122
230 pub fn loc(self: *const Error) TokenIndex {
231 switch (self.*) {
232 .InvalidToken => |x| return x.token,
233 .ExpectedContainerMembers => |x| return x.token,
234 .ExpectedStringLiteral => |x| return x.token,
235 .ExpectedIntegerLiteral => |x| return x.token,
236 .ExpectedPubItem => |x| return x.token,
237 .ExpectedIdentifier => |x| return x.token,
238 .ExpectedStatement => |x| return x.token,
239 .ExpectedVarDeclOrFn => |x| return x.token,
240 .ExpectedVarDecl => |x| return x.token,
241 .ExpectedFn => |x| return x.token,
242 .ExpectedReturnType => |x| return x.token,
243 .ExpectedAggregateKw => |x| return x.token,
244 .UnattachedDocComment => |x| return x.token,
245 .ExpectedEqOrSemi => |x| return x.token,
246 .ExpectedSemiOrLBrace => |x| return x.token,
247 .ExpectedSemiOrElse => |x| return x.token,
248 .ExpectedLabelOrLBrace => |x| return x.token,
249 .ExpectedLBrace => |x| return x.token,
250 .ExpectedColonOrRParen => |x| return x.token,
251 .ExpectedLabelable => |x| return x.token,
252 .ExpectedInlinable => |x| return x.token,
253 .ExpectedAsmOutputReturnOrType => |x| return x.token,
254 .ExpectedCall => |x| return x.node.firstToken(),
255 .ExpectedCallOrFnProto => |x| return x.node.firstToken(),
256 .ExpectedSliceOrRBracket => |x| return x.token,
257 .ExtraAlignQualifier => |x| return x.token,
258 .ExtraConstQualifier => |x| return x.token,
259 .ExtraVolatileQualifier => |x| return x.token,
260 .ExtraAllowZeroQualifier => |x| return x.token,
261 .ExpectedTypeExpr => |x| return x.token,
262 .ExpectedPrimaryTypeExpr => |x| return x.token,
263 .ExpectedParamType => |x| return x.token,
264 .ExpectedExpr => |x| return x.token,
265 .ExpectedPrimaryExpr => |x| return x.token,
266 .ExpectedToken => |x| return x.token,
267 .ExpectedCommaOrEnd => |x| return x.token,
268 .ExpectedParamList => |x| return x.token,
269 .ExpectedPayload => |x| return x.token,
270 .ExpectedBlockOrAssignment => |x| return x.token,
271 .ExpectedBlockOrExpression => |x| return x.token,
272 .ExpectedExprOrAssignment => |x| return x.token,
273 .ExpectedPrefixExpr => |x| return x.token,
274 .ExpectedLoopExpr => |x| return x.token,
275 .ExpectedDerefOrUnwrap => |x| return x.token,
276 .ExpectedSuffixOp => |x| return x.token,
277 .ExpectedBlockOrField => |x| return x.token,
278 .DeclBetweenFields => |x| return x.token,
279 .InvalidAnd => |x| return x.token,
280 .AsteriskAfterPointerDereference => |x| return x.token,
281 }
1123 pub fn tokensOnSameLine(tree: Tree, token1: TokenIndex, token2: TokenIndex) bool {
1124 const token_starts = tree.tokens.items(.start);
1125 const source = tree.source[token_starts[token1]..token_starts[token2]];
1126 return mem.indexOfScalar(u8, source, '\n') == null;
2821127 }
2831128
284 pub const InvalidToken = SingleTokenError("Invalid token '{s}'");
285 pub const ExpectedContainerMembers = SingleTokenError("Expected test, comptime, var decl, or container field, found '{s}'");
286 pub const ExpectedStringLiteral = SingleTokenError("Expected string literal, found '{s}'");
287 pub const ExpectedIntegerLiteral = SingleTokenError("Expected integer literal, found '{s}'");
288 pub const ExpectedIdentifier = SingleTokenError("Expected identifier, found '{s}'");
289 pub const ExpectedStatement = SingleTokenError("Expected statement, found '{s}'");
290 pub const ExpectedVarDeclOrFn = SingleTokenError("Expected variable declaration or function, found '{s}'");
291 pub const ExpectedVarDecl = SingleTokenError("Expected variable declaration, found '{s}'");
292 pub const ExpectedFn = SingleTokenError("Expected function, found '{s}'");
293 pub const ExpectedReturnType = SingleTokenError("Expected 'var' or return type expression, found '{s}'");
294 pub const ExpectedAggregateKw = SingleTokenError("Expected '" ++ Token.Id.Keyword_struct.symbol() ++ "', '" ++ Token.Id.Keyword_union.symbol() ++ "', '" ++ Token.Id.Keyword_enum.symbol() ++ "', or '" ++ Token.Id.Keyword_opaque.symbol() ++ "', found '{s}'");
295 pub const ExpectedEqOrSemi = SingleTokenError("Expected '=' or ';', found '{s}'");
296 pub const ExpectedSemiOrLBrace = SingleTokenError("Expected ';' or '{{', found '{s}'");
297 pub const ExpectedSemiOrElse = SingleTokenError("Expected ';' or 'else', found '{s}'");
298 pub const ExpectedLBrace = SingleTokenError("Expected '{{', found '{s}'");
299 pub const ExpectedLabelOrLBrace = SingleTokenError("Expected label or '{{', found '{s}'");
300 pub const ExpectedColonOrRParen = SingleTokenError("Expected ':' or ')', found '{s}'");
301 pub const ExpectedLabelable = SingleTokenError("Expected 'while', 'for', 'inline', 'suspend', or '{{', found '{s}'");
302 pub const ExpectedInlinable = SingleTokenError("Expected 'while' or 'for', found '{s}'");
303 pub const ExpectedAsmOutputReturnOrType = SingleTokenError("Expected '->' or '" ++ Token.Id.Identifier.symbol() ++ "', found '{s}'");
304 pub const ExpectedSliceOrRBracket = SingleTokenError("Expected ']' or '..', found '{s}'");
305 pub const ExpectedTypeExpr = SingleTokenError("Expected type expression, found '{s}'");
306 pub const ExpectedPrimaryTypeExpr = SingleTokenError("Expected primary type expression, found '{s}'");
307 pub const ExpectedExpr = SingleTokenError("Expected expression, found '{s}'");
308 pub const ExpectedPrimaryExpr = SingleTokenError("Expected primary expression, found '{s}'");
309 pub const ExpectedParamList = SingleTokenError("Expected parameter list, found '{s}'");
310 pub const ExpectedPayload = SingleTokenError("Expected loop payload, found '{s}'");
311 pub const ExpectedBlockOrAssignment = SingleTokenError("Expected block or assignment, found '{s}'");
312 pub const ExpectedBlockOrExpression = SingleTokenError("Expected block or expression, found '{s}'");
313 pub const ExpectedExprOrAssignment = SingleTokenError("Expected expression or assignment, found '{s}'");
314 pub const ExpectedPrefixExpr = SingleTokenError("Expected prefix expression, found '{s}'");
315 pub const ExpectedLoopExpr = SingleTokenError("Expected loop expression, found '{s}'");
316 pub const ExpectedDerefOrUnwrap = SingleTokenError("Expected pointer dereference or optional unwrap, found '{s}'");
317 pub const ExpectedSuffixOp = SingleTokenError("Expected pointer dereference, optional unwrap, or field access, found '{s}'");
318 pub const ExpectedBlockOrField = SingleTokenError("Expected block or field, found '{s}'");
319
320 pub const ExpectedParamType = SimpleError("Expected parameter type");
321 pub const ExpectedPubItem = SimpleError("Expected function or variable declaration after pub");
322 pub const UnattachedDocComment = SimpleError("Unattached documentation comment");
323 pub const ExtraAlignQualifier = SimpleError("Extra align qualifier");
324 pub const ExtraConstQualifier = SimpleError("Extra const qualifier");
325 pub const ExtraVolatileQualifier = SimpleError("Extra volatile qualifier");
326 pub const ExtraAllowZeroQualifier = SimpleError("Extra allowzero qualifier");
327 pub const DeclBetweenFields = SimpleError("Declarations are not allowed between container fields");
328 pub const InvalidAnd = SimpleError("`&&` is invalid. Note that `and` is boolean AND.");
329 pub const AsteriskAfterPointerDereference = SimpleError("`.*` can't be followed by `*`. Are you missing a space?");
330
331 pub const ExpectedCall = struct {
332 node: *Node,
333
334 pub fn render(self: *const ExpectedCall, tokens: []const Token.Id, stream: anytype) !void {
335 return stream.print("expected " ++ @tagName(Node.Tag.Call) ++ ", found {s}", .{
336 @tagName(self.node.tag),
337 });
338 }
339 };
1129 pub fn getNodeSource(tree: Tree, node: Node.Index) []const u8 {
1130 const token_starts = tree.tokens.items(.start);
1131 const first_token = tree.firstToken(node);
1132 const last_token = tree.lastToken(node);
1133 const start = token_starts[first_token];
1134 const end = token_starts[last_token] + tree.tokenSlice(last_token).len;
1135 return tree.source[start..end];
1136 }
3401137
341 pub const ExpectedCallOrFnProto = struct {
342 node: *Node,
1138 pub fn globalVarDecl(tree: Tree, node: Node.Index) full.VarDecl {
1139 assert(tree.nodes.items(.tag)[node] == .global_var_decl);
1140 const data = tree.nodes.items(.data)[node];
1141 const extra = tree.extraData(data.lhs, Node.GlobalVarDecl);
1142 return tree.fullVarDecl(.{
1143 .type_node = extra.type_node,
1144 .align_node = extra.align_node,
1145 .section_node = extra.section_node,
1146 .init_node = data.rhs,
1147 .mut_token = tree.nodes.items(.main_token)[node],
1148 });
1149 }
3431150
344 pub fn render(self: *const ExpectedCallOrFnProto, tokens: []const Token.Id, stream: anytype) !void {
345 return stream.print("expected " ++ @tagName(Node.Tag.Call) ++ " or " ++
346 @tagName(Node.Tag.FnProto) ++ ", found {s}", .{@tagName(self.node.tag)});
347 }
348 };
1151 pub fn localVarDecl(tree: Tree, node: Node.Index) full.VarDecl {
1152 assert(tree.nodes.items(.tag)[node] == .local_var_decl);
1153 const data = tree.nodes.items(.data)[node];
1154 const extra = tree.extraData(data.lhs, Node.LocalVarDecl);
1155 return tree.fullVarDecl(.{
1156 .type_node = extra.type_node,
1157 .align_node = extra.align_node,
1158 .section_node = 0,
1159 .init_node = data.rhs,
1160 .mut_token = tree.nodes.items(.main_token)[node],
1161 });
1162 }
3491163
350 pub const ExpectedToken = struct {
351 token: TokenIndex,
352 expected_id: Token.Id,
1164 pub fn simpleVarDecl(tree: Tree, node: Node.Index) full.VarDecl {
1165 assert(tree.nodes.items(.tag)[node] == .simple_var_decl);
1166 const data = tree.nodes.items(.data)[node];
1167 return tree.fullVarDecl(.{
1168 .type_node = data.lhs,
1169 .align_node = 0,
1170 .section_node = 0,
1171 .init_node = data.rhs,
1172 .mut_token = tree.nodes.items(.main_token)[node],
1173 });
1174 }
3531175
354 pub fn render(self: *const ExpectedToken, tokens: []const Token.Id, stream: anytype) !void {
355 const found_token = tokens[self.token];
356 switch (found_token) {
357 .Invalid => {
358 return stream.print("expected '{s}', found invalid bytes", .{self.expected_id.symbol()});
359 },
360 else => {
361 const token_name = found_token.symbol();
362 return stream.print("expected '{s}', found '{s}'", .{ self.expected_id.symbol(), token_name });
363 },
364 }
365 }
366 };
1176 pub fn alignedVarDecl(tree: Tree, node: Node.Index) full.VarDecl {
1177 assert(tree.nodes.items(.tag)[node] == .aligned_var_decl);
1178 const data = tree.nodes.items(.data)[node];
1179 return tree.fullVarDecl(.{
1180 .type_node = 0,
1181 .align_node = data.lhs,
1182 .section_node = 0,
1183 .init_node = data.rhs,
1184 .mut_token = tree.nodes.items(.main_token)[node],
1185 });
1186 }
3671187
368 pub const ExpectedCommaOrEnd = struct {
369 token: TokenIndex,
370 end_id: Token.Id,
1188 pub fn ifSimple(tree: Tree, node: Node.Index) full.If {
1189 assert(tree.nodes.items(.tag)[node] == .if_simple);
1190 const data = tree.nodes.items(.data)[node];
1191 return tree.fullIf(.{
1192 .cond_expr = data.lhs,
1193 .then_expr = data.rhs,
1194 .else_expr = 0,
1195 .if_token = tree.nodes.items(.main_token)[node],
1196 });
1197 }
3711198
372 pub fn render(self: *const ExpectedCommaOrEnd, tokens: []const Token.Id, stream: anytype) !void {
373 const actual_token = tokens[self.token];
374 return stream.print("expected ',' or '{s}', found '{s}'", .{
375 self.end_id.symbol(),
376 actual_token.symbol(),
377 });
378 }
379 };
1199 pub fn ifFull(tree: Tree, node: Node.Index) full.If {
1200 assert(tree.nodes.items(.tag)[node] == .@"if");
1201 const data = tree.nodes.items(.data)[node];
1202 const extra = tree.extraData(data.rhs, Node.If);
1203 return tree.fullIf(.{
1204 .cond_expr = data.lhs,
1205 .then_expr = extra.then_expr,
1206 .else_expr = extra.else_expr,
1207 .if_token = tree.nodes.items(.main_token)[node],
1208 });
1209 }
3801210
381 fn SingleTokenError(comptime msg: []const u8) type {
382 return struct {
383 const ThisError = @This();
1211 pub fn containerField(tree: Tree, node: Node.Index) full.ContainerField {
1212 assert(tree.nodes.items(.tag)[node] == .container_field);
1213 const data = tree.nodes.items(.data)[node];
1214 const extra = tree.extraData(data.rhs, Node.ContainerField);
1215 return tree.fullContainerField(.{
1216 .name_token = tree.nodes.items(.main_token)[node],
1217 .type_expr = data.lhs,
1218 .value_expr = extra.value_expr,
1219 .align_expr = extra.align_expr,
1220 });
1221 }
3841222
385 token: TokenIndex,
1223 pub fn containerFieldInit(tree: Tree, node: Node.Index) full.ContainerField {
1224 assert(tree.nodes.items(.tag)[node] == .container_field_init);
1225 const data = tree.nodes.items(.data)[node];
1226 return tree.fullContainerField(.{
1227 .name_token = tree.nodes.items(.main_token)[node],
1228 .type_expr = data.lhs,
1229 .value_expr = data.rhs,
1230 .align_expr = 0,
1231 });
1232 }
3861233
387 pub fn render(self: *const ThisError, tokens: []const Token.Id, stream: anytype) !void {
388 const actual_token = tokens[self.token];
389 return stream.print(msg, .{actual_token.symbol()});
390 }
391 };
1234 pub fn containerFieldAlign(tree: Tree, node: Node.Index) full.ContainerField {
1235 assert(tree.nodes.items(.tag)[node] == .container_field_align);
1236 const data = tree.nodes.items(.data)[node];
1237 return tree.fullContainerField(.{
1238 .name_token = tree.nodes.items(.main_token)[node],
1239 .type_expr = data.lhs,
1240 .value_expr = 0,
1241 .align_expr = data.rhs,
1242 });
3921243 }
3931244
394 fn SimpleError(comptime msg: []const u8) type {
395 return struct {
396 const ThisError = @This();
1245 pub fn fnProtoSimple(tree: Tree, buffer: *[1]Node.Index, node: Node.Index) full.FnProto {
1246 assert(tree.nodes.items(.tag)[node] == .fn_proto_simple);
1247 const data = tree.nodes.items(.data)[node];
1248 buffer[0] = data.lhs;
1249 const params = if (data.lhs == 0) buffer[0..0] else buffer[0..1];
1250 return tree.fullFnProto(.{
1251 .fn_token = tree.nodes.items(.main_token)[node],
1252 .return_type = data.rhs,
1253 .params = params,
1254 .align_expr = 0,
1255 .section_expr = 0,
1256 .callconv_expr = 0,
1257 });
1258 }
3971259
398 token: TokenIndex,
1260 pub fn fnProtoMulti(tree: Tree, node: Node.Index) full.FnProto {
1261 assert(tree.nodes.items(.tag)[node] == .fn_proto_multi);
1262 const data = tree.nodes.items(.data)[node];
1263 const params_range = tree.extraData(data.lhs, Node.SubRange);
1264 const params = tree.extra_data[params_range.start..params_range.end];
1265 return tree.fullFnProto(.{
1266 .fn_token = tree.nodes.items(.main_token)[node],
1267 .return_type = data.rhs,
1268 .params = params,
1269 .align_expr = 0,
1270 .section_expr = 0,
1271 .callconv_expr = 0,
1272 });
1273 }
3991274
400 pub fn render(self: *const ThisError, tokens: []const Token.Id, stream: anytype) !void {
401 return stream.writeAll(msg);
402 }
403 };
1275 pub fn fnProtoOne(tree: Tree, buffer: *[1]Node.Index, node: Node.Index) full.FnProto {
1276 assert(tree.nodes.items(.tag)[node] == .fn_proto_one);
1277 const data = tree.nodes.items(.data)[node];
1278 const extra = tree.extraData(data.lhs, Node.FnProtoOne);
1279 buffer[0] = extra.param;
1280 const params = if (extra.param == 0) buffer[0..0] else buffer[0..1];
1281 return tree.fullFnProto(.{
1282 .fn_token = tree.nodes.items(.main_token)[node],
1283 .return_type = data.rhs,
1284 .params = params,
1285 .align_expr = extra.align_expr,
1286 .section_expr = extra.section_expr,
1287 .callconv_expr = extra.callconv_expr,
1288 });
4041289 }
405};
4061290
407pub const Node = struct {
408 tag: Tag,
1291 pub fn fnProto(tree: Tree, node: Node.Index) full.FnProto {
1292 assert(tree.nodes.items(.tag)[node] == .fn_proto);
1293 const data = tree.nodes.items(.data)[node];
1294 const extra = tree.extraData(data.lhs, Node.FnProto);
1295 const params = tree.extra_data[extra.params_start..extra.params_end];
1296 return tree.fullFnProto(.{
1297 .fn_token = tree.nodes.items(.main_token)[node],
1298 .return_type = data.rhs,
1299 .params = params,
1300 .align_expr = extra.align_expr,
1301 .section_expr = extra.section_expr,
1302 .callconv_expr = extra.callconv_expr,
1303 });
1304 }
4091305
410 pub const Tag = enum {
411 // Top level
412 Root,
413 Use,
414 TestDecl,
415
416 // Statements
417 VarDecl,
418 Defer,
419
420 // Infix operators
421 Catch,
422
423 // SimpleInfixOp
424 Add,
425 AddWrap,
426 ArrayCat,
427 ArrayMult,
428 Assign,
429 AssignBitAnd,
430 AssignBitOr,
431 AssignBitShiftLeft,
432 AssignBitShiftRight,
433 AssignBitXor,
434 AssignDiv,
435 AssignSub,
436 AssignSubWrap,
437 AssignMod,
438 AssignAdd,
439 AssignAddWrap,
440 AssignMul,
441 AssignMulWrap,
442 BangEqual,
443 BitAnd,
444 BitOr,
445 BitShiftLeft,
446 BitShiftRight,
447 BitXor,
448 BoolAnd,
449 BoolOr,
450 Div,
451 EqualEqual,
452 ErrorUnion,
453 GreaterOrEqual,
454 GreaterThan,
455 LessOrEqual,
456 LessThan,
457 MergeErrorSets,
458 Mod,
459 Mul,
460 MulWrap,
461 Period,
462 Range,
463 Sub,
464 SubWrap,
465 OrElse,
466
467 // SimplePrefixOp
468 AddressOf,
469 Await,
470 BitNot,
471 BoolNot,
472 OptionalType,
473 Negation,
474 NegationWrap,
475 Resume,
476 Try,
477
478 ArrayType,
479 /// ArrayType but has a sentinel node.
480 ArrayTypeSentinel,
481 PtrType,
482 SliceType,
483 /// `a[b..c]`
484 Slice,
485 /// `a.*`
486 Deref,
487 /// `a.?`
488 UnwrapOptional,
489 /// `a[b]`
490 ArrayAccess,
491 /// `T{a, b}`
492 ArrayInitializer,
493 /// ArrayInitializer but with `.` instead of a left-hand-side operand.
494 ArrayInitializerDot,
495 /// `T{.a = b}`
496 StructInitializer,
497 /// StructInitializer but with `.` instead of a left-hand-side operand.
498 StructInitializerDot,
499 /// `foo()`
500 Call,
501
502 // Control flow
503 Switch,
504 While,
505 For,
506 If,
507 Suspend,
508 Continue,
509 Break,
510 Return,
511
512 // Type expressions
513 AnyType,
514 ErrorType,
515 FnProto,
516 AnyFrameType,
517
518 // Primary expressions
519 IntegerLiteral,
520 FloatLiteral,
521 EnumLiteral,
522 StringLiteral,
523 MultilineStringLiteral,
524 CharLiteral,
525 BoolLiteral,
526 NullLiteral,
527 UndefinedLiteral,
528 Unreachable,
529 Identifier,
530 GroupedExpression,
531 BuiltinCall,
532 ErrorSetDecl,
533 ContainerDecl,
534 Asm,
535 Comptime,
536 Nosuspend,
537 Block,
538 LabeledBlock,
539
540 // Misc
541 DocComment,
542 SwitchCase, // TODO make this not a child of AST Node
543 SwitchElse, // TODO make this not a child of AST Node
544 Else, // TODO make this not a child of AST Node
545 Payload, // TODO make this not a child of AST Node
546 PointerPayload, // TODO make this not a child of AST Node
547 PointerIndexPayload, // TODO make this not a child of AST Node
548 ContainerField,
549 ErrorTag, // TODO make this not a child of AST Node
550 FieldInitializer, // TODO make this not a child of AST Node
551
552 pub fn Type(tag: Tag) type {
553 return switch (tag) {
554 .Root => Root,
555 .Use => Use,
556 .TestDecl => TestDecl,
557 .VarDecl => VarDecl,
558 .Defer => Defer,
559 .Catch => Catch,
560
561 .Add,
562 .AddWrap,
563 .ArrayCat,
564 .ArrayMult,
565 .Assign,
566 .AssignBitAnd,
567 .AssignBitOr,
568 .AssignBitShiftLeft,
569 .AssignBitShiftRight,
570 .AssignBitXor,
571 .AssignDiv,
572 .AssignSub,
573 .AssignSubWrap,
574 .AssignMod,
575 .AssignAdd,
576 .AssignAddWrap,
577 .AssignMul,
578 .AssignMulWrap,
579 .BangEqual,
580 .BitAnd,
581 .BitOr,
582 .BitShiftLeft,
583 .BitShiftRight,
584 .BitXor,
585 .BoolAnd,
586 .BoolOr,
587 .Div,
588 .EqualEqual,
589 .ErrorUnion,
590 .GreaterOrEqual,
591 .GreaterThan,
592 .LessOrEqual,
593 .LessThan,
594 .MergeErrorSets,
595 .Mod,
596 .Mul,
597 .MulWrap,
598 .Period,
599 .Range,
600 .Sub,
601 .SubWrap,
602 .OrElse,
603 => SimpleInfixOp,
604
605 .AddressOf,
606 .Await,
607 .BitNot,
608 .BoolNot,
609 .OptionalType,
610 .Negation,
611 .NegationWrap,
612 .Resume,
613 .Try,
614 => SimplePrefixOp,
615
616 .Identifier,
617 .BoolLiteral,
618 .NullLiteral,
619 .UndefinedLiteral,
620 .Unreachable,
621 .AnyType,
622 .ErrorType,
623 .IntegerLiteral,
624 .FloatLiteral,
625 .StringLiteral,
626 .CharLiteral,
627 => OneToken,
628
629 .Continue,
630 .Break,
631 .Return,
632 => ControlFlowExpression,
633
634 .ArrayType => ArrayType,
635 .ArrayTypeSentinel => ArrayTypeSentinel,
636
637 .PtrType => PtrType,
638 .SliceType => SliceType,
639 .Slice => Slice,
640 .Deref, .UnwrapOptional => SimpleSuffixOp,
641 .ArrayAccess => ArrayAccess,
642
643 .ArrayInitializer => ArrayInitializer,
644 .ArrayInitializerDot => ArrayInitializerDot,
645
646 .StructInitializer => StructInitializer,
647 .StructInitializerDot => StructInitializerDot,
648
649 .Call => Call,
650 .Switch => Switch,
651 .While => While,
652 .For => For,
653 .If => If,
654 .Suspend => Suspend,
655 .FnProto => FnProto,
656 .AnyFrameType => AnyFrameType,
657 .EnumLiteral => EnumLiteral,
658 .MultilineStringLiteral => MultilineStringLiteral,
659 .GroupedExpression => GroupedExpression,
660 .BuiltinCall => BuiltinCall,
661 .ErrorSetDecl => ErrorSetDecl,
662 .ContainerDecl => ContainerDecl,
663 .Asm => Asm,
664 .Comptime => Comptime,
665 .Nosuspend => Nosuspend,
666 .Block => Block,
667 .LabeledBlock => LabeledBlock,
668 .DocComment => DocComment,
669 .SwitchCase => SwitchCase,
670 .SwitchElse => SwitchElse,
671 .Else => Else,
672 .Payload => Payload,
673 .PointerPayload => PointerPayload,
674 .PointerIndexPayload => PointerIndexPayload,
675 .ContainerField => ContainerField,
676 .ErrorTag => ErrorTag,
677 .FieldInitializer => FieldInitializer,
678 };
679 }
1306 pub fn structInitOne(tree: Tree, buffer: *[1]Node.Index, node: Node.Index) full.StructInit {
1307 assert(tree.nodes.items(.tag)[node] == .struct_init_one or
1308 tree.nodes.items(.tag)[node] == .struct_init_one_comma);
1309 const data = tree.nodes.items(.data)[node];
1310 buffer[0] = data.rhs;
1311 const fields = if (data.rhs == 0) buffer[0..0] else buffer[0..1];
1312 return tree.fullStructInit(.{
1313 .lbrace = tree.nodes.items(.main_token)[node],
1314 .fields = fields,
1315 .type_expr = data.lhs,
1316 });
1317 }
6801318
681 pub fn isBlock(tag: Tag) bool {
682 return switch (tag) {
683 .Block, .LabeledBlock => true,
684 else => false,
685 };
686 }
687 };
1319 pub fn structInitDotTwo(tree: Tree, buffer: *[2]Node.Index, node: Node.Index) full.StructInit {
1320 assert(tree.nodes.items(.tag)[node] == .struct_init_dot_two or
1321 tree.nodes.items(.tag)[node] == .struct_init_dot_two_comma);
1322 const data = tree.nodes.items(.data)[node];
1323 buffer.* = .{ data.lhs, data.rhs };
1324 const fields = if (data.rhs != 0)
1325 buffer[0..2]
1326 else if (data.lhs != 0)
1327 buffer[0..1]
1328 else
1329 buffer[0..0];
1330 return tree.fullStructInit(.{
1331 .lbrace = tree.nodes.items(.main_token)[node],
1332 .fields = fields,
1333 .type_expr = 0,
1334 });
1335 }
6881336
689 /// Prefer `castTag` to this.
690 pub fn cast(base: *Node, comptime T: type) ?*T {
691 if (std.meta.fieldInfo(T, .base).default_value) |default_base| {
692 return base.castTag(default_base.tag);
693 }
694 inline for (@typeInfo(Tag).Enum.fields) |field| {
695 const tag = @intToEnum(Tag, field.value);
696 if (base.tag == tag) {
697 if (T == tag.Type()) {
698 return @fieldParentPtr(T, "base", base);
699 }
700 return null;
701 }
702 }
703 unreachable;
1337 pub fn structInitDot(tree: Tree, node: Node.Index) full.StructInit {
1338 assert(tree.nodes.items(.tag)[node] == .struct_init_dot or
1339 tree.nodes.items(.tag)[node] == .struct_init_dot_comma);
1340 const data = tree.nodes.items(.data)[node];
1341 return tree.fullStructInit(.{
1342 .lbrace = tree.nodes.items(.main_token)[node],
1343 .fields = tree.extra_data[data.lhs..data.rhs],
1344 .type_expr = 0,
1345 });
7041346 }
7051347
706 pub fn castTag(base: *Node, comptime tag: Tag) ?*tag.Type() {
707 if (base.tag == tag) {
708 return @fieldParentPtr(tag.Type(), "base", base);
709 }
710 return null;
1348 pub fn structInit(tree: Tree, node: Node.Index) full.StructInit {
1349 assert(tree.nodes.items(.tag)[node] == .struct_init or
1350 tree.nodes.items(.tag)[node] == .struct_init_comma);
1351 const data = tree.nodes.items(.data)[node];
1352 const fields_range = tree.extraData(data.rhs, Node.SubRange);
1353 return tree.fullStructInit(.{
1354 .lbrace = tree.nodes.items(.main_token)[node],
1355 .fields = tree.extra_data[fields_range.start..fields_range.end],
1356 .type_expr = data.lhs,
1357 });
7111358 }
7121359
713 pub fn iterate(base: *Node, index: usize) ?*Node {
714 inline for (@typeInfo(Tag).Enum.fields) |field| {
715 const tag = @intToEnum(Tag, field.value);
716 if (base.tag == tag) {
717 return @fieldParentPtr(tag.Type(), "base", base).iterate(index);
718 }
719 }
720 unreachable;
1360 pub fn arrayInitOne(tree: Tree, buffer: *[1]Node.Index, node: Node.Index) full.ArrayInit {
1361 assert(tree.nodes.items(.tag)[node] == .array_init_one or
1362 tree.nodes.items(.tag)[node] == .array_init_one_comma);
1363 const data = tree.nodes.items(.data)[node];
1364 buffer[0] = data.rhs;
1365 const elements = if (data.rhs == 0) buffer[0..0] else buffer[0..1];
1366 return .{
1367 .ast = .{
1368 .lbrace = tree.nodes.items(.main_token)[node],
1369 .elements = elements,
1370 .type_expr = data.lhs,
1371 },
1372 };
7211373 }
7221374
723 pub fn firstToken(base: *const Node) TokenIndex {
724 inline for (@typeInfo(Tag).Enum.fields) |field| {
725 const tag = @intToEnum(Tag, field.value);
726 if (base.tag == tag) {
727 return @fieldParentPtr(tag.Type(), "base", base).firstToken();
728 }
729 }
730 unreachable;
1375 pub fn arrayInitDotTwo(tree: Tree, buffer: *[2]Node.Index, node: Node.Index) full.ArrayInit {
1376 assert(tree.nodes.items(.tag)[node] == .array_init_dot_two or
1377 tree.nodes.items(.tag)[node] == .array_init_dot_two_comma);
1378 const data = tree.nodes.items(.data)[node];
1379 buffer.* = .{ data.lhs, data.rhs };
1380 const elements = if (data.rhs != 0)
1381 buffer[0..2]
1382 else if (data.lhs != 0)
1383 buffer[0..1]
1384 else
1385 buffer[0..0];
1386 return .{
1387 .ast = .{
1388 .lbrace = tree.nodes.items(.main_token)[node],
1389 .elements = elements,
1390 .type_expr = 0,
1391 },
1392 };
7311393 }
7321394
733 pub fn lastToken(base: *const Node) TokenIndex {
734 inline for (@typeInfo(Tag).Enum.fields) |field| {
735 const tag = @intToEnum(Tag, field.value);
736 if (base.tag == tag) {
737 return @fieldParentPtr(tag.Type(), "base", base).lastToken();
738 }
739 }
740 unreachable;
741 }
742
743 pub fn requireSemiColon(base: *const Node) bool {
744 var n = base;
745 while (true) {
746 switch (n.tag) {
747 .Root,
748 .ContainerField,
749 .Block,
750 .LabeledBlock,
751 .Payload,
752 .PointerPayload,
753 .PointerIndexPayload,
754 .Switch,
755 .SwitchCase,
756 .SwitchElse,
757 .FieldInitializer,
758 .DocComment,
759 .TestDecl,
760 => return false,
761
762 .While => {
763 const while_node = @fieldParentPtr(While, "base", n);
764 if (while_node.@"else") |@"else"| {
765 n = &@"else".base;
766 continue;
767 }
1395 pub fn arrayInitDot(tree: Tree, node: Node.Index) full.ArrayInit {
1396 assert(tree.nodes.items(.tag)[node] == .array_init_dot or
1397 tree.nodes.items(.tag)[node] == .array_init_dot_comma);
1398 const data = tree.nodes.items(.data)[node];
1399 return .{
1400 .ast = .{
1401 .lbrace = tree.nodes.items(.main_token)[node],
1402 .elements = tree.extra_data[data.lhs..data.rhs],
1403 .type_expr = 0,
1404 },
1405 };
1406 }
7681407
769 return !while_node.body.tag.isBlock();
770 },
771 .For => {
772 const for_node = @fieldParentPtr(For, "base", n);
773 if (for_node.@"else") |@"else"| {
774 n = &@"else".base;
775 continue;
776 }
1408 pub fn arrayInit(tree: Tree, node: Node.Index) full.ArrayInit {
1409 assert(tree.nodes.items(.tag)[node] == .array_init or
1410 tree.nodes.items(.tag)[node] == .array_init_comma);
1411 const data = tree.nodes.items(.data)[node];
1412 const elem_range = tree.extraData(data.rhs, Node.SubRange);
1413 return .{
1414 .ast = .{
1415 .lbrace = tree.nodes.items(.main_token)[node],
1416 .elements = tree.extra_data[elem_range.start..elem_range.end],
1417 .type_expr = data.lhs,
1418 },
1419 };
1420 }
7771421
778 return !for_node.body.tag.isBlock();
779 },
780 .If => {
781 const if_node = @fieldParentPtr(If, "base", n);
782 if (if_node.@"else") |@"else"| {
783 n = &@"else".base;
784 continue;
785 }
1422 pub fn arrayType(tree: Tree, node: Node.Index) full.ArrayType {
1423 assert(tree.nodes.items(.tag)[node] == .array_type);
1424 const data = tree.nodes.items(.data)[node];
1425 return .{
1426 .ast = .{
1427 .lbracket = tree.nodes.items(.main_token)[node],
1428 .elem_count = data.lhs,
1429 .sentinel = null,
1430 .elem_type = data.rhs,
1431 },
1432 };
1433 }
7861434
787 return !if_node.body.tag.isBlock();
788 },
789 .Else => {
790 const else_node = @fieldParentPtr(Else, "base", n);
791 n = else_node.body;
792 continue;
793 },
794 .Defer => {
795 const defer_node = @fieldParentPtr(Defer, "base", n);
796 return !defer_node.expr.tag.isBlock();
797 },
798 .Comptime => {
799 const comptime_node = @fieldParentPtr(Comptime, "base", n);
800 return !comptime_node.expr.tag.isBlock();
801 },
802 .Suspend => {
803 const suspend_node = @fieldParentPtr(Suspend, "base", n);
804 if (suspend_node.body) |body| {
805 return !body.tag.isBlock();
806 }
1435 pub fn arrayTypeSentinel(tree: Tree, node: Node.Index) full.ArrayType {
1436 assert(tree.nodes.items(.tag)[node] == .array_type_sentinel);
1437 const data = tree.nodes.items(.data)[node];
1438 const extra = tree.extraData(data.rhs, Node.ArrayTypeSentinel);
1439 return .{
1440 .ast = .{
1441 .lbracket = tree.nodes.items(.main_token)[node],
1442 .elem_count = data.lhs,
1443 .sentinel = extra.sentinel,
1444 .elem_type = extra.elem_type,
1445 },
1446 };
1447 }
8071448
808 return true;
809 },
810 .Nosuspend => {
811 const nosuspend_node = @fieldParentPtr(Nosuspend, "base", n);
812 return !nosuspend_node.expr.tag.isBlock();
813 },
814 else => return true,
815 }
816 }
1449 pub fn ptrTypeAligned(tree: Tree, node: Node.Index) full.PtrType {
1450 assert(tree.nodes.items(.tag)[node] == .ptr_type_aligned);
1451 const data = tree.nodes.items(.data)[node];
1452 return tree.fullPtrType(.{
1453 .main_token = tree.nodes.items(.main_token)[node],
1454 .align_node = data.lhs,
1455 .sentinel = 0,
1456 .bit_range_start = 0,
1457 .bit_range_end = 0,
1458 .child_type = data.rhs,
1459 });
8171460 }
8181461
819 /// Asserts the node is a Block or LabeledBlock and returns the statements slice.
820 pub fn blockStatements(base: *Node) []*Node {
821 if (base.castTag(.Block)) |block| {
822 return block.statements();
823 } else if (base.castTag(.LabeledBlock)) |labeled_block| {
824 return labeled_block.statements();
825 } else {
826 unreachable;
827 }
1462 pub fn ptrTypeSentinel(tree: Tree, node: Node.Index) full.PtrType {
1463 assert(tree.nodes.items(.tag)[node] == .ptr_type_sentinel);
1464 const data = tree.nodes.items(.data)[node];
1465 return tree.fullPtrType(.{
1466 .main_token = tree.nodes.items(.main_token)[node],
1467 .align_node = 0,
1468 .sentinel = data.lhs,
1469 .bit_range_start = 0,
1470 .bit_range_end = 0,
1471 .child_type = data.rhs,
1472 });
8281473 }
8291474
830 pub fn findFirstWithId(self: *Node, id: Id) ?*Node {
831 if (self.id == id) return self;
832 var child_i: usize = 0;
833 while (self.iterate(child_i)) |child| : (child_i += 1) {
834 if (child.findFirstWithId(id)) |result| return result;
835 }
836 return null;
1475 pub fn ptrType(tree: Tree, node: Node.Index) full.PtrType {
1476 assert(tree.nodes.items(.tag)[node] == .ptr_type);
1477 const data = tree.nodes.items(.data)[node];
1478 const extra = tree.extraData(data.lhs, Node.PtrType);
1479 return tree.fullPtrType(.{
1480 .main_token = tree.nodes.items(.main_token)[node],
1481 .align_node = extra.align_node,
1482 .sentinel = extra.sentinel,
1483 .bit_range_start = 0,
1484 .bit_range_end = 0,
1485 .child_type = data.rhs,
1486 });
8371487 }
8381488
839 pub fn dump(self: *Node, indent: usize) void {
840 {
841 var i: usize = 0;
842 while (i < indent) : (i += 1) {
843 std.debug.warn(" ", .{});
844 }
845 }
846 std.debug.warn("{s}\n", .{@tagName(self.tag)});
1489 pub fn ptrTypeBitRange(tree: Tree, node: Node.Index) full.PtrType {
1490 assert(tree.nodes.items(.tag)[node] == .ptr_type_bit_range);
1491 const data = tree.nodes.items(.data)[node];
1492 const extra = tree.extraData(data.lhs, Node.PtrTypeBitRange);
1493 return tree.fullPtrType(.{
1494 .main_token = tree.nodes.items(.main_token)[node],
1495 .align_node = extra.align_node,
1496 .sentinel = extra.sentinel,
1497 .bit_range_start = extra.bit_range_start,
1498 .bit_range_end = extra.bit_range_end,
1499 .child_type = data.rhs,
1500 });
1501 }
8471502
848 var child_i: usize = 0;
849 while (self.iterate(child_i)) |child| : (child_i += 1) {
850 child.dump(indent + 2);
851 }
1503 pub fn sliceOpen(tree: Tree, node: Node.Index) full.Slice {
1504 assert(tree.nodes.items(.tag)[node] == .slice_open);
1505 const data = tree.nodes.items(.data)[node];
1506 return .{
1507 .ast = .{
1508 .sliced = data.lhs,
1509 .lbracket = tree.nodes.items(.main_token)[node],
1510 .start = data.rhs,
1511 .end = 0,
1512 .sentinel = 0,
1513 },
1514 };
8521515 }
8531516
854 /// The decls data follows this struct in memory as an array of Node pointers.
855 pub const Root = struct {
856 base: Node = Node{ .tag = .Root },
857 eof_token: TokenIndex,
858 decls_len: NodeIndex,
1517 pub fn slice(tree: Tree, node: Node.Index) full.Slice {
1518 assert(tree.nodes.items(.tag)[node] == .slice);
1519 const data = tree.nodes.items(.data)[node];
1520 const extra = tree.extraData(data.rhs, Node.Slice);
1521 return .{
1522 .ast = .{
1523 .sliced = data.lhs,
1524 .lbracket = tree.nodes.items(.main_token)[node],
1525 .start = extra.start,
1526 .end = extra.end,
1527 .sentinel = 0,
1528 },
1529 };
1530 }
8591531
860 /// After this the caller must initialize the decls list.
861 pub fn create(allocator: *mem.Allocator, decls_len: NodeIndex, eof_token: TokenIndex) !*Root {
862 const bytes = try allocator.alignedAlloc(u8, @alignOf(Root), sizeInBytes(decls_len));
863 const self = @ptrCast(*Root, bytes.ptr);
864 self.* = .{
865 .eof_token = eof_token,
866 .decls_len = decls_len,
867 };
868 return self;
869 }
1532 pub fn sliceSentinel(tree: Tree, node: Node.Index) full.Slice {
1533 assert(tree.nodes.items(.tag)[node] == .slice_sentinel);
1534 const data = tree.nodes.items(.data)[node];
1535 const extra = tree.extraData(data.rhs, Node.SliceSentinel);
1536 return .{
1537 .ast = .{
1538 .sliced = data.lhs,
1539 .lbracket = tree.nodes.items(.main_token)[node],
1540 .start = extra.start,
1541 .end = extra.end,
1542 .sentinel = extra.sentinel,
1543 },
1544 };
1545 }
8701546
871 pub fn destroy(self: *Decl, allocator: *mem.Allocator) void {
872 const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.decls_len)];
873 allocator.free(bytes);
874 }
1547 pub fn containerDeclTwo(tree: Tree, buffer: *[2]Node.Index, node: Node.Index) full.ContainerDecl {
1548 assert(tree.nodes.items(.tag)[node] == .container_decl_two or
1549 tree.nodes.items(.tag)[node] == .container_decl_two_trailing);
1550 const data = tree.nodes.items(.data)[node];
1551 buffer.* = .{ data.lhs, data.rhs };
1552 const members = if (data.rhs != 0)
1553 buffer[0..2]
1554 else if (data.lhs != 0)
1555 buffer[0..1]
1556 else
1557 buffer[0..0];
1558 return tree.fullContainerDecl(.{
1559 .main_token = tree.nodes.items(.main_token)[node],
1560 .enum_token = null,
1561 .members = members,
1562 .arg = 0,
1563 });
1564 }
8751565
876 pub fn iterate(self: *const Root, index: usize) ?*Node {
877 var i = index;
1566 pub fn containerDecl(tree: Tree, node: Node.Index) full.ContainerDecl {
1567 assert(tree.nodes.items(.tag)[node] == .container_decl or
1568 tree.nodes.items(.tag)[node] == .container_decl_trailing);
1569 const data = tree.nodes.items(.data)[node];
1570 return tree.fullContainerDecl(.{
1571 .main_token = tree.nodes.items(.main_token)[node],
1572 .enum_token = null,
1573 .members = tree.extra_data[data.lhs..data.rhs],
1574 .arg = 0,
1575 });
1576 }
8781577
879 if (i < self.decls_len) return self.declsConst()[i];
880 return null;
881 }
1578 pub fn containerDeclArg(tree: Tree, node: Node.Index) full.ContainerDecl {
1579 assert(tree.nodes.items(.tag)[node] == .container_decl_arg or
1580 tree.nodes.items(.tag)[node] == .container_decl_arg_trailing);
1581 const data = tree.nodes.items(.data)[node];
1582 const members_range = tree.extraData(data.rhs, Node.SubRange);
1583 return tree.fullContainerDecl(.{
1584 .main_token = tree.nodes.items(.main_token)[node],
1585 .enum_token = null,
1586 .members = tree.extra_data[members_range.start..members_range.end],
1587 .arg = data.lhs,
1588 });
1589 }
8821590
883 pub fn decls(self: *Root) []*Node {
884 const decls_start = @ptrCast([*]u8, self) + @sizeOf(Root);
885 return @ptrCast([*]*Node, decls_start)[0..self.decls_len];
886 }
1591 pub fn taggedUnionTwo(tree: Tree, buffer: *[2]Node.Index, node: Node.Index) full.ContainerDecl {
1592 assert(tree.nodes.items(.tag)[node] == .tagged_union_two or
1593 tree.nodes.items(.tag)[node] == .tagged_union_two_trailing);
1594 const data = tree.nodes.items(.data)[node];
1595 buffer.* = .{ data.lhs, data.rhs };
1596 const members = if (data.rhs != 0)
1597 buffer[0..2]
1598 else if (data.lhs != 0)
1599 buffer[0..1]
1600 else
1601 buffer[0..0];
1602 const main_token = tree.nodes.items(.main_token)[node];
1603 return tree.fullContainerDecl(.{
1604 .main_token = main_token,
1605 .enum_token = main_token + 2, // union lparen enum
1606 .members = members,
1607 .arg = 0,
1608 });
1609 }
8871610
888 pub fn declsConst(self: *const Root) []const *Node {
889 const decls_start = @ptrCast([*]const u8, self) + @sizeOf(Root);
890 return @ptrCast([*]const *Node, decls_start)[0..self.decls_len];
891 }
1611 pub fn taggedUnion(tree: Tree, node: Node.Index) full.ContainerDecl {
1612 assert(tree.nodes.items(.tag)[node] == .tagged_union or
1613 tree.nodes.items(.tag)[node] == .tagged_union_trailing);
1614 const data = tree.nodes.items(.data)[node];
1615 const main_token = tree.nodes.items(.main_token)[node];
1616 return tree.fullContainerDecl(.{
1617 .main_token = main_token,
1618 .enum_token = main_token + 2, // union lparen enum
1619 .members = tree.extra_data[data.lhs..data.rhs],
1620 .arg = 0,
1621 });
1622 }
8921623
893 pub fn firstToken(self: *const Root) TokenIndex {
894 if (self.decls_len == 0) return self.eof_token;
895 return self.declsConst()[0].firstToken();
896 }
1624 pub fn taggedUnionEnumTag(tree: Tree, node: Node.Index) full.ContainerDecl {
1625 assert(tree.nodes.items(.tag)[node] == .tagged_union_enum_tag or
1626 tree.nodes.items(.tag)[node] == .tagged_union_enum_tag_trailing);
1627 const data = tree.nodes.items(.data)[node];
1628 const members_range = tree.extraData(data.rhs, Node.SubRange);
1629 const main_token = tree.nodes.items(.main_token)[node];
1630 return tree.fullContainerDecl(.{
1631 .main_token = main_token,
1632 .enum_token = main_token + 2, // union lparen enum
1633 .members = tree.extra_data[members_range.start..members_range.end],
1634 .arg = data.lhs,
1635 });
1636 }
8971637
898 pub fn lastToken(self: *const Root) TokenIndex {
899 if (self.decls_len == 0) return self.eof_token;
900 return self.declsConst()[self.decls_len - 1].lastToken();
901 }
1638 pub fn switchCaseOne(tree: Tree, node: Node.Index) full.SwitchCase {
1639 const data = &tree.nodes.items(.data)[node];
1640 const values: *[1]Node.Index = &data.lhs;
1641 return tree.fullSwitchCase(.{
1642 .values = if (data.lhs == 0) values[0..0] else values[0..1],
1643 .arrow_token = tree.nodes.items(.main_token)[node],
1644 .target_expr = data.rhs,
1645 });
1646 }
9021647
903 fn sizeInBytes(decls_len: NodeIndex) usize {
904 return @sizeOf(Root) + @sizeOf(*Node) * @as(usize, decls_len);
905 }
906 };
1648 pub fn switchCase(tree: Tree, node: Node.Index) full.SwitchCase {
1649 const data = tree.nodes.items(.data)[node];
1650 const extra = tree.extraData(data.lhs, Node.SubRange);
1651 return tree.fullSwitchCase(.{
1652 .values = tree.extra_data[extra.start..extra.end],
1653 .arrow_token = tree.nodes.items(.main_token)[node],
1654 .target_expr = data.rhs,
1655 });
1656 }
9071657
908 /// Trailed in memory by possibly many things, with each optional thing
909 /// determined by a bit in `trailer_flags`.
910 pub const VarDecl = struct {
911 base: Node = Node{ .tag = .VarDecl },
912 trailer_flags: TrailerFlags,
913 mut_token: TokenIndex,
914 name_token: TokenIndex,
915 semicolon_token: TokenIndex,
916
917 pub const TrailerFlags = std.meta.TrailerFlags(struct {
918 doc_comments: *DocComment,
919 visib_token: TokenIndex,
920 thread_local_token: TokenIndex,
921 eq_token: TokenIndex,
922 comptime_token: TokenIndex,
923 extern_export_token: TokenIndex,
924 lib_name: *Node,
925 type_node: *Node,
926 align_node: *Node,
927 section_node: *Node,
928 init_node: *Node,
1658 pub fn asmSimple(tree: Tree, node: Node.Index) full.Asm {
1659 const data = tree.nodes.items(.data)[node];
1660 return tree.fullAsm(.{
1661 .asm_token = tree.nodes.items(.main_token)[node],
1662 .template = data.lhs,
1663 .items = &.{},
1664 .rparen = data.rhs,
9291665 });
1666 }
9301667
931 pub fn getDocComments(self: *const VarDecl) ?*DocComment {
932 return self.getTrailer(.doc_comments);
933 }
1668 pub fn asmFull(tree: Tree, node: Node.Index) full.Asm {
1669 const data = tree.nodes.items(.data)[node];
1670 const extra = tree.extraData(data.rhs, Node.Asm);
1671 return tree.fullAsm(.{
1672 .asm_token = tree.nodes.items(.main_token)[node],
1673 .template = data.lhs,
1674 .items = tree.extra_data[extra.items_start..extra.items_end],
1675 .rparen = extra.rparen,
1676 });
1677 }
9341678
935 pub fn setDocComments(self: *VarDecl, value: *DocComment) void {
936 self.setTrailer(.doc_comments, value);
937 }
1679 pub fn whileSimple(tree: Tree, node: Node.Index) full.While {
1680 const data = tree.nodes.items(.data)[node];
1681 return tree.fullWhile(.{
1682 .while_token = tree.nodes.items(.main_token)[node],
1683 .cond_expr = data.lhs,
1684 .cont_expr = 0,
1685 .then_expr = data.rhs,
1686 .else_expr = 0,
1687 });
1688 }
9381689
939 pub fn getVisibToken(self: *const VarDecl) ?TokenIndex {
940 return self.getTrailer(.visib_token);
941 }
1690 pub fn whileCont(tree: Tree, node: Node.Index) full.While {
1691 const data = tree.nodes.items(.data)[node];
1692 const extra = tree.extraData(data.rhs, Node.WhileCont);
1693 return tree.fullWhile(.{
1694 .while_token = tree.nodes.items(.main_token)[node],
1695 .cond_expr = data.lhs,
1696 .cont_expr = extra.cont_expr,
1697 .then_expr = extra.then_expr,
1698 .else_expr = 0,
1699 });
1700 }
9421701
943 pub fn setVisibToken(self: *VarDecl, value: TokenIndex) void {
944 self.setTrailer(.visib_token, value);
945 }
1702 pub fn whileFull(tree: Tree, node: Node.Index) full.While {
1703 const data = tree.nodes.items(.data)[node];
1704 const extra = tree.extraData(data.rhs, Node.While);
1705 return tree.fullWhile(.{
1706 .while_token = tree.nodes.items(.main_token)[node],
1707 .cond_expr = data.lhs,
1708 .cont_expr = extra.cont_expr,
1709 .then_expr = extra.then_expr,
1710 .else_expr = extra.else_expr,
1711 });
1712 }
9461713
947 pub fn getThreadLocalToken(self: *const VarDecl) ?TokenIndex {
948 return self.getTrailer(.thread_local_token);
949 }
1714 pub fn forSimple(tree: Tree, node: Node.Index) full.While {
1715 const data = tree.nodes.items(.data)[node];
1716 return tree.fullWhile(.{
1717 .while_token = tree.nodes.items(.main_token)[node],
1718 .cond_expr = data.lhs,
1719 .cont_expr = 0,
1720 .then_expr = data.rhs,
1721 .else_expr = 0,
1722 });
1723 }
9501724
951 pub fn setThreadLocalToken(self: *VarDecl, value: TokenIndex) void {
952 self.setTrailer(.thread_local_token, value);
953 }
1725 pub fn forFull(tree: Tree, node: Node.Index) full.While {
1726 const data = tree.nodes.items(.data)[node];
1727 const extra = tree.extraData(data.rhs, Node.If);
1728 return tree.fullWhile(.{
1729 .while_token = tree.nodes.items(.main_token)[node],
1730 .cond_expr = data.lhs,
1731 .cont_expr = 0,
1732 .then_expr = extra.then_expr,
1733 .else_expr = extra.else_expr,
1734 });
1735 }
9541736
955 pub fn getEqToken(self: *const VarDecl) ?TokenIndex {
956 return self.getTrailer(.eq_token);
957 }
1737 pub fn callOne(tree: Tree, buffer: *[1]Node.Index, node: Node.Index) full.Call {
1738 const data = tree.nodes.items(.data)[node];
1739 buffer.* = .{data.rhs};
1740 const params = if (data.rhs != 0) buffer[0..1] else buffer[0..0];
1741 return tree.fullCall(.{
1742 .lparen = tree.nodes.items(.main_token)[node],
1743 .fn_expr = data.lhs,
1744 .params = params,
1745 });
1746 }
9581747
959 pub fn setEqToken(self: *VarDecl, value: TokenIndex) void {
960 self.setTrailer(.eq_token, value);
961 }
1748 pub fn callFull(tree: Tree, node: Node.Index) full.Call {
1749 const data = tree.nodes.items(.data)[node];
1750 const extra = tree.extraData(data.rhs, Node.SubRange);
1751 return tree.fullCall(.{
1752 .lparen = tree.nodes.items(.main_token)[node],
1753 .fn_expr = data.lhs,
1754 .params = tree.extra_data[extra.start..extra.end],
1755 });
1756 }
9621757
963 pub fn getComptimeToken(self: *const VarDecl) ?TokenIndex {
964 return self.getTrailer(.comptime_token);
1758 fn fullVarDecl(tree: Tree, info: full.VarDecl.Ast) full.VarDecl {
1759 const token_tags = tree.tokens.items(.tag);
1760 var result: full.VarDecl = .{
1761 .ast = info,
1762 .visib_token = null,
1763 .extern_export_token = null,
1764 .lib_name = null,
1765 .threadlocal_token = null,
1766 .comptime_token = null,
1767 };
1768 var i = info.mut_token;
1769 while (i > 0) {
1770 i -= 1;
1771 switch (token_tags[i]) {
1772 .keyword_extern, .keyword_export => result.extern_export_token = i,
1773 .keyword_comptime => result.comptime_token = i,
1774 .keyword_pub => result.visib_token = i,
1775 .keyword_threadlocal => result.threadlocal_token = i,
1776 .string_literal => result.lib_name = i,
1777 else => break,
1778 }
9651779 }
1780 return result;
1781 }
9661782
967 pub fn setComptimeToken(self: *VarDecl, value: TokenIndex) void {
968 self.setTrailer(.comptime_token, value);
1783 fn fullIf(tree: Tree, info: full.If.Ast) full.If {
1784 const token_tags = tree.tokens.items(.tag);
1785 var result: full.If = .{
1786 .ast = info,
1787 .payload_token = null,
1788 .error_token = null,
1789 .else_token = undefined,
1790 };
1791 // if (cond_expr) |x|
1792 // ^ ^
1793 const payload_pipe = tree.lastToken(info.cond_expr) + 2;
1794 if (token_tags[payload_pipe] == .pipe) {
1795 result.payload_token = payload_pipe + 1;
9691796 }
970
971 pub fn getExternExportToken(self: *const VarDecl) ?TokenIndex {
972 return self.getTrailer(.extern_export_token);
1797 if (info.else_expr != 0) {
1798 // then_expr else |x|
1799 // ^ ^
1800 result.else_token = tree.lastToken(info.then_expr) + 1;
1801 if (token_tags[result.else_token + 1] == .pipe) {
1802 result.error_token = result.else_token + 2;
1803 }
9731804 }
1805 return result;
1806 }
9741807
975 pub fn setExternExportToken(self: *VarDecl, value: TokenIndex) void {
976 self.setTrailer(.extern_export_token, value);
1808 fn fullContainerField(tree: Tree, info: full.ContainerField.Ast) full.ContainerField {
1809 const token_tags = tree.tokens.items(.tag);
1810 var result: full.ContainerField = .{
1811 .ast = info,
1812 .comptime_token = null,
1813 };
1814 // comptime name: type = init,
1815 // ^
1816 if (info.name_token > 0 and token_tags[info.name_token - 1] == .keyword_comptime) {
1817 result.comptime_token = info.name_token - 1;
9771818 }
1819 return result;
1820 }
9781821
979 pub fn getLibName(self: *const VarDecl) ?*Node {
980 return self.getTrailer(.lib_name);
981 }
982
983 pub fn setLibName(self: *VarDecl, value: *Node) void {
984 self.setTrailer(.lib_name, value);
985 }
986
987 pub fn getTypeNode(self: *const VarDecl) ?*Node {
988 return self.getTrailer(.type_node);
989 }
990
991 pub fn setTypeNode(self: *VarDecl, value: *Node) void {
992 self.setTrailer(.type_node, value);
993 }
994
995 pub fn getAlignNode(self: *const VarDecl) ?*Node {
996 return self.getTrailer(.align_node);
997 }
998
999 pub fn setAlignNode(self: *VarDecl, value: *Node) void {
1000 self.setTrailer(.align_node, value);
1001 }
1002
1003 pub fn getSectionNode(self: *const VarDecl) ?*Node {
1004 return self.getTrailer(.section_node);
1005 }
1006
1007 pub fn setSectionNode(self: *VarDecl, value: *Node) void {
1008 self.setTrailer(.section_node, value);
1009 }
1010
1011 pub fn getInitNode(self: *const VarDecl) ?*Node {
1012 return self.getTrailer(.init_node);
1013 }
1014
1015 pub fn setInitNode(self: *VarDecl, value: *Node) void {
1016 self.setTrailer(.init_node, value);
1017 }
1018
1019 pub const RequiredFields = struct {
1020 mut_token: TokenIndex,
1021 name_token: TokenIndex,
1022 semicolon_token: TokenIndex,
1023 };
1024
1025 fn getTrailer(self: *const VarDecl, comptime field: TrailerFlags.FieldEnum) ?TrailerFlags.Field(field) {
1026 const trailers_start = @ptrCast([*]const u8, self) + @sizeOf(VarDecl);
1027 return self.trailer_flags.get(trailers_start, field);
1028 }
1029
1030 fn setTrailer(self: *VarDecl, comptime field: TrailerFlags.FieldEnum, value: TrailerFlags.Field(field)) void {
1031 const trailers_start = @ptrCast([*]u8, self) + @sizeOf(VarDecl);
1032 self.trailer_flags.set(trailers_start, field, value);
1033 }
1034
1035 pub fn create(allocator: *mem.Allocator, required: RequiredFields, trailers: TrailerFlags.InitStruct) !*VarDecl {
1036 const trailer_flags = TrailerFlags.init(trailers);
1037 const bytes = try allocator.alignedAlloc(u8, @alignOf(VarDecl), sizeInBytes(trailer_flags));
1038 const var_decl = @ptrCast(*VarDecl, bytes.ptr);
1039 var_decl.* = .{
1040 .trailer_flags = trailer_flags,
1041 .mut_token = required.mut_token,
1042 .name_token = required.name_token,
1043 .semicolon_token = required.semicolon_token,
1044 };
1045 const trailers_start = bytes.ptr + @sizeOf(VarDecl);
1046 trailer_flags.setMany(trailers_start, trailers);
1047 return var_decl;
1048 }
1049
1050 pub fn destroy(self: *VarDecl, allocator: *mem.Allocator) void {
1051 const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.trailer_flags)];
1052 allocator.free(bytes);
1053 }
1054
1055 pub fn iterate(self: *const VarDecl, index: usize) ?*Node {
1056 var i = index;
1057
1058 if (self.getTypeNode()) |type_node| {
1059 if (i < 1) return type_node;
1060 i -= 1;
1061 }
1062
1063 if (self.getAlignNode()) |align_node| {
1064 if (i < 1) return align_node;
1065 i -= 1;
1066 }
1067
1068 if (self.getSectionNode()) |section_node| {
1069 if (i < 1) return section_node;
1070 i -= 1;
1071 }
1072
1073 if (self.getInitNode()) |init_node| {
1074 if (i < 1) return init_node;
1075 i -= 1;
1076 }
1077
1078 return null;
1079 }
1080
1081 pub fn firstToken(self: *const VarDecl) TokenIndex {
1082 if (self.getVisibToken()) |visib_token| return visib_token;
1083 if (self.getThreadLocalToken()) |thread_local_token| return thread_local_token;
1084 if (self.getComptimeToken()) |comptime_token| return comptime_token;
1085 if (self.getExternExportToken()) |extern_export_token| return extern_export_token;
1086 assert(self.getLibName() == null);
1087 return self.mut_token;
1088 }
1089
1090 pub fn lastToken(self: *const VarDecl) TokenIndex {
1091 return self.semicolon_token;
1092 }
1093
1094 fn sizeInBytes(trailer_flags: TrailerFlags) usize {
1095 return @sizeOf(VarDecl) + trailer_flags.sizeInBytes();
1096 }
1097 };
1098
1099 pub const Use = struct {
1100 base: Node = Node{ .tag = .Use },
1101 doc_comments: ?*DocComment,
1102 visib_token: ?TokenIndex,
1103 use_token: TokenIndex,
1104 expr: *Node,
1105 semicolon_token: TokenIndex,
1106
1107 pub fn iterate(self: *const Use, index: usize) ?*Node {
1108 var i = index;
1109
1110 if (i < 1) return self.expr;
1111 i -= 1;
1112
1113 return null;
1114 }
1115
1116 pub fn firstToken(self: *const Use) TokenIndex {
1117 if (self.visib_token) |visib_token| return visib_token;
1118 return self.use_token;
1119 }
1120
1121 pub fn lastToken(self: *const Use) TokenIndex {
1122 return self.semicolon_token;
1123 }
1124 };
1125
1126 pub const ErrorSetDecl = struct {
1127 base: Node = Node{ .tag = .ErrorSetDecl },
1128 error_token: TokenIndex,
1129 rbrace_token: TokenIndex,
1130 decls_len: NodeIndex,
1131
1132 /// After this the caller must initialize the decls list.
1133 pub fn alloc(allocator: *mem.Allocator, decls_len: NodeIndex) !*ErrorSetDecl {
1134 const bytes = try allocator.alignedAlloc(u8, @alignOf(ErrorSetDecl), sizeInBytes(decls_len));
1135 return @ptrCast(*ErrorSetDecl, bytes.ptr);
1136 }
1137
1138 pub fn free(self: *ErrorSetDecl, allocator: *mem.Allocator) void {
1139 const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.decls_len)];
1140 allocator.free(bytes);
1141 }
1142
1143 pub fn iterate(self: *const ErrorSetDecl, index: usize) ?*Node {
1144 var i = index;
1145
1146 if (i < self.decls_len) return self.declsConst()[i];
1147 i -= self.decls_len;
1148
1149 return null;
1150 }
1151
1152 pub fn firstToken(self: *const ErrorSetDecl) TokenIndex {
1153 return self.error_token;
1154 }
1155
1156 pub fn lastToken(self: *const ErrorSetDecl) TokenIndex {
1157 return self.rbrace_token;
1158 }
1159
1160 pub fn decls(self: *ErrorSetDecl) []*Node {
1161 const decls_start = @ptrCast([*]u8, self) + @sizeOf(ErrorSetDecl);
1162 return @ptrCast([*]*Node, decls_start)[0..self.decls_len];
1163 }
1164
1165 pub fn declsConst(self: *const ErrorSetDecl) []const *Node {
1166 const decls_start = @ptrCast([*]const u8, self) + @sizeOf(ErrorSetDecl);
1167 return @ptrCast([*]const *Node, decls_start)[0..self.decls_len];
1168 }
1169
1170 fn sizeInBytes(decls_len: NodeIndex) usize {
1171 return @sizeOf(ErrorSetDecl) + @sizeOf(*Node) * @as(usize, decls_len);
1172 }
1173 };
1174
1175 /// The fields and decls Node pointers directly follow this struct in memory.
1176 pub const ContainerDecl = struct {
1177 base: Node = Node{ .tag = .ContainerDecl },
1178 kind_token: TokenIndex,
1179 layout_token: ?TokenIndex,
1180 lbrace_token: TokenIndex,
1181 rbrace_token: TokenIndex,
1182 fields_and_decls_len: NodeIndex,
1183 init_arg_expr: InitArg,
1184
1185 pub const InitArg = union(enum) {
1186 None,
1187 Enum: ?*Node,
1188 Type: *Node,
1189 };
1190
1191 /// After this the caller must initialize the fields_and_decls list.
1192 pub fn alloc(allocator: *mem.Allocator, fields_and_decls_len: NodeIndex) !*ContainerDecl {
1193 const bytes = try allocator.alignedAlloc(u8, @alignOf(ContainerDecl), sizeInBytes(fields_and_decls_len));
1194 return @ptrCast(*ContainerDecl, bytes.ptr);
1195 }
1196
1197 pub fn free(self: *ContainerDecl, allocator: *mem.Allocator) void {
1198 const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.fields_and_decls_len)];
1199 allocator.free(bytes);
1200 }
1201
1202 pub fn iterate(self: *const ContainerDecl, index: usize) ?*Node {
1203 var i = index;
1204
1205 switch (self.init_arg_expr) {
1206 .Type => |t| {
1207 if (i < 1) return t;
1208 i -= 1;
1209 },
1210 .None, .Enum => {},
1211 }
1212
1213 if (i < self.fields_and_decls_len) return self.fieldsAndDeclsConst()[i];
1214 i -= self.fields_and_decls_len;
1215
1216 return null;
1217 }
1218
1219 pub fn firstToken(self: *const ContainerDecl) TokenIndex {
1220 if (self.layout_token) |layout_token| {
1221 return layout_token;
1222 }
1223 return self.kind_token;
1224 }
1225
1226 pub fn lastToken(self: *const ContainerDecl) TokenIndex {
1227 return self.rbrace_token;
1228 }
1229
1230 pub fn fieldsAndDecls(self: *ContainerDecl) []*Node {
1231 const decls_start = @ptrCast([*]u8, self) + @sizeOf(ContainerDecl);
1232 return @ptrCast([*]*Node, decls_start)[0..self.fields_and_decls_len];
1233 }
1234
1235 pub fn fieldsAndDeclsConst(self: *const ContainerDecl) []const *Node {
1236 const decls_start = @ptrCast([*]const u8, self) + @sizeOf(ContainerDecl);
1237 return @ptrCast([*]const *Node, decls_start)[0..self.fields_and_decls_len];
1238 }
1239
1240 fn sizeInBytes(fields_and_decls_len: NodeIndex) usize {
1241 return @sizeOf(ContainerDecl) + @sizeOf(*Node) * @as(usize, fields_and_decls_len);
1242 }
1243 };
1244
1245 pub const ContainerField = struct {
1246 base: Node = Node{ .tag = .ContainerField },
1247 doc_comments: ?*DocComment,
1248 comptime_token: ?TokenIndex,
1249 name_token: TokenIndex,
1250 type_expr: ?*Node,
1251 value_expr: ?*Node,
1252 align_expr: ?*Node,
1253
1254 pub fn iterate(self: *const ContainerField, index: usize) ?*Node {
1255 var i = index;
1256
1257 if (self.type_expr) |type_expr| {
1258 if (i < 1) return type_expr;
1259 i -= 1;
1260 }
1261
1262 if (self.align_expr) |align_expr| {
1263 if (i < 1) return align_expr;
1264 i -= 1;
1265 }
1266
1267 if (self.value_expr) |value_expr| {
1268 if (i < 1) return value_expr;
1269 i -= 1;
1270 }
1271
1272 return null;
1273 }
1274
1275 pub fn firstToken(self: *const ContainerField) TokenIndex {
1276 return self.comptime_token orelse self.name_token;
1277 }
1278
1279 pub fn lastToken(self: *const ContainerField) TokenIndex {
1280 if (self.value_expr) |value_expr| {
1281 return value_expr.lastToken();
1282 }
1283 if (self.align_expr) |align_expr| {
1284 // The expression refers to what's inside the parenthesis, the
1285 // last token is the closing one
1286 return align_expr.lastToken() + 1;
1287 }
1288 if (self.type_expr) |type_expr| {
1289 return type_expr.lastToken();
1290 }
1291
1292 return self.name_token;
1293 }
1294 };
1295
1296 pub const ErrorTag = struct {
1297 base: Node = Node{ .tag = .ErrorTag },
1298 doc_comments: ?*DocComment,
1299 name_token: TokenIndex,
1300
1301 pub fn iterate(self: *const ErrorTag, index: usize) ?*Node {
1302 var i = index;
1303
1304 if (self.doc_comments) |comments| {
1305 if (i < 1) return &comments.base;
1306 i -= 1;
1307 }
1308
1309 return null;
1310 }
1311
1312 pub fn firstToken(self: *const ErrorTag) TokenIndex {
1313 return self.name_token;
1314 }
1315
1316 pub fn lastToken(self: *const ErrorTag) TokenIndex {
1317 return self.name_token;
1318 }
1319 };
1320
1321 pub const OneToken = struct {
1322 base: Node,
1323 token: TokenIndex,
1324
1325 pub fn iterate(self: *const OneToken, index: usize) ?*Node {
1326 return null;
1327 }
1328
1329 pub fn firstToken(self: *const OneToken) TokenIndex {
1330 return self.token;
1331 }
1332
1333 pub fn lastToken(self: *const OneToken) TokenIndex {
1334 return self.token;
1335 }
1336 };
1337
1338 /// The params are directly after the FnProto in memory.
1339 /// Next, each optional thing determined by a bit in `trailer_flags`.
1340 pub const FnProto = struct {
1341 base: Node = Node{ .tag = .FnProto },
1342 trailer_flags: TrailerFlags,
1343 fn_token: TokenIndex,
1344 params_len: NodeIndex,
1345 return_type: ReturnType,
1346
1347 pub const TrailerFlags = std.meta.TrailerFlags(struct {
1348 doc_comments: *DocComment,
1349 body_node: *Node,
1350 lib_name: *Node, // populated if this is an extern declaration
1351 align_expr: *Node, // populated if align(A) is present
1352 section_expr: *Node, // populated if linksection(A) is present
1353 callconv_expr: *Node, // populated if callconv(A) is present
1354 visib_token: TokenIndex,
1355 name_token: TokenIndex,
1356 var_args_token: TokenIndex,
1357 extern_export_inline_token: TokenIndex,
1358 is_extern_prototype: void, // TODO: Remove once extern fn rewriting is
1359 is_async: void, // TODO: remove once async fn rewriting is
1360 is_inline: void, // TODO: remove once inline fn rewriting is
1361 });
1362
1363 pub const RequiredFields = struct {
1364 fn_token: TokenIndex,
1365 params_len: NodeIndex,
1366 return_type: ReturnType,
1367 };
1368
1369 pub const ReturnType = union(enum) {
1370 Explicit: *Node,
1371 InferErrorSet: *Node,
1372 Invalid: TokenIndex,
1373 };
1374
1375 pub const ParamDecl = struct {
1376 doc_comments: ?*DocComment,
1377 comptime_token: ?TokenIndex,
1378 noalias_token: ?TokenIndex,
1379 name_token: ?TokenIndex,
1380 param_type: ParamType,
1381
1382 pub const ParamType = union(enum) {
1383 any_type: *Node,
1384 type_expr: *Node,
1385 };
1386
1387 pub fn iterate(self: *const ParamDecl, index: usize) ?*Node {
1388 var i = index;
1389
1390 if (i < 1) {
1391 switch (self.param_type) {
1392 .any_type, .type_expr => |node| return node,
1393 }
1394 }
1395 i -= 1;
1396
1397 return null;
1398 }
1399
1400 pub fn firstToken(self: *const ParamDecl) TokenIndex {
1401 if (self.comptime_token) |comptime_token| return comptime_token;
1402 if (self.noalias_token) |noalias_token| return noalias_token;
1403 if (self.name_token) |name_token| return name_token;
1404 switch (self.param_type) {
1405 .any_type, .type_expr => |node| return node.firstToken(),
1406 }
1407 }
1408
1409 pub fn lastToken(self: *const ParamDecl) TokenIndex {
1410 switch (self.param_type) {
1411 .any_type, .type_expr => |node| return node.lastToken(),
1412 }
1413 }
1414 };
1415
1416 /// For debugging purposes.
1417 pub fn dump(self: *const FnProto) void {
1418 const trailers_start = @alignCast(
1419 @alignOf(ParamDecl),
1420 @ptrCast([*]const u8, self) + @sizeOf(FnProto) + @sizeOf(ParamDecl) * self.params_len,
1421 );
1422 std.debug.print("{*} flags: {b} name_token: {s} {*} params_len: {d}\n", .{
1423 self,
1424 self.trailer_flags.bits,
1425 self.getNameToken(),
1426 self.trailer_flags.ptrConst(trailers_start, .name_token),
1427 self.params_len,
1428 });
1429 }
1430
1431 pub fn getDocComments(self: *const FnProto) ?*DocComment {
1432 return self.getTrailer(.doc_comments);
1433 }
1434
1435 pub fn setDocComments(self: *FnProto, value: *DocComment) void {
1436 self.setTrailer(.doc_comments, value);
1437 }
1438
1439 pub fn getBodyNode(self: *const FnProto) ?*Node {
1440 return self.getTrailer(.body_node);
1441 }
1442
1443 pub fn setBodyNode(self: *FnProto, value: *Node) void {
1444 self.setTrailer(.body_node, value);
1445 }
1446
1447 pub fn getLibName(self: *const FnProto) ?*Node {
1448 return self.getTrailer(.lib_name);
1449 }
1450
1451 pub fn setLibName(self: *FnProto, value: *Node) void {
1452 self.setTrailer(.lib_name, value);
1453 }
1454
1455 pub fn getAlignExpr(self: *const FnProto) ?*Node {
1456 return self.getTrailer(.align_expr);
1457 }
1458
1459 pub fn setAlignExpr(self: *FnProto, value: *Node) void {
1460 self.setTrailer(.align_expr, value);
1461 }
1462
1463 pub fn getSectionExpr(self: *const FnProto) ?*Node {
1464 return self.getTrailer(.section_expr);
1465 }
1466
1467 pub fn setSectionExpr(self: *FnProto, value: *Node) void {
1468 self.setTrailer(.section_expr, value);
1469 }
1470
1471 pub fn getCallconvExpr(self: *const FnProto) ?*Node {
1472 return self.getTrailer(.callconv_expr);
1473 }
1474
1475 pub fn setCallconvExpr(self: *FnProto, value: *Node) void {
1476 self.setTrailer(.callconv_expr, value);
1477 }
1478
1479 pub fn getVisibToken(self: *const FnProto) ?TokenIndex {
1480 return self.getTrailer(.visib_token);
1481 }
1482
1483 pub fn setVisibToken(self: *FnProto, value: TokenIndex) void {
1484 self.setTrailer(.visib_token, value);
1485 }
1486
1487 pub fn getNameToken(self: *const FnProto) ?TokenIndex {
1488 return self.getTrailer(.name_token);
1489 }
1490
1491 pub fn setNameToken(self: *FnProto, value: TokenIndex) void {
1492 self.setTrailer(.name_token, value);
1493 }
1494
1495 pub fn getVarArgsToken(self: *const FnProto) ?TokenIndex {
1496 return self.getTrailer(.var_args_token);
1497 }
1498
1499 pub fn setVarArgsToken(self: *FnProto, value: TokenIndex) void {
1500 self.setTrailer(.var_args_token, value);
1501 }
1502
1503 pub fn getExternExportInlineToken(self: *const FnProto) ?TokenIndex {
1504 return self.getTrailer(.extern_export_inline_token);
1505 }
1506
1507 pub fn setExternExportInlineToken(self: *FnProto, value: TokenIndex) void {
1508 self.setTrailer(.extern_export_inline_token, value);
1509 }
1510
1511 pub fn getIsExternPrototype(self: *const FnProto) ?void {
1512 return self.getTrailer(.is_extern_prototype);
1513 }
1514
1515 pub fn setIsExternPrototype(self: *FnProto, value: void) void {
1516 self.setTrailer(.is_extern_prototype, value);
1517 }
1518
1519 pub fn getIsAsync(self: *const FnProto) ?void {
1520 return self.getTrailer(.is_async);
1521 }
1522
1523 pub fn setIsAsync(self: *FnProto, value: void) void {
1524 self.setTrailer(.is_async, value);
1525 }
1526
1527 pub fn getIsInline(self: *const FnProto) ?void {
1528 return self.getTrailer(.is_inline);
1529 }
1530
1531 pub fn setIsInline(self: *FnProto, value: void) void {
1532 self.setTrailer(.is_inline, value);
1533 }
1534
1535 fn getTrailer(self: *const FnProto, comptime field: TrailerFlags.FieldEnum) ?TrailerFlags.Field(field) {
1536 const trailers_start = @alignCast(
1537 @alignOf(ParamDecl),
1538 @ptrCast([*]const u8, self) + @sizeOf(FnProto) + @sizeOf(ParamDecl) * self.params_len,
1539 );
1540 return self.trailer_flags.get(trailers_start, field);
1541 }
1542
1543 fn setTrailer(self: *FnProto, comptime field: TrailerFlags.FieldEnum, value: TrailerFlags.Field(field)) void {
1544 const trailers_start = @alignCast(
1545 @alignOf(ParamDecl),
1546 @ptrCast([*]u8, self) + @sizeOf(FnProto) + @sizeOf(ParamDecl) * self.params_len,
1547 );
1548 self.trailer_flags.set(trailers_start, field, value);
1549 }
1550
1551 /// After this the caller must initialize the params list.
1552 pub fn create(allocator: *mem.Allocator, required: RequiredFields, trailers: TrailerFlags.InitStruct) !*FnProto {
1553 const trailer_flags = TrailerFlags.init(trailers);
1554 const bytes = try allocator.alignedAlloc(u8, @alignOf(FnProto), sizeInBytes(
1555 required.params_len,
1556 trailer_flags,
1557 ));
1558 const fn_proto = @ptrCast(*FnProto, bytes.ptr);
1559 fn_proto.* = .{
1560 .trailer_flags = trailer_flags,
1561 .fn_token = required.fn_token,
1562 .params_len = required.params_len,
1563 .return_type = required.return_type,
1564 };
1565 const trailers_start = @alignCast(
1566 @alignOf(ParamDecl),
1567 bytes.ptr + @sizeOf(FnProto) + @sizeOf(ParamDecl) * required.params_len,
1568 );
1569 trailer_flags.setMany(trailers_start, trailers);
1570 return fn_proto;
1571 }
1572
1573 pub fn destroy(self: *FnProto, allocator: *mem.Allocator) void {
1574 const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.params_len, self.trailer_flags)];
1575 allocator.free(bytes);
1576 }
1577
1578 pub fn iterate(self: *const FnProto, index: usize) ?*Node {
1579 var i = index;
1580
1581 if (self.getLibName()) |lib_name| {
1582 if (i < 1) return lib_name;
1583 i -= 1;
1584 }
1585
1586 const params_len: usize = if (self.params_len == 0)
1587 0
1588 else switch (self.paramsConst()[self.params_len - 1].param_type) {
1589 .any_type, .type_expr => self.params_len,
1590 };
1591 if (i < params_len) {
1592 switch (self.paramsConst()[i].param_type) {
1593 .any_type => |n| return n,
1594 .type_expr => |n| return n,
1595 }
1596 }
1597 i -= params_len;
1598
1599 if (self.getAlignExpr()) |align_expr| {
1600 if (i < 1) return align_expr;
1601 i -= 1;
1602 }
1603
1604 if (self.getSectionExpr()) |section_expr| {
1605 if (i < 1) return section_expr;
1606 i -= 1;
1607 }
1608
1609 switch (self.return_type) {
1610 .Explicit, .InferErrorSet => |node| {
1611 if (i < 1) return node;
1612 i -= 1;
1613 },
1614 .Invalid => {},
1615 }
1616
1617 if (self.getBodyNode()) |body_node| {
1618 if (i < 1) return body_node;
1619 i -= 1;
1620 }
1621
1622 return null;
1623 }
1624
1625 pub fn firstToken(self: *const FnProto) TokenIndex {
1626 if (self.getVisibToken()) |visib_token| return visib_token;
1627 if (self.getExternExportInlineToken()) |extern_export_inline_token| return extern_export_inline_token;
1628 assert(self.getLibName() == null);
1629 return self.fn_token;
1630 }
1631
1632 pub fn lastToken(self: *const FnProto) TokenIndex {
1633 if (self.getBodyNode()) |body_node| return body_node.lastToken();
1634 switch (self.return_type) {
1635 .Explicit, .InferErrorSet => |node| return node.lastToken(),
1636 .Invalid => |tok| return tok,
1637 }
1638 }
1639
1640 pub fn params(self: *FnProto) []ParamDecl {
1641 const params_start = @ptrCast([*]u8, self) + @sizeOf(FnProto);
1642 return @ptrCast([*]ParamDecl, params_start)[0..self.params_len];
1643 }
1644
1645 pub fn paramsConst(self: *const FnProto) []const ParamDecl {
1646 const params_start = @ptrCast([*]const u8, self) + @sizeOf(FnProto);
1647 return @ptrCast([*]const ParamDecl, params_start)[0..self.params_len];
1648 }
1649
1650 fn sizeInBytes(params_len: NodeIndex, trailer_flags: TrailerFlags) usize {
1651 return @sizeOf(FnProto) + @sizeOf(ParamDecl) * @as(usize, params_len) + trailer_flags.sizeInBytes();
1652 }
1653 };
1654
1655 pub const AnyFrameType = struct {
1656 base: Node = Node{ .tag = .AnyFrameType },
1657 anyframe_token: TokenIndex,
1658 result: ?Result,
1659
1660 pub const Result = struct {
1661 arrow_token: TokenIndex,
1662 return_type: *Node,
1663 };
1664
1665 pub fn iterate(self: *const AnyFrameType, index: usize) ?*Node {
1666 var i = index;
1667
1668 if (self.result) |result| {
1669 if (i < 1) return result.return_type;
1670 i -= 1;
1671 }
1672
1673 return null;
1674 }
1675
1676 pub fn firstToken(self: *const AnyFrameType) TokenIndex {
1677 return self.anyframe_token;
1678 }
1679
1680 pub fn lastToken(self: *const AnyFrameType) TokenIndex {
1681 if (self.result) |result| return result.return_type.lastToken();
1682 return self.anyframe_token;
1683 }
1684 };
1685
1686 /// The statements of the block follow Block directly in memory.
1687 pub const Block = struct {
1688 base: Node = Node{ .tag = .Block },
1689 statements_len: NodeIndex,
1690 lbrace: TokenIndex,
1691 rbrace: TokenIndex,
1692
1693 /// After this the caller must initialize the statements list.
1694 pub fn alloc(allocator: *mem.Allocator, statements_len: NodeIndex) !*Block {
1695 const bytes = try allocator.alignedAlloc(u8, @alignOf(Block), sizeInBytes(statements_len));
1696 return @ptrCast(*Block, bytes.ptr);
1697 }
1698
1699 pub fn free(self: *Block, allocator: *mem.Allocator) void {
1700 const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.statements_len)];
1701 allocator.free(bytes);
1702 }
1703
1704 pub fn iterate(self: *const Block, index: usize) ?*Node {
1705 var i = index;
1706
1707 if (i < self.statements_len) return self.statementsConst()[i];
1708 i -= self.statements_len;
1709
1710 return null;
1711 }
1712
1713 pub fn firstToken(self: *const Block) TokenIndex {
1714 return self.lbrace;
1715 }
1716
1717 pub fn lastToken(self: *const Block) TokenIndex {
1718 return self.rbrace;
1719 }
1720
1721 pub fn statements(self: *Block) []*Node {
1722 const decls_start = @ptrCast([*]u8, self) + @sizeOf(Block);
1723 return @ptrCast([*]*Node, decls_start)[0..self.statements_len];
1724 }
1725
1726 pub fn statementsConst(self: *const Block) []const *Node {
1727 const decls_start = @ptrCast([*]const u8, self) + @sizeOf(Block);
1728 return @ptrCast([*]const *Node, decls_start)[0..self.statements_len];
1729 }
1730
1731 fn sizeInBytes(statements_len: NodeIndex) usize {
1732 return @sizeOf(Block) + @sizeOf(*Node) * @as(usize, statements_len);
1733 }
1734 };
1735
1736 /// The statements of the block follow LabeledBlock directly in memory.
1737 pub const LabeledBlock = struct {
1738 base: Node = Node{ .tag = .LabeledBlock },
1739 statements_len: NodeIndex,
1740 lbrace: TokenIndex,
1741 rbrace: TokenIndex,
1742 label: TokenIndex,
1743
1744 /// After this the caller must initialize the statements list.
1745 pub fn alloc(allocator: *mem.Allocator, statements_len: NodeIndex) !*LabeledBlock {
1746 const bytes = try allocator.alignedAlloc(u8, @alignOf(LabeledBlock), sizeInBytes(statements_len));
1747 return @ptrCast(*LabeledBlock, bytes.ptr);
1748 }
1749
1750 pub fn free(self: *LabeledBlock, allocator: *mem.Allocator) void {
1751 const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.statements_len)];
1752 allocator.free(bytes);
1753 }
1754
1755 pub fn iterate(self: *const LabeledBlock, index: usize) ?*Node {
1756 var i = index;
1757
1758 if (i < self.statements_len) return self.statementsConst()[i];
1759 i -= self.statements_len;
1760
1761 return null;
1762 }
1763
1764 pub fn firstToken(self: *const LabeledBlock) TokenIndex {
1765 return self.label;
1766 }
1767
1768 pub fn lastToken(self: *const LabeledBlock) TokenIndex {
1769 return self.rbrace;
1770 }
1771
1772 pub fn statements(self: *LabeledBlock) []*Node {
1773 const decls_start = @ptrCast([*]u8, self) + @sizeOf(LabeledBlock);
1774 return @ptrCast([*]*Node, decls_start)[0..self.statements_len];
1775 }
1776
1777 pub fn statementsConst(self: *const LabeledBlock) []const *Node {
1778 const decls_start = @ptrCast([*]const u8, self) + @sizeOf(LabeledBlock);
1779 return @ptrCast([*]const *Node, decls_start)[0..self.statements_len];
1780 }
1781
1782 fn sizeInBytes(statements_len: NodeIndex) usize {
1783 return @sizeOf(LabeledBlock) + @sizeOf(*Node) * @as(usize, statements_len);
1784 }
1785 };
1786
1787 pub const Defer = struct {
1788 base: Node = Node{ .tag = .Defer },
1789 defer_token: TokenIndex,
1790 payload: ?*Node,
1791 expr: *Node,
1792
1793 pub fn iterate(self: *const Defer, index: usize) ?*Node {
1794 var i = index;
1795
1796 if (i < 1) return self.expr;
1797 i -= 1;
1798
1799 return null;
1800 }
1801
1802 pub fn firstToken(self: *const Defer) TokenIndex {
1803 return self.defer_token;
1804 }
1805
1806 pub fn lastToken(self: *const Defer) TokenIndex {
1807 return self.expr.lastToken();
1808 }
1809 };
1810
1811 pub const Comptime = struct {
1812 base: Node = Node{ .tag = .Comptime },
1813 doc_comments: ?*DocComment,
1814 comptime_token: TokenIndex,
1815 expr: *Node,
1816
1817 pub fn iterate(self: *const Comptime, index: usize) ?*Node {
1818 var i = index;
1819
1820 if (i < 1) return self.expr;
1821 i -= 1;
1822
1823 return null;
1824 }
1825
1826 pub fn firstToken(self: *const Comptime) TokenIndex {
1827 return self.comptime_token;
1828 }
1829
1830 pub fn lastToken(self: *const Comptime) TokenIndex {
1831 return self.expr.lastToken();
1832 }
1833 };
1834
1835 pub const Nosuspend = struct {
1836 base: Node = Node{ .tag = .Nosuspend },
1837 nosuspend_token: TokenIndex,
1838 expr: *Node,
1839
1840 pub fn iterate(self: *const Nosuspend, index: usize) ?*Node {
1841 var i = index;
1842
1843 if (i < 1) return self.expr;
1844 i -= 1;
1845
1846 return null;
1847 }
1848
1849 pub fn firstToken(self: *const Nosuspend) TokenIndex {
1850 return self.nosuspend_token;
1851 }
1852
1853 pub fn lastToken(self: *const Nosuspend) TokenIndex {
1854 return self.expr.lastToken();
1855 }
1856 };
1857
1858 pub const Payload = struct {
1859 base: Node = Node{ .tag = .Payload },
1860 lpipe: TokenIndex,
1861 error_symbol: *Node,
1862 rpipe: TokenIndex,
1863
1864 pub fn iterate(self: *const Payload, index: usize) ?*Node {
1865 var i = index;
1866
1867 if (i < 1) return self.error_symbol;
1868 i -= 1;
1869
1870 return null;
1871 }
1872
1873 pub fn firstToken(self: *const Payload) TokenIndex {
1874 return self.lpipe;
1875 }
1876
1877 pub fn lastToken(self: *const Payload) TokenIndex {
1878 return self.rpipe;
1879 }
1880 };
1881
1882 pub const PointerPayload = struct {
1883 base: Node = Node{ .tag = .PointerPayload },
1884 lpipe: TokenIndex,
1885 ptr_token: ?TokenIndex,
1886 value_symbol: *Node,
1887 rpipe: TokenIndex,
1888
1889 pub fn iterate(self: *const PointerPayload, index: usize) ?*Node {
1890 var i = index;
1891
1892 if (i < 1) return self.value_symbol;
1893 i -= 1;
1894
1895 return null;
1896 }
1897
1898 pub fn firstToken(self: *const PointerPayload) TokenIndex {
1899 return self.lpipe;
1900 }
1901
1902 pub fn lastToken(self: *const PointerPayload) TokenIndex {
1903 return self.rpipe;
1904 }
1905 };
1906
1907 pub const PointerIndexPayload = struct {
1908 base: Node = Node{ .tag = .PointerIndexPayload },
1909 lpipe: TokenIndex,
1910 ptr_token: ?TokenIndex,
1911 value_symbol: *Node,
1912 index_symbol: ?*Node,
1913 rpipe: TokenIndex,
1914
1915 pub fn iterate(self: *const PointerIndexPayload, index: usize) ?*Node {
1916 var i = index;
1917
1918 if (i < 1) return self.value_symbol;
1919 i -= 1;
1920
1921 if (self.index_symbol) |index_symbol| {
1922 if (i < 1) return index_symbol;
1923 i -= 1;
1924 }
1925
1926 return null;
1927 }
1928
1929 pub fn firstToken(self: *const PointerIndexPayload) TokenIndex {
1930 return self.lpipe;
1931 }
1932
1933 pub fn lastToken(self: *const PointerIndexPayload) TokenIndex {
1934 return self.rpipe;
1935 }
1936 };
1937
1938 pub const Else = struct {
1939 base: Node = Node{ .tag = .Else },
1940 else_token: TokenIndex,
1941 payload: ?*Node,
1942 body: *Node,
1943
1944 pub fn iterate(self: *const Else, index: usize) ?*Node {
1945 var i = index;
1946
1947 if (self.payload) |payload| {
1948 if (i < 1) return payload;
1949 i -= 1;
1950 }
1951
1952 if (i < 1) return self.body;
1953 i -= 1;
1954
1955 return null;
1956 }
1957
1958 pub fn firstToken(self: *const Else) TokenIndex {
1959 return self.else_token;
1960 }
1961
1962 pub fn lastToken(self: *const Else) TokenIndex {
1963 return self.body.lastToken();
1964 }
1965 };
1966
1967 /// The cases node pointers are found in memory after Switch.
1968 /// They must be SwitchCase or SwitchElse nodes.
1969 pub const Switch = struct {
1970 base: Node = Node{ .tag = .Switch },
1971 switch_token: TokenIndex,
1972 rbrace: TokenIndex,
1973 cases_len: NodeIndex,
1974 expr: *Node,
1975
1976 /// After this the caller must initialize the fields_and_decls list.
1977 pub fn alloc(allocator: *mem.Allocator, cases_len: NodeIndex) !*Switch {
1978 const bytes = try allocator.alignedAlloc(u8, @alignOf(Switch), sizeInBytes(cases_len));
1979 return @ptrCast(*Switch, bytes.ptr);
1980 }
1981
1982 pub fn free(self: *Switch, allocator: *mem.Allocator) void {
1983 const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.cases_len)];
1984 allocator.free(bytes);
1985 }
1986
1987 pub fn iterate(self: *const Switch, index: usize) ?*Node {
1988 var i = index;
1989
1990 if (i < 1) return self.expr;
1991 i -= 1;
1992
1993 if (i < self.cases_len) return self.casesConst()[i];
1994 i -= self.cases_len;
1995
1996 return null;
1997 }
1998
1999 pub fn firstToken(self: *const Switch) TokenIndex {
2000 return self.switch_token;
2001 }
2002
2003 pub fn lastToken(self: *const Switch) TokenIndex {
2004 return self.rbrace;
2005 }
2006
2007 pub fn cases(self: *Switch) []*Node {
2008 const decls_start = @ptrCast([*]u8, self) + @sizeOf(Switch);
2009 return @ptrCast([*]*Node, decls_start)[0..self.cases_len];
2010 }
2011
2012 pub fn casesConst(self: *const Switch) []const *Node {
2013 const decls_start = @ptrCast([*]const u8, self) + @sizeOf(Switch);
2014 return @ptrCast([*]const *Node, decls_start)[0..self.cases_len];
2015 }
2016
2017 fn sizeInBytes(cases_len: NodeIndex) usize {
2018 return @sizeOf(Switch) + @sizeOf(*Node) * @as(usize, cases_len);
2019 }
2020 };
2021
2022 /// Items sub-nodes appear in memory directly following SwitchCase.
2023 pub const SwitchCase = struct {
2024 base: Node = Node{ .tag = .SwitchCase },
2025 arrow_token: TokenIndex,
2026 payload: ?*Node,
2027 expr: *Node,
2028 items_len: NodeIndex,
2029
2030 /// After this the caller must initialize the fields_and_decls list.
2031 pub fn alloc(allocator: *mem.Allocator, items_len: NodeIndex) !*SwitchCase {
2032 const bytes = try allocator.alignedAlloc(u8, @alignOf(SwitchCase), sizeInBytes(items_len));
2033 return @ptrCast(*SwitchCase, bytes.ptr);
2034 }
2035
2036 pub fn free(self: *SwitchCase, allocator: *mem.Allocator) void {
2037 const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.items_len)];
2038 allocator.free(bytes);
2039 }
2040
2041 pub fn iterate(self: *const SwitchCase, index: usize) ?*Node {
2042 var i = index;
2043
2044 if (i < self.items_len) return self.itemsConst()[i];
2045 i -= self.items_len;
2046
2047 if (self.payload) |payload| {
2048 if (i < 1) return payload;
2049 i -= 1;
2050 }
2051
2052 if (i < 1) return self.expr;
2053 i -= 1;
2054
2055 return null;
2056 }
2057
2058 pub fn firstToken(self: *const SwitchCase) TokenIndex {
2059 return self.itemsConst()[0].firstToken();
2060 }
2061
2062 pub fn lastToken(self: *const SwitchCase) TokenIndex {
2063 return self.expr.lastToken();
2064 }
2065
2066 pub fn items(self: *SwitchCase) []*Node {
2067 const decls_start = @ptrCast([*]u8, self) + @sizeOf(SwitchCase);
2068 return @ptrCast([*]*Node, decls_start)[0..self.items_len];
2069 }
2070
2071 pub fn itemsConst(self: *const SwitchCase) []const *Node {
2072 const decls_start = @ptrCast([*]const u8, self) + @sizeOf(SwitchCase);
2073 return @ptrCast([*]const *Node, decls_start)[0..self.items_len];
2074 }
2075
2076 fn sizeInBytes(items_len: NodeIndex) usize {
2077 return @sizeOf(SwitchCase) + @sizeOf(*Node) * @as(usize, items_len);
2078 }
2079 };
2080
2081 pub const SwitchElse = struct {
2082 base: Node = Node{ .tag = .SwitchElse },
2083 token: TokenIndex,
2084
2085 pub fn iterate(self: *const SwitchElse, index: usize) ?*Node {
2086 return null;
2087 }
2088
2089 pub fn firstToken(self: *const SwitchElse) TokenIndex {
2090 return self.token;
2091 }
2092
2093 pub fn lastToken(self: *const SwitchElse) TokenIndex {
2094 return self.token;
2095 }
2096 };
2097
2098 pub const While = struct {
2099 base: Node = Node{ .tag = .While },
2100 label: ?TokenIndex,
2101 inline_token: ?TokenIndex,
2102 while_token: TokenIndex,
2103 condition: *Node,
2104 payload: ?*Node,
2105 continue_expr: ?*Node,
2106 body: *Node,
2107 @"else": ?*Else,
2108
2109 pub fn iterate(self: *const While, index: usize) ?*Node {
2110 var i = index;
2111
2112 if (i < 1) return self.condition;
2113 i -= 1;
2114
2115 if (self.payload) |payload| {
2116 if (i < 1) return payload;
2117 i -= 1;
2118 }
2119
2120 if (self.continue_expr) |continue_expr| {
2121 if (i < 1) return continue_expr;
2122 i -= 1;
2123 }
2124
2125 if (i < 1) return self.body;
2126 i -= 1;
2127
2128 if (self.@"else") |@"else"| {
2129 if (i < 1) return &@"else".base;
2130 i -= 1;
2131 }
2132
2133 return null;
2134 }
2135
2136 pub fn firstToken(self: *const While) TokenIndex {
2137 if (self.label) |label| {
2138 return label;
2139 }
2140
2141 if (self.inline_token) |inline_token| {
2142 return inline_token;
2143 }
2144
2145 return self.while_token;
2146 }
2147
2148 pub fn lastToken(self: *const While) TokenIndex {
2149 if (self.@"else") |@"else"| {
2150 return @"else".body.lastToken();
2151 }
2152
2153 return self.body.lastToken();
2154 }
2155 };
2156
2157 pub const For = struct {
2158 base: Node = Node{ .tag = .For },
2159 label: ?TokenIndex,
2160 inline_token: ?TokenIndex,
2161 for_token: TokenIndex,
2162 array_expr: *Node,
2163 payload: *Node,
2164 body: *Node,
2165 @"else": ?*Else,
2166
2167 pub fn iterate(self: *const For, index: usize) ?*Node {
2168 var i = index;
2169
2170 if (i < 1) return self.array_expr;
2171 i -= 1;
2172
2173 if (i < 1) return self.payload;
2174 i -= 1;
2175
2176 if (i < 1) return self.body;
2177 i -= 1;
2178
2179 if (self.@"else") |@"else"| {
2180 if (i < 1) return &@"else".base;
2181 i -= 1;
2182 }
2183
2184 return null;
2185 }
2186
2187 pub fn firstToken(self: *const For) TokenIndex {
2188 if (self.label) |label| {
2189 return label;
2190 }
2191
2192 if (self.inline_token) |inline_token| {
2193 return inline_token;
2194 }
2195
2196 return self.for_token;
2197 }
2198
2199 pub fn lastToken(self: *const For) TokenIndex {
2200 if (self.@"else") |@"else"| {
2201 return @"else".body.lastToken();
2202 }
2203
2204 return self.body.lastToken();
2205 }
2206 };
2207
2208 pub const If = struct {
2209 base: Node = Node{ .tag = .If },
2210 if_token: TokenIndex,
2211 condition: *Node,
2212 payload: ?*Node,
2213 body: *Node,
2214 @"else": ?*Else,
2215
2216 pub fn iterate(self: *const If, index: usize) ?*Node {
2217 var i = index;
2218
2219 if (i < 1) return self.condition;
2220 i -= 1;
2221
2222 if (self.payload) |payload| {
2223 if (i < 1) return payload;
2224 i -= 1;
2225 }
2226
2227 if (i < 1) return self.body;
2228 i -= 1;
2229
2230 if (self.@"else") |@"else"| {
2231 if (i < 1) return &@"else".base;
2232 i -= 1;
2233 }
2234
2235 return null;
2236 }
2237
2238 pub fn firstToken(self: *const If) TokenIndex {
2239 return self.if_token;
2240 }
2241
2242 pub fn lastToken(self: *const If) TokenIndex {
2243 if (self.@"else") |@"else"| {
2244 return @"else".body.lastToken();
2245 }
2246
2247 return self.body.lastToken();
2248 }
2249 };
2250
2251 pub const Catch = struct {
2252 base: Node = Node{ .tag = .Catch },
2253 op_token: TokenIndex,
2254 lhs: *Node,
2255 rhs: *Node,
2256 payload: ?*Node,
2257
2258 pub fn iterate(self: *const Catch, index: usize) ?*Node {
2259 var i = index;
2260
2261 if (i < 1) return self.lhs;
2262 i -= 1;
2263
2264 if (self.payload) |payload| {
2265 if (i < 1) return payload;
2266 i -= 1;
2267 }
2268
2269 if (i < 1) return self.rhs;
2270 i -= 1;
2271
2272 return null;
2273 }
2274
2275 pub fn firstToken(self: *const Catch) TokenIndex {
2276 return self.lhs.firstToken();
2277 }
2278
2279 pub fn lastToken(self: *const Catch) TokenIndex {
2280 return self.rhs.lastToken();
2281 }
2282 };
2283
2284 pub const SimpleInfixOp = struct {
2285 base: Node,
2286 op_token: TokenIndex,
2287 lhs: *Node,
2288 rhs: *Node,
2289
2290 pub fn iterate(self: *const SimpleInfixOp, index: usize) ?*Node {
2291 var i = index;
2292
2293 if (i < 1) return self.lhs;
2294 i -= 1;
2295
2296 if (i < 1) return self.rhs;
2297 i -= 1;
2298
2299 return null;
2300 }
2301
2302 pub fn firstToken(self: *const SimpleInfixOp) TokenIndex {
2303 return self.lhs.firstToken();
2304 }
2305
2306 pub fn lastToken(self: *const SimpleInfixOp) TokenIndex {
2307 return self.rhs.lastToken();
2308 }
2309 };
2310
2311 pub const SimplePrefixOp = struct {
2312 base: Node,
2313 op_token: TokenIndex,
2314 rhs: *Node,
2315
2316 const Self = @This();
2317
2318 pub fn iterate(self: *const Self, index: usize) ?*Node {
2319 if (index == 0) return self.rhs;
2320 return null;
2321 }
2322
2323 pub fn firstToken(self: *const Self) TokenIndex {
2324 return self.op_token;
2325 }
2326
2327 pub fn lastToken(self: *const Self) TokenIndex {
2328 return self.rhs.lastToken();
2329 }
2330 };
2331
2332 pub const ArrayType = struct {
2333 base: Node = Node{ .tag = .ArrayType },
2334 op_token: TokenIndex,
2335 rhs: *Node,
2336 len_expr: *Node,
2337
2338 pub fn iterate(self: *const ArrayType, index: usize) ?*Node {
2339 var i = index;
2340
2341 if (i < 1) return self.len_expr;
2342 i -= 1;
2343
2344 if (i < 1) return self.rhs;
2345 i -= 1;
2346
2347 return null;
2348 }
2349
2350 pub fn firstToken(self: *const ArrayType) TokenIndex {
2351 return self.op_token;
2352 }
2353
2354 pub fn lastToken(self: *const ArrayType) TokenIndex {
2355 return self.rhs.lastToken();
2356 }
2357 };
2358
2359 pub const ArrayTypeSentinel = struct {
2360 base: Node = Node{ .tag = .ArrayTypeSentinel },
2361 op_token: TokenIndex,
2362 rhs: *Node,
2363 len_expr: *Node,
2364 sentinel: *Node,
2365
2366 pub fn iterate(self: *const ArrayTypeSentinel, index: usize) ?*Node {
2367 var i = index;
2368
2369 if (i < 1) return self.len_expr;
2370 i -= 1;
2371
2372 if (i < 1) return self.sentinel;
2373 i -= 1;
2374
2375 if (i < 1) return self.rhs;
2376 i -= 1;
2377
2378 return null;
2379 }
2380
2381 pub fn firstToken(self: *const ArrayTypeSentinel) TokenIndex {
2382 return self.op_token;
2383 }
2384
2385 pub fn lastToken(self: *const ArrayTypeSentinel) TokenIndex {
2386 return self.rhs.lastToken();
2387 }
2388 };
2389
2390 pub const PtrType = struct {
2391 base: Node = Node{ .tag = .PtrType },
2392 op_token: TokenIndex,
2393 rhs: *Node,
2394 /// TODO Add a u8 flags field to Node where it would otherwise be padding, and each bit represents
2395 /// one of these possibly-null things. Then we have them directly follow the PtrType in memory.
2396 ptr_info: PtrInfo = .{},
2397
2398 pub fn iterate(self: *const PtrType, index: usize) ?*Node {
2399 var i = index;
2400
2401 if (self.ptr_info.sentinel) |sentinel| {
2402 if (i < 1) return sentinel;
2403 i -= 1;
2404 }
2405
2406 if (self.ptr_info.align_info) |align_info| {
2407 if (i < 1) return align_info.node;
2408 i -= 1;
2409 }
2410
2411 if (i < 1) return self.rhs;
2412 i -= 1;
2413
2414 return null;
2415 }
2416
2417 pub fn firstToken(self: *const PtrType) TokenIndex {
2418 return self.op_token;
2419 }
2420
2421 pub fn lastToken(self: *const PtrType) TokenIndex {
2422 return self.rhs.lastToken();
2423 }
2424 };
2425
2426 pub const SliceType = struct {
2427 base: Node = Node{ .tag = .SliceType },
2428 op_token: TokenIndex,
2429 rhs: *Node,
2430 /// TODO Add a u8 flags field to Node where it would otherwise be padding, and each bit represents
2431 /// one of these possibly-null things. Then we have them directly follow the SliceType in memory.
2432 ptr_info: PtrInfo = .{},
2433
2434 pub fn iterate(self: *const SliceType, index: usize) ?*Node {
2435 var i = index;
2436
2437 if (self.ptr_info.sentinel) |sentinel| {
2438 if (i < 1) return sentinel;
2439 i -= 1;
2440 }
2441
2442 if (self.ptr_info.align_info) |align_info| {
2443 if (i < 1) return align_info.node;
2444 i -= 1;
2445 }
2446
2447 if (i < 1) return self.rhs;
2448 i -= 1;
2449
2450 return null;
2451 }
2452
2453 pub fn firstToken(self: *const SliceType) TokenIndex {
2454 return self.op_token;
2455 }
2456
2457 pub fn lastToken(self: *const SliceType) TokenIndex {
2458 return self.rhs.lastToken();
2459 }
2460 };
2461
2462 pub const FieldInitializer = struct {
2463 base: Node = Node{ .tag = .FieldInitializer },
2464 period_token: TokenIndex,
2465 name_token: TokenIndex,
2466 expr: *Node,
2467
2468 pub fn iterate(self: *const FieldInitializer, index: usize) ?*Node {
2469 var i = index;
2470
2471 if (i < 1) return self.expr;
2472 i -= 1;
2473
2474 return null;
2475 }
2476
2477 pub fn firstToken(self: *const FieldInitializer) TokenIndex {
2478 return self.period_token;
2479 }
2480
2481 pub fn lastToken(self: *const FieldInitializer) TokenIndex {
2482 return self.expr.lastToken();
2483 }
2484 };
2485
2486 /// Elements occur directly in memory after ArrayInitializer.
2487 pub const ArrayInitializer = struct {
2488 base: Node = Node{ .tag = .ArrayInitializer },
2489 rtoken: TokenIndex,
2490 list_len: NodeIndex,
2491 lhs: *Node,
2492
2493 /// After this the caller must initialize the fields_and_decls list.
2494 pub fn alloc(allocator: *mem.Allocator, list_len: NodeIndex) !*ArrayInitializer {
2495 const bytes = try allocator.alignedAlloc(u8, @alignOf(ArrayInitializer), sizeInBytes(list_len));
2496 return @ptrCast(*ArrayInitializer, bytes.ptr);
2497 }
2498
2499 pub fn free(self: *ArrayInitializer, allocator: *mem.Allocator) void {
2500 const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.list_len)];
2501 allocator.free(bytes);
2502 }
2503
2504 pub fn iterate(self: *const ArrayInitializer, index: usize) ?*Node {
2505 var i = index;
2506
2507 if (i < 1) return self.lhs;
2508 i -= 1;
2509
2510 if (i < self.list_len) return self.listConst()[i];
2511 i -= self.list_len;
2512
2513 return null;
2514 }
2515
2516 pub fn firstToken(self: *const ArrayInitializer) TokenIndex {
2517 return self.lhs.firstToken();
2518 }
2519
2520 pub fn lastToken(self: *const ArrayInitializer) TokenIndex {
2521 return self.rtoken;
2522 }
2523
2524 pub fn list(self: *ArrayInitializer) []*Node {
2525 const decls_start = @ptrCast([*]u8, self) + @sizeOf(ArrayInitializer);
2526 return @ptrCast([*]*Node, decls_start)[0..self.list_len];
2527 }
2528
2529 pub fn listConst(self: *const ArrayInitializer) []const *Node {
2530 const decls_start = @ptrCast([*]const u8, self) + @sizeOf(ArrayInitializer);
2531 return @ptrCast([*]const *Node, decls_start)[0..self.list_len];
2532 }
2533
2534 fn sizeInBytes(list_len: NodeIndex) usize {
2535 return @sizeOf(ArrayInitializer) + @sizeOf(*Node) * @as(usize, list_len);
2536 }
2537 };
2538
2539 /// Elements occur directly in memory after ArrayInitializerDot.
2540 pub const ArrayInitializerDot = struct {
2541 base: Node = Node{ .tag = .ArrayInitializerDot },
2542 dot: TokenIndex,
2543 rtoken: TokenIndex,
2544 list_len: NodeIndex,
2545
2546 /// After this the caller must initialize the fields_and_decls list.
2547 pub fn alloc(allocator: *mem.Allocator, list_len: NodeIndex) !*ArrayInitializerDot {
2548 const bytes = try allocator.alignedAlloc(u8, @alignOf(ArrayInitializerDot), sizeInBytes(list_len));
2549 return @ptrCast(*ArrayInitializerDot, bytes.ptr);
2550 }
2551
2552 pub fn free(self: *ArrayInitializerDot, allocator: *mem.Allocator) void {
2553 const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.list_len)];
2554 allocator.free(bytes);
2555 }
2556
2557 pub fn iterate(self: *const ArrayInitializerDot, index: usize) ?*Node {
2558 var i = index;
2559
2560 if (i < self.list_len) return self.listConst()[i];
2561 i -= self.list_len;
2562
2563 return null;
2564 }
2565
2566 pub fn firstToken(self: *const ArrayInitializerDot) TokenIndex {
2567 return self.dot;
2568 }
2569
2570 pub fn lastToken(self: *const ArrayInitializerDot) TokenIndex {
2571 return self.rtoken;
2572 }
2573
2574 pub fn list(self: *ArrayInitializerDot) []*Node {
2575 const decls_start = @ptrCast([*]u8, self) + @sizeOf(ArrayInitializerDot);
2576 return @ptrCast([*]*Node, decls_start)[0..self.list_len];
2577 }
2578
2579 pub fn listConst(self: *const ArrayInitializerDot) []const *Node {
2580 const decls_start = @ptrCast([*]const u8, self) + @sizeOf(ArrayInitializerDot);
2581 return @ptrCast([*]const *Node, decls_start)[0..self.list_len];
2582 }
2583
2584 fn sizeInBytes(list_len: NodeIndex) usize {
2585 return @sizeOf(ArrayInitializerDot) + @sizeOf(*Node) * @as(usize, list_len);
2586 }
2587 };
2588
2589 /// Elements occur directly in memory after StructInitializer.
2590 pub const StructInitializer = struct {
2591 base: Node = Node{ .tag = .StructInitializer },
2592 rtoken: TokenIndex,
2593 list_len: NodeIndex,
2594 lhs: *Node,
2595
2596 /// After this the caller must initialize the fields_and_decls list.
2597 pub fn alloc(allocator: *mem.Allocator, list_len: NodeIndex) !*StructInitializer {
2598 const bytes = try allocator.alignedAlloc(u8, @alignOf(StructInitializer), sizeInBytes(list_len));
2599 return @ptrCast(*StructInitializer, bytes.ptr);
2600 }
2601
2602 pub fn free(self: *StructInitializer, allocator: *mem.Allocator) void {
2603 const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.list_len)];
2604 allocator.free(bytes);
2605 }
2606
2607 pub fn iterate(self: *const StructInitializer, index: usize) ?*Node {
2608 var i = index;
2609
2610 if (i < 1) return self.lhs;
2611 i -= 1;
2612
2613 if (i < self.list_len) return self.listConst()[i];
2614 i -= self.list_len;
2615
2616 return null;
2617 }
2618
2619 pub fn firstToken(self: *const StructInitializer) TokenIndex {
2620 return self.lhs.firstToken();
2621 }
2622
2623 pub fn lastToken(self: *const StructInitializer) TokenIndex {
2624 return self.rtoken;
2625 }
2626
2627 pub fn list(self: *StructInitializer) []*Node {
2628 const decls_start = @ptrCast([*]u8, self) + @sizeOf(StructInitializer);
2629 return @ptrCast([*]*Node, decls_start)[0..self.list_len];
2630 }
2631
2632 pub fn listConst(self: *const StructInitializer) []const *Node {
2633 const decls_start = @ptrCast([*]const u8, self) + @sizeOf(StructInitializer);
2634 return @ptrCast([*]const *Node, decls_start)[0..self.list_len];
2635 }
2636
2637 fn sizeInBytes(list_len: NodeIndex) usize {
2638 return @sizeOf(StructInitializer) + @sizeOf(*Node) * @as(usize, list_len);
2639 }
2640 };
2641
2642 /// Elements occur directly in memory after StructInitializerDot.
2643 pub const StructInitializerDot = struct {
2644 base: Node = Node{ .tag = .StructInitializerDot },
2645 dot: TokenIndex,
2646 rtoken: TokenIndex,
2647 list_len: NodeIndex,
2648
2649 /// After this the caller must initialize the fields_and_decls list.
2650 pub fn alloc(allocator: *mem.Allocator, list_len: NodeIndex) !*StructInitializerDot {
2651 const bytes = try allocator.alignedAlloc(u8, @alignOf(StructInitializerDot), sizeInBytes(list_len));
2652 return @ptrCast(*StructInitializerDot, bytes.ptr);
2653 }
2654
2655 pub fn free(self: *StructInitializerDot, allocator: *mem.Allocator) void {
2656 const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.list_len)];
2657 allocator.free(bytes);
2658 }
2659
2660 pub fn iterate(self: *const StructInitializerDot, index: usize) ?*Node {
2661 var i = index;
2662
2663 if (i < self.list_len) return self.listConst()[i];
2664 i -= self.list_len;
2665
2666 return null;
2667 }
2668
2669 pub fn firstToken(self: *const StructInitializerDot) TokenIndex {
2670 return self.dot;
2671 }
2672
2673 pub fn lastToken(self: *const StructInitializerDot) TokenIndex {
2674 return self.rtoken;
2675 }
2676
2677 pub fn list(self: *StructInitializerDot) []*Node {
2678 const decls_start = @ptrCast([*]u8, self) + @sizeOf(StructInitializerDot);
2679 return @ptrCast([*]*Node, decls_start)[0..self.list_len];
2680 }
2681
2682 pub fn listConst(self: *const StructInitializerDot) []const *Node {
2683 const decls_start = @ptrCast([*]const u8, self) + @sizeOf(StructInitializerDot);
2684 return @ptrCast([*]const *Node, decls_start)[0..self.list_len];
2685 }
2686
2687 fn sizeInBytes(list_len: NodeIndex) usize {
2688 return @sizeOf(StructInitializerDot) + @sizeOf(*Node) * @as(usize, list_len);
2689 }
2690 };
2691
2692 /// Parameter nodes directly follow Call in memory.
2693 pub const Call = struct {
2694 base: Node = Node{ .tag = .Call },
2695 rtoken: TokenIndex,
2696 lhs: *Node,
2697 params_len: NodeIndex,
2698 async_token: ?TokenIndex,
2699
2700 /// After this the caller must initialize the fields_and_decls list.
2701 pub fn alloc(allocator: *mem.Allocator, params_len: NodeIndex) !*Call {
2702 const bytes = try allocator.alignedAlloc(u8, @alignOf(Call), sizeInBytes(params_len));
2703 return @ptrCast(*Call, bytes.ptr);
2704 }
2705
2706 pub fn free(self: *Call, allocator: *mem.Allocator) void {
2707 const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.params_len)];
2708 allocator.free(bytes);
2709 }
2710
2711 pub fn iterate(self: *const Call, index: usize) ?*Node {
2712 var i = index;
2713
2714 if (i < 1) return self.lhs;
2715 i -= 1;
2716
2717 if (i < self.params_len) return self.paramsConst()[i];
2718 i -= self.params_len;
2719
2720 return null;
2721 }
2722
2723 pub fn firstToken(self: *const Call) TokenIndex {
2724 if (self.async_token) |async_token| return async_token;
2725 return self.lhs.firstToken();
2726 }
2727
2728 pub fn lastToken(self: *const Call) TokenIndex {
2729 return self.rtoken;
2730 }
2731
2732 pub fn params(self: *Call) []*Node {
2733 const decls_start = @ptrCast([*]u8, self) + @sizeOf(Call);
2734 return @ptrCast([*]*Node, decls_start)[0..self.params_len];
2735 }
2736
2737 pub fn paramsConst(self: *const Call) []const *Node {
2738 const decls_start = @ptrCast([*]const u8, self) + @sizeOf(Call);
2739 return @ptrCast([*]const *Node, decls_start)[0..self.params_len];
2740 }
2741
2742 fn sizeInBytes(params_len: NodeIndex) usize {
2743 return @sizeOf(Call) + @sizeOf(*Node) * @as(usize, params_len);
2744 }
2745 };
2746
2747 pub const ArrayAccess = struct {
2748 base: Node = Node{ .tag = .ArrayAccess },
2749 rtoken: TokenIndex,
2750 lhs: *Node,
2751 index_expr: *Node,
2752
2753 pub fn iterate(self: *const ArrayAccess, index: usize) ?*Node {
2754 var i = index;
2755
2756 if (i < 1) return self.lhs;
2757 i -= 1;
2758
2759 if (i < 1) return self.index_expr;
1822 fn fullFnProto(tree: Tree, info: full.FnProto.Ast) full.FnProto {
1823 const token_tags = tree.tokens.items(.tag);
1824 var result: full.FnProto = .{
1825 .ast = info,
1826 .visib_token = null,
1827 .extern_export_token = null,
1828 .lib_name = null,
1829 .name_token = null,
1830 .lparen = undefined,
1831 };
1832 var i = info.fn_token;
1833 while (i > 0) {
27601834 i -= 1;
2761
2762 return null;
2763 }
2764
2765 pub fn firstToken(self: *const ArrayAccess) TokenIndex {
2766 return self.lhs.firstToken();
1835 switch (token_tags[i]) {
1836 .keyword_extern, .keyword_export => result.extern_export_token = i,
1837 .keyword_pub => result.visib_token = i,
1838 .string_literal => result.lib_name = i,
1839 else => break,
1840 }
27671841 }
2768
2769 pub fn lastToken(self: *const ArrayAccess) TokenIndex {
2770 return self.rtoken;
1842 const after_fn_token = info.fn_token + 1;
1843 if (token_tags[after_fn_token] == .identifier) {
1844 result.name_token = after_fn_token;
1845 result.lparen = after_fn_token + 1;
1846 } else {
1847 result.lparen = after_fn_token;
27711848 }
2772 };
1849 assert(token_tags[result.lparen] == .l_paren);
27731850
2774 pub const SimpleSuffixOp = struct {
2775 base: Node,
2776 rtoken: TokenIndex,
2777 lhs: *Node,
2778
2779 pub fn iterate(self: *const SimpleSuffixOp, index: usize) ?*Node {
2780 var i = index;
1851 return result;
1852 }
27811853
2782 if (i < 1) return self.lhs;
2783 i -= 1;
1854 fn fullStructInit(tree: Tree, info: full.StructInit.Ast) full.StructInit {
1855 const token_tags = tree.tokens.items(.tag);
1856 var result: full.StructInit = .{
1857 .ast = info,
1858 };
1859 return result;
1860 }
27841861
2785 return null;
1862 fn fullPtrType(tree: Tree, info: full.PtrType.Ast) full.PtrType {
1863 const token_tags = tree.tokens.items(.tag);
1864 // TODO: looks like stage1 isn't quite smart enough to handle enum
1865 // literals in some places here
1866 const Size = std.builtin.TypeInfo.Pointer.Size;
1867 const size: Size = switch (token_tags[info.main_token]) {
1868 .asterisk,
1869 .asterisk_asterisk,
1870 => switch (token_tags[info.main_token + 1]) {
1871 .r_bracket, .colon => .Many,
1872 .identifier => if (token_tags[info.main_token - 1] == .l_bracket) Size.C else .One,
1873 else => .One,
1874 },
1875 .l_bracket => Size.Slice,
1876 else => unreachable,
1877 };
1878 var result: full.PtrType = .{
1879 .size = size,
1880 .allowzero_token = null,
1881 .const_token = null,
1882 .volatile_token = null,
1883 .ast = info,
1884 };
1885 // We need to be careful that we don't iterate over any sub-expressions
1886 // here while looking for modifiers as that could result in false
1887 // positives. Therefore, start after a sentinel if there is one and
1888 // skip over any align node and bit range nodes.
1889 var i = if (info.sentinel != 0) tree.lastToken(info.sentinel) + 1 else info.main_token;
1890 const end = tree.firstToken(info.child_type);
1891 while (i < end) : (i += 1) {
1892 switch (token_tags[i]) {
1893 .keyword_allowzero => result.allowzero_token = i,
1894 .keyword_const => result.const_token = i,
1895 .keyword_volatile => result.volatile_token = i,
1896 .keyword_align => {
1897 assert(info.align_node != 0);
1898 if (info.bit_range_end != 0) {
1899 assert(info.bit_range_start != 0);
1900 i = tree.lastToken(info.bit_range_end) + 1;
1901 } else {
1902 i = tree.lastToken(info.align_node) + 1;
1903 }
1904 },
1905 else => {},
1906 }
27861907 }
1908 return result;
1909 }
27871910
2788 pub fn firstToken(self: *const SimpleSuffixOp) TokenIndex {
2789 return self.lhs.firstToken();
1911 fn fullContainerDecl(tree: Tree, info: full.ContainerDecl.Ast) full.ContainerDecl {
1912 const token_tags = tree.tokens.items(.tag);
1913 var result: full.ContainerDecl = .{
1914 .ast = info,
1915 .layout_token = null,
1916 };
1917 switch (token_tags[info.main_token - 1]) {
1918 .keyword_extern, .keyword_packed => result.layout_token = info.main_token - 1,
1919 else => {},
27901920 }
1921 return result;
1922 }
27911923
2792 pub fn lastToken(self: *const SimpleSuffixOp) TokenIndex {
2793 return self.rtoken;
1924 fn fullSwitchCase(tree: Tree, info: full.SwitchCase.Ast) full.SwitchCase {
1925 const token_tags = tree.tokens.items(.tag);
1926 var result: full.SwitchCase = .{
1927 .ast = info,
1928 .payload_token = null,
1929 };
1930 if (token_tags[info.arrow_token + 1] == .pipe) {
1931 result.payload_token = info.arrow_token + 2;
27941932 }
2795 };
2796
2797 pub const Slice = struct {
2798 base: Node = Node{ .tag = .Slice },
2799 rtoken: TokenIndex,
2800 lhs: *Node,
2801 start: *Node,
2802 end: ?*Node,
2803 sentinel: ?*Node,
2804
2805 pub fn iterate(self: *const Slice, index: usize) ?*Node {
2806 var i = index;
2807
2808 if (i < 1) return self.lhs;
2809 i -= 1;
2810
2811 if (i < 1) return self.start;
2812 i -= 1;
1933 return result;
1934 }
28131935
2814 if (self.end) |end| {
2815 if (i < 1) return end;
2816 i -= 1;
1936 fn fullAsm(tree: Tree, info: full.Asm.Ast) full.Asm {
1937 const token_tags = tree.tokens.items(.tag);
1938 const node_tags = tree.nodes.items(.tag);
1939 var result: full.Asm = .{
1940 .ast = info,
1941 .volatile_token = null,
1942 .inputs = &.{},
1943 .outputs = &.{},
1944 .first_clobber = null,
1945 };
1946 if (token_tags[info.asm_token + 1] == .keyword_volatile) {
1947 result.volatile_token = info.asm_token + 1;
1948 }
1949 const outputs_end: usize = for (info.items) |item, i| {
1950 switch (node_tags[item]) {
1951 .asm_output => continue,
1952 else => break i,
1953 }
1954 } else info.items.len;
1955
1956 result.outputs = info.items[0..outputs_end];
1957 result.inputs = info.items[outputs_end..];
1958
1959 if (info.items.len == 0) {
1960 // asm ("foo" ::: "a", "b");
1961 const template_token = tree.lastToken(info.template);
1962 if (token_tags[template_token + 1] == .colon and
1963 token_tags[template_token + 2] == .colon and
1964 token_tags[template_token + 3] == .colon and
1965 token_tags[template_token + 4] == .string_literal)
1966 {
1967 result.first_clobber = template_token + 4;
1968 }
1969 } else if (result.inputs.len != 0) {
1970 // asm ("foo" :: [_] "" (y) : "a", "b");
1971 const last_input = result.inputs[result.inputs.len - 1];
1972 const rparen = tree.lastToken(last_input);
1973 if (token_tags[rparen + 1] == .colon and
1974 token_tags[rparen + 2] == .string_literal)
1975 {
1976 result.first_clobber = rparen + 2;
28171977 }
2818 if (self.sentinel) |sentinel| {
2819 if (i < 1) return sentinel;
2820 i -= 1;
1978 } else {
1979 // asm ("foo" : [_] "" (x) :: "a", "b");
1980 const last_output = result.outputs[result.outputs.len - 1];
1981 const rparen = tree.lastToken(last_output);
1982 if (token_tags[rparen + 1] == .colon and
1983 token_tags[rparen + 2] == .colon and
1984 token_tags[rparen + 3] == .string_literal)
1985 {
1986 result.first_clobber = rparen + 3;
28211987 }
2822
2823 return null;
2824 }
2825
2826 pub fn firstToken(self: *const Slice) TokenIndex {
2827 return self.lhs.firstToken();
2828 }
2829
2830 pub fn lastToken(self: *const Slice) TokenIndex {
2831 return self.rtoken;
2832 }
2833 };
2834
2835 pub const GroupedExpression = struct {
2836 base: Node = Node{ .tag = .GroupedExpression },
2837 lparen: TokenIndex,
2838 expr: *Node,
2839 rparen: TokenIndex,
2840
2841 pub fn iterate(self: *const GroupedExpression, index: usize) ?*Node {
2842 var i = index;
2843
2844 if (i < 1) return self.expr;
2845 i -= 1;
2846
2847 return null;
2848 }
2849
2850 pub fn firstToken(self: *const GroupedExpression) TokenIndex {
2851 return self.lparen;
2852 }
2853
2854 pub fn lastToken(self: *const GroupedExpression) TokenIndex {
2855 return self.rparen;
28561988 }
2857 };
28581989
2859 /// Trailed in memory by possibly many things, with each optional thing
2860 /// determined by a bit in `trailer_flags`.
2861 /// Can be: return, break, continue
2862 pub const ControlFlowExpression = struct {
2863 base: Node,
2864 trailer_flags: TrailerFlags,
2865 ltoken: TokenIndex,
2866
2867 pub const TrailerFlags = std.meta.TrailerFlags(struct {
2868 rhs: *Node,
2869 label: TokenIndex,
2870 });
1990 return result;
1991 }
28711992
2872 pub const RequiredFields = struct {
2873 tag: Tag,
2874 ltoken: TokenIndex,
1993 fn fullWhile(tree: Tree, info: full.While.Ast) full.While {
1994 const token_tags = tree.tokens.items(.tag);
1995 var result: full.While = .{
1996 .ast = info,
1997 .inline_token = null,
1998 .label_token = null,
1999 .payload_token = null,
2000 .else_token = undefined,
2001 .error_token = null,
28752002 };
2876
2877 pub fn getRHS(self: *const ControlFlowExpression) ?*Node {
2878 return self.getTrailer(.rhs);
2879 }
2880
2881 pub fn setRHS(self: *ControlFlowExpression, value: *Node) void {
2882 self.setTrailer(.rhs, value);
2883 }
2884
2885 pub fn getLabel(self: *const ControlFlowExpression) ?TokenIndex {
2886 return self.getTrailer(.label);
2887 }
2888
2889 pub fn setLabel(self: *ControlFlowExpression, value: TokenIndex) void {
2890 self.setTrailer(.label, value);
2891 }
2892
2893 fn getTrailer(self: *const ControlFlowExpression, comptime field: TrailerFlags.FieldEnum) ?TrailerFlags.Field(field) {
2894 const trailers_start = @ptrCast([*]const u8, self) + @sizeOf(ControlFlowExpression);
2895 return self.trailer_flags.get(trailers_start, field);
2896 }
2897
2898 fn setTrailer(self: *ControlFlowExpression, comptime field: TrailerFlags.FieldEnum, value: TrailerFlags.Field(field)) void {
2899 const trailers_start = @ptrCast([*]u8, self) + @sizeOf(ControlFlowExpression);
2900 self.trailer_flags.set(trailers_start, field, value);
2003 var tok_i = info.while_token - 1;
2004 if (token_tags[tok_i] == .keyword_inline) {
2005 result.inline_token = tok_i;
2006 tok_i -= 1;
29012007 }
2902
2903 pub fn create(allocator: *mem.Allocator, required: RequiredFields, trailers: TrailerFlags.InitStruct) !*ControlFlowExpression {
2904 const trailer_flags = TrailerFlags.init(trailers);
2905 const bytes = try allocator.alignedAlloc(u8, @alignOf(ControlFlowExpression), sizeInBytes(trailer_flags));
2906 const ctrl_flow_expr = @ptrCast(*ControlFlowExpression, bytes.ptr);
2907 ctrl_flow_expr.* = .{
2908 .base = .{ .tag = required.tag },
2909 .trailer_flags = trailer_flags,
2910 .ltoken = required.ltoken,
2911 };
2912 const trailers_start = bytes.ptr + @sizeOf(ControlFlowExpression);
2913 trailer_flags.setMany(trailers_start, trailers);
2914 return ctrl_flow_expr;
2008 if (token_tags[tok_i] == .colon and
2009 token_tags[tok_i - 1] == .identifier)
2010 {
2011 result.label_token = tok_i - 1;
29152012 }
2916
2917 pub fn destroy(self: *ControlFlowExpression, allocator: *mem.Allocator) void {
2918 const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.trailer_flags)];
2919 allocator.free(bytes);
2013 const last_cond_token = tree.lastToken(info.cond_expr);
2014 if (token_tags[last_cond_token + 2] == .pipe) {
2015 result.payload_token = last_cond_token + 3;
29202016 }
2921
2922 pub fn iterate(self: *const ControlFlowExpression, index: usize) ?*Node {
2923 var i = index;
2924
2925 if (self.getRHS()) |rhs| {
2926 if (i < 1) return rhs;
2927 i -= 1;
2017 if (info.else_expr != 0) {
2018 // then_expr else |x|
2019 // ^ ^
2020 result.else_token = tree.lastToken(info.then_expr) + 1;
2021 if (token_tags[result.else_token + 1] == .pipe) {
2022 result.error_token = result.else_token + 2;
29282023 }
2929
2930 return null;
29312024 }
2025 return result;
2026 }
29322027
2933 pub fn firstToken(self: *const ControlFlowExpression) TokenIndex {
2934 return self.ltoken;
2028 fn fullCall(tree: Tree, info: full.Call.Ast) full.Call {
2029 const token_tags = tree.tokens.items(.tag);
2030 var result: full.Call = .{
2031 .ast = info,
2032 .async_token = null,
2033 };
2034 const maybe_async_token = tree.firstToken(info.fn_expr) - 1;
2035 if (token_tags[maybe_async_token] == .keyword_async) {
2036 result.async_token = maybe_async_token;
29352037 }
2038 return result;
2039 }
2040};
29362041
2937 pub fn lastToken(self: *const ControlFlowExpression) TokenIndex {
2938 if (self.getRHS()) |rhs| {
2939 return rhs.lastToken();
2940 }
2941
2942 if (self.getLabel()) |label| {
2943 return label;
2944 }
2945
2946 return self.ltoken;
2947 }
2042/// Fully assembled AST node information.
2043pub const full = struct {
2044 pub const VarDecl = struct {
2045 visib_token: ?TokenIndex,
2046 extern_export_token: ?TokenIndex,
2047 lib_name: ?TokenIndex,
2048 threadlocal_token: ?TokenIndex,
2049 comptime_token: ?TokenIndex,
2050 ast: Ast,
29482051
2949 fn sizeInBytes(trailer_flags: TrailerFlags) usize {
2950 return @sizeOf(ControlFlowExpression) + trailer_flags.sizeInBytes();
2951 }
2052 pub const Ast = struct {
2053 mut_token: TokenIndex,
2054 type_node: Node.Index,
2055 align_node: Node.Index,
2056 section_node: Node.Index,
2057 init_node: Node.Index,
2058 };
29522059 };
29532060
2954 pub const Suspend = struct {
2955 base: Node = Node{ .tag = .Suspend },
2956 suspend_token: TokenIndex,
2957 body: ?*Node,
2958
2959 pub fn iterate(self: *const Suspend, index: usize) ?*Node {
2960 var i = index;
2961
2962 if (self.body) |body| {
2963 if (i < 1) return body;
2964 i -= 1;
2965 }
2966
2967 return null;
2968 }
2969
2970 pub fn firstToken(self: *const Suspend) TokenIndex {
2971 return self.suspend_token;
2972 }
2973
2974 pub fn lastToken(self: *const Suspend) TokenIndex {
2975 if (self.body) |body| {
2976 return body.lastToken();
2977 }
2061 pub const If = struct {
2062 /// Points to the first token after the `|`. Will either be an identifier or
2063 /// a `*` (with an identifier immediately after it).
2064 payload_token: ?TokenIndex,
2065 /// Points to the identifier after the `|`.
2066 error_token: ?TokenIndex,
2067 /// Populated only if else_expr != 0.
2068 else_token: TokenIndex,
2069 ast: Ast,
29782070
2979 return self.suspend_token;
2980 }
2071 pub const Ast = struct {
2072 if_token: TokenIndex,
2073 cond_expr: Node.Index,
2074 then_expr: Node.Index,
2075 else_expr: Node.Index,
2076 };
29812077 };
29822078
2983 pub const EnumLiteral = struct {
2984 base: Node = Node{ .tag = .EnumLiteral },
2985 dot: TokenIndex,
2986 name: TokenIndex,
2987
2988 pub fn iterate(self: *const EnumLiteral, index: usize) ?*Node {
2989 return null;
2990 }
2991
2992 pub fn firstToken(self: *const EnumLiteral) TokenIndex {
2993 return self.dot;
2994 }
2079 pub const While = struct {
2080 ast: Ast,
2081 inline_token: ?TokenIndex,
2082 label_token: ?TokenIndex,
2083 payload_token: ?TokenIndex,
2084 error_token: ?TokenIndex,
2085 /// Populated only if else_expr != 0.
2086 else_token: TokenIndex,
29952087
2996 pub fn lastToken(self: *const EnumLiteral) TokenIndex {
2997 return self.name;
2998 }
2088 pub const Ast = struct {
2089 while_token: TokenIndex,
2090 cond_expr: Node.Index,
2091 cont_expr: Node.Index,
2092 then_expr: Node.Index,
2093 else_expr: Node.Index,
2094 };
29992095 };
30002096
3001 /// Parameters are in memory following BuiltinCall.
3002 pub const BuiltinCall = struct {
3003 base: Node = Node{ .tag = .BuiltinCall },
3004 params_len: NodeIndex,
3005 builtin_token: TokenIndex,
3006 rparen_token: TokenIndex,
3007
3008 /// After this the caller must initialize the fields_and_decls list.
3009 pub fn alloc(allocator: *mem.Allocator, params_len: NodeIndex) !*BuiltinCall {
3010 const bytes = try allocator.alignedAlloc(u8, @alignOf(BuiltinCall), sizeInBytes(params_len));
3011 return @ptrCast(*BuiltinCall, bytes.ptr);
3012 }
3013
3014 pub fn free(self: *BuiltinCall, allocator: *mem.Allocator) void {
3015 const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.params_len)];
3016 allocator.free(bytes);
3017 }
2097 pub const ContainerField = struct {
2098 comptime_token: ?TokenIndex,
2099 ast: Ast,
30182100
3019 pub fn iterate(self: *const BuiltinCall, index: usize) ?*Node {
3020 var i = index;
2101 pub const Ast = struct {
2102 name_token: TokenIndex,
2103 type_expr: Node.Index,
2104 value_expr: Node.Index,
2105 align_expr: Node.Index,
2106 };
2107 };
30212108
3022 if (i < self.params_len) return self.paramsConst()[i];
3023 i -= self.params_len;
2109 pub const FnProto = struct {
2110 visib_token: ?TokenIndex,
2111 extern_export_token: ?TokenIndex,
2112 lib_name: ?TokenIndex,
2113 name_token: ?TokenIndex,
2114 lparen: TokenIndex,
2115 ast: Ast,
30242116
3025 return null;
3026 }
2117 pub const Ast = struct {
2118 fn_token: TokenIndex,
2119 return_type: Node.Index,
2120 params: []const Node.Index,
2121 align_expr: Node.Index,
2122 section_expr: Node.Index,
2123 callconv_expr: Node.Index,
2124 };
30272125
3028 pub fn firstToken(self: *const BuiltinCall) TokenIndex {
3029 return self.builtin_token;
3030 }
2126 pub const Param = struct {
2127 first_doc_comment: ?TokenIndex,
2128 name_token: ?TokenIndex,
2129 comptime_noalias: ?TokenIndex,
2130 anytype_ellipsis3: ?TokenIndex,
2131 type_expr: Node.Index,
2132 };
30312133
3032 pub fn lastToken(self: *const BuiltinCall) TokenIndex {
3033 return self.rparen_token;
3034 }
2134 /// Abstracts over the fact that anytype and ... are not included
2135 /// in the params slice, since they are simple identifiers and
2136 /// not sub-expressions.
2137 pub const Iterator = struct {
2138 tree: *const Tree,
2139 fn_proto: *const FnProto,
2140 param_i: usize,
2141 tok_i: TokenIndex,
2142 tok_flag: bool,
2143
2144 pub fn next(it: *Iterator) ?Param {
2145 const token_tags = it.tree.tokens.items(.tag);
2146 while (true) {
2147 var first_doc_comment: ?TokenIndex = null;
2148 var comptime_noalias: ?TokenIndex = null;
2149 var name_token: ?TokenIndex = null;
2150 if (!it.tok_flag) {
2151 if (it.param_i >= it.fn_proto.ast.params.len) {
2152 return null;
2153 }
2154 const param_type = it.fn_proto.ast.params[it.param_i];
2155 var tok_i = it.tree.firstToken(param_type) - 1;
2156 while (true) : (tok_i -= 1) switch (token_tags[tok_i]) {
2157 .colon => continue,
2158 .identifier => name_token = tok_i,
2159 .doc_comment => first_doc_comment = tok_i,
2160 .keyword_comptime, .keyword_noalias => comptime_noalias = tok_i,
2161 else => break,
2162 };
2163 it.param_i += 1;
2164 it.tok_i = it.tree.lastToken(param_type) + 1;
2165 it.tok_flag = true;
2166 return Param{
2167 .first_doc_comment = first_doc_comment,
2168 .comptime_noalias = comptime_noalias,
2169 .name_token = name_token,
2170 .anytype_ellipsis3 = null,
2171 .type_expr = param_type,
2172 };
2173 }
2174 // Look for anytype and ... params afterwards.
2175 if (token_tags[it.tok_i] == .comma) {
2176 it.tok_i += 1;
2177 } else {
2178 return null;
2179 }
2180 if (token_tags[it.tok_i] == .doc_comment) {
2181 first_doc_comment = it.tok_i;
2182 while (token_tags[it.tok_i] == .doc_comment) {
2183 it.tok_i += 1;
2184 }
2185 }
2186 switch (token_tags[it.tok_i]) {
2187 .ellipsis3 => {
2188 it.tok_flag = false; // Next iteration should return null.
2189 return Param{
2190 .first_doc_comment = first_doc_comment,
2191 .comptime_noalias = null,
2192 .name_token = null,
2193 .anytype_ellipsis3 = it.tok_i,
2194 .type_expr = 0,
2195 };
2196 },
2197 .keyword_noalias, .keyword_comptime => {
2198 comptime_noalias = it.tok_i;
2199 it.tok_i += 1;
2200 },
2201 else => {},
2202 }
2203 if (token_tags[it.tok_i] == .identifier and
2204 token_tags[it.tok_i + 1] == .colon)
2205 {
2206 name_token = it.tok_i;
2207 it.tok_i += 2;
2208 }
2209 if (token_tags[it.tok_i] == .keyword_anytype) {
2210 it.tok_i += 1;
2211 return Param{
2212 .first_doc_comment = first_doc_comment,
2213 .comptime_noalias = comptime_noalias,
2214 .name_token = name_token,
2215 .anytype_ellipsis3 = it.tok_i - 1,
2216 .type_expr = 0,
2217 };
2218 }
2219 it.tok_flag = false;
2220 }
2221 }
2222 };
30352223
3036 pub fn params(self: *BuiltinCall) []*Node {
3037 const decls_start = @ptrCast([*]u8, self) + @sizeOf(BuiltinCall);
3038 return @ptrCast([*]*Node, decls_start)[0..self.params_len];
2224 pub fn iterate(fn_proto: FnProto, tree: Tree) Iterator {
2225 return .{
2226 .tree = &tree,
2227 .fn_proto = &fn_proto,
2228 .param_i = 0,
2229 .tok_i = undefined,
2230 .tok_flag = false,
2231 };
30392232 }
2233 };
30402234
3041 pub fn paramsConst(self: *const BuiltinCall) []const *Node {
3042 const decls_start = @ptrCast([*]const u8, self) + @sizeOf(BuiltinCall);
3043 return @ptrCast([*]const *Node, decls_start)[0..self.params_len];
3044 }
2235 pub const StructInit = struct {
2236 ast: Ast,
30452237
3046 fn sizeInBytes(params_len: NodeIndex) usize {
3047 return @sizeOf(BuiltinCall) + @sizeOf(*Node) * @as(usize, params_len);
3048 }
2238 pub const Ast = struct {
2239 lbrace: TokenIndex,
2240 fields: []const Node.Index,
2241 type_expr: Node.Index,
2242 };
30492243 };
30502244
3051 /// The string literal tokens appear directly in memory after MultilineStringLiteral.
3052 pub const MultilineStringLiteral = struct {
3053 base: Node = Node{ .tag = .MultilineStringLiteral },
3054 lines_len: TokenIndex,
2245 pub const ArrayInit = struct {
2246 ast: Ast,
30552247
3056 /// After this the caller must initialize the lines list.
3057 pub fn alloc(allocator: *mem.Allocator, lines_len: NodeIndex) !*MultilineStringLiteral {
3058 const bytes = try allocator.alignedAlloc(u8, @alignOf(MultilineStringLiteral), sizeInBytes(lines_len));
3059 return @ptrCast(*MultilineStringLiteral, bytes.ptr);
3060 }
2248 pub const Ast = struct {
2249 lbrace: TokenIndex,
2250 elements: []const Node.Index,
2251 type_expr: Node.Index,
2252 };
2253 };
30612254
3062 pub fn free(self: *MultilineStringLiteral, allocator: *mem.Allocator) void {
3063 const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.lines_len)];
3064 allocator.free(bytes);
3065 }
2255 pub const ArrayType = struct {
2256 ast: Ast,
30662257
3067 pub fn iterate(self: *const MultilineStringLiteral, index: usize) ?*Node {
3068 return null;
3069 }
2258 pub const Ast = struct {
2259 lbracket: TokenIndex,
2260 elem_count: Node.Index,
2261 sentinel: ?Node.Index,
2262 elem_type: Node.Index,
2263 };
2264 };
30702265
3071 pub fn firstToken(self: *const MultilineStringLiteral) TokenIndex {
3072 return self.linesConst()[0];
3073 }
2266 pub const PtrType = struct {
2267 size: std.builtin.TypeInfo.Pointer.Size,
2268 allowzero_token: ?TokenIndex,
2269 const_token: ?TokenIndex,
2270 volatile_token: ?TokenIndex,
2271 ast: Ast,
2272
2273 pub const Ast = struct {
2274 main_token: TokenIndex,
2275 align_node: Node.Index,
2276 sentinel: Node.Index,
2277 bit_range_start: Node.Index,
2278 bit_range_end: Node.Index,
2279 child_type: Node.Index,
2280 };
2281 };
30742282
3075 pub fn lastToken(self: *const MultilineStringLiteral) TokenIndex {
3076 return self.linesConst()[self.lines_len - 1];
3077 }
2283 pub const Slice = struct {
2284 ast: Ast,
30782285
3079 pub fn lines(self: *MultilineStringLiteral) []TokenIndex {
3080 const decls_start = @ptrCast([*]u8, self) + @sizeOf(MultilineStringLiteral);
3081 return @ptrCast([*]TokenIndex, decls_start)[0..self.lines_len];
3082 }
2286 pub const Ast = struct {
2287 sliced: Node.Index,
2288 lbracket: TokenIndex,
2289 start: Node.Index,
2290 end: Node.Index,
2291 sentinel: Node.Index,
2292 };
2293 };
30832294
3084 pub fn linesConst(self: *const MultilineStringLiteral) []const TokenIndex {
3085 const decls_start = @ptrCast([*]const u8, self) + @sizeOf(MultilineStringLiteral);
3086 return @ptrCast([*]const TokenIndex, decls_start)[0..self.lines_len];
3087 }
2295 pub const ContainerDecl = struct {
2296 layout_token: ?TokenIndex,
2297 ast: Ast,
2298
2299 pub const Ast = struct {
2300 main_token: TokenIndex,
2301 /// Populated when main_token is Keyword_union.
2302 enum_token: ?TokenIndex,
2303 members: []const Node.Index,
2304 arg: Node.Index,
2305 };
2306 };
30882307
3089 fn sizeInBytes(lines_len: NodeIndex) usize {
3090 return @sizeOf(MultilineStringLiteral) + @sizeOf(TokenIndex) * @as(usize, lines_len);
3091 }
2308 pub const SwitchCase = struct {
2309 /// Points to the first token after the `|`. Will either be an identifier or
2310 /// a `*` (with an identifier immediately after it).
2311 payload_token: ?TokenIndex,
2312 ast: Ast,
2313
2314 pub const Ast = struct {
2315 /// If empty, this is an else case
2316 values: []const Node.Index,
2317 arrow_token: TokenIndex,
2318 target_expr: Node.Index,
2319 };
30922320 };
30932321
30942322 pub const Asm = struct {
3095 base: Node = Node{ .tag = .Asm },
3096 asm_token: TokenIndex,
3097 rparen: TokenIndex,
2323 ast: Ast,
30982324 volatile_token: ?TokenIndex,
3099 template: *Node,
3100 outputs: []Output,
3101 inputs: []Input,
3102 /// A clobber node must be a StringLiteral or MultilineStringLiteral.
3103 clobbers: []*Node,
3104
3105 pub const Output = struct {
3106 lbracket: TokenIndex,
3107 symbolic_name: *Node,
3108 constraint: *Node,
3109 kind: Kind,
2325 first_clobber: ?TokenIndex,
2326 outputs: []const Node.Index,
2327 inputs: []const Node.Index,
2328
2329 pub const Ast = struct {
2330 asm_token: TokenIndex,
2331 template: Node.Index,
2332 items: []const Node.Index,
31102333 rparen: TokenIndex,
3111
3112 pub const Kind = union(enum) {
3113 Variable: *OneToken,
3114 Return: *Node,
3115 };
3116
3117 pub fn iterate(self: *const Output, index: usize) ?*Node {
3118 var i = index;
3119
3120 if (i < 1) return self.symbolic_name;
3121 i -= 1;
3122
3123 if (i < 1) return self.constraint;
3124 i -= 1;
3125
3126 switch (self.kind) {
3127 .Variable => |variable_name| {
3128 if (i < 1) return &variable_name.base;
3129 i -= 1;
3130 },
3131 .Return => |return_type| {
3132 if (i < 1) return return_type;
3133 i -= 1;
3134 },
3135 }
3136
3137 return null;
3138 }
3139
3140 pub fn firstToken(self: *const Output) TokenIndex {
3141 return self.lbracket;
3142 }
3143
3144 pub fn lastToken(self: *const Output) TokenIndex {
3145 return self.rparen;
3146 }
31472334 };
2335 };
31482336
3149 pub const Input = struct {
3150 lbracket: TokenIndex,
3151 symbolic_name: *Node,
3152 constraint: *Node,
3153 expr: *Node,
3154 rparen: TokenIndex,
3155
3156 pub fn iterate(self: *const Input, index: usize) ?*Node {
3157 var i = index;
3158
3159 if (i < 1) return self.symbolic_name;
3160 i -= 1;
2337 pub const Call = struct {
2338 ast: Ast,
2339 async_token: ?TokenIndex,
31612340
3162 if (i < 1) return self.constraint;
3163 i -= 1;
2341 pub const Ast = struct {
2342 lparen: TokenIndex,
2343 fn_expr: Node.Index,
2344 params: []const Node.Index,
2345 };
2346 };
2347};
31642348
3165 if (i < 1) return self.expr;
3166 i -= 1;
2349pub const Error = struct {
2350 tag: Tag,
2351 token: TokenIndex,
2352 extra: union {
2353 none: void,
2354 expected_tag: Token.Tag,
2355 } = .{ .none = {} },
31672356
3168 return null;
3169 }
2357 pub const Tag = enum {
2358 asterisk_after_ptr_deref,
2359 decl_between_fields,
2360 expected_block,
2361 expected_block_or_assignment,
2362 expected_block_or_expr,
2363 expected_block_or_field,
2364 expected_container_members,
2365 expected_expr,
2366 expected_expr_or_assignment,
2367 expected_fn,
2368 expected_inlinable,
2369 expected_labelable,
2370 expected_param_list,
2371 expected_prefix_expr,
2372 expected_primary_type_expr,
2373 expected_pub_item,
2374 expected_return_type,
2375 expected_semi_or_else,
2376 expected_semi_or_lbrace,
2377 expected_statement,
2378 expected_string_literal,
2379 expected_suffix_op,
2380 expected_type_expr,
2381 expected_var_decl,
2382 expected_var_decl_or_fn,
2383 expected_loop_payload,
2384 expected_container,
2385 extra_align_qualifier,
2386 extra_allowzero_qualifier,
2387 extra_const_qualifier,
2388 extra_volatile_qualifier,
2389 invalid_align,
2390 invalid_and,
2391 invalid_bit_range,
2392 invalid_token,
2393 same_line_doc_comment,
2394 unattached_doc_comment,
2395
2396 /// `expected_tag` is populated.
2397 expected_token,
2398 };
2399};
31702400
3171 pub fn firstToken(self: *const Input) TokenIndex {
3172 return self.lbracket;
3173 }
2401pub const Node = struct {
2402 tag: Tag,
2403 main_token: TokenIndex,
2404 data: Data,
31742405
3175 pub fn lastToken(self: *const Input) TokenIndex {
3176 return self.rparen;
3177 }
3178 };
2406 pub const Index = u32;
31792407
3180 pub fn iterate(self: *const Asm, index: usize) ?*Node {
3181 var i = index;
2408 comptime {
2409 // Goal is to keep this under one byte for efficiency.
2410 assert(@sizeOf(Tag) == 1);
2411 }
31822412
3183 if (i < self.outputs.len * 3) switch (i % 3) {
3184 0 => return self.outputs[i / 3].symbolic_name,
3185 1 => return self.outputs[i / 3].constraint,
3186 2 => switch (self.outputs[i / 3].kind) {
3187 .Variable => |variable_name| return &variable_name.base,
3188 .Return => |return_type| return return_type,
3189 },
3190 else => unreachable,
3191 };
3192 i -= self.outputs.len * 3;
2413 /// Note: The FooComma/FooSemicolon variants exist to ease the implementation of
2414 /// Tree.lastToken()
2415 pub const Tag = enum {
2416 /// sub_list[lhs...rhs]
2417 root,
2418 /// `usingnamespace lhs;`. rhs unused. main_token is `usingnamespace`.
2419 @"usingnamespace",
2420 /// lhs is test name token (must be string literal), if any.
2421 /// rhs is the body node.
2422 test_decl,
2423 /// lhs is the index into extra_data.
2424 /// rhs is the initialization expression, if any.
2425 /// main_token is `var` or `const`.
2426 global_var_decl,
2427 /// `var a: x align(y) = rhs`
2428 /// lhs is the index into extra_data.
2429 /// main_token is `var` or `const`.
2430 local_var_decl,
2431 /// `var a: lhs = rhs`. lhs and rhs may be unused.
2432 /// Can be local or global.
2433 /// main_token is `var` or `const`.
2434 simple_var_decl,
2435 /// `var a align(lhs) = rhs`. lhs and rhs may be unused.
2436 /// Can be local or global.
2437 /// main_token is `var` or `const`.
2438 aligned_var_decl,
2439 /// lhs is the identifier token payload if any,
2440 /// rhs is the deferred expression.
2441 @"errdefer",
2442 /// lhs is unused.
2443 /// rhs is the deferred expression.
2444 @"defer",
2445 /// lhs catch rhs
2446 /// lhs catch |err| rhs
2447 /// main_token is the `catch` keyword.
2448 /// payload is determined by looking at the next token after the `catch` keyword.
2449 @"catch",
2450 /// `lhs.a`. main_token is the dot. rhs is the identifier token index.
2451 field_access,
2452 /// `lhs.?`. main_token is the dot. rhs is the `?` token index.
2453 unwrap_optional,
2454 /// `lhs == rhs`. main_token is op.
2455 equal_equal,
2456 /// `lhs != rhs`. main_token is op.
2457 bang_equal,
2458 /// `lhs < rhs`. main_token is op.
2459 less_than,
2460 /// `lhs > rhs`. main_token is op.
2461 greater_than,
2462 /// `lhs <= rhs`. main_token is op.
2463 less_or_equal,
2464 /// `lhs >= rhs`. main_token is op.
2465 greater_or_equal,
2466 /// `lhs *= rhs`. main_token is op.
2467 assign_mul,
2468 /// `lhs /= rhs`. main_token is op.
2469 assign_div,
2470 /// `lhs *= rhs`. main_token is op.
2471 assign_mod,
2472 /// `lhs += rhs`. main_token is op.
2473 assign_add,
2474 /// `lhs -= rhs`. main_token is op.
2475 assign_sub,
2476 /// `lhs <<= rhs`. main_token is op.
2477 assign_bit_shift_left,
2478 /// `lhs >>= rhs`. main_token is op.
2479 assign_bit_shift_right,
2480 /// `lhs &= rhs`. main_token is op.
2481 assign_bit_and,
2482 /// `lhs ^= rhs`. main_token is op.
2483 assign_bit_xor,
2484 /// `lhs |= rhs`. main_token is op.
2485 assign_bit_or,
2486 /// `lhs *%= rhs`. main_token is op.
2487 assign_mul_wrap,
2488 /// `lhs +%= rhs`. main_token is op.
2489 assign_add_wrap,
2490 /// `lhs -%= rhs`. main_token is op.
2491 assign_sub_wrap,
2492 /// `lhs = rhs`. main_token is op.
2493 assign,
2494 /// `lhs || rhs`. main_token is the `||`.
2495 merge_error_sets,
2496 /// `lhs * rhs`. main_token is the `*`.
2497 mul,
2498 /// `lhs / rhs`. main_token is the `/`.
2499 div,
2500 /// `lhs % rhs`. main_token is the `%`.
2501 mod,
2502 /// `lhs ** rhs`. main_token is the `**`.
2503 array_mult,
2504 /// `lhs *% rhs`. main_token is the `*%`.
2505 mul_wrap,
2506 /// `lhs + rhs`. main_token is the `+`.
2507 add,
2508 /// `lhs - rhs`. main_token is the `-`.
2509 sub,
2510 /// `lhs ++ rhs`. main_token is the `++`.
2511 array_cat,
2512 /// `lhs +% rhs`. main_token is the `+%`.
2513 add_wrap,
2514 /// `lhs -% rhs`. main_token is the `-%`.
2515 sub_wrap,
2516 /// `lhs << rhs`. main_token is the `<<`.
2517 bit_shift_left,
2518 /// `lhs >> rhs`. main_token is the `>>`.
2519 bit_shift_right,
2520 /// `lhs & rhs`. main_token is the `&`.
2521 bit_and,
2522 /// `lhs ^ rhs`. main_token is the `^`.
2523 bit_xor,
2524 /// `lhs | rhs`. main_token is the `|`.
2525 bit_or,
2526 /// `lhs orelse rhs`. main_token is the `orelse`.
2527 @"orelse",
2528 /// `lhs and rhs`. main_token is the `and`.
2529 bool_and,
2530 /// `lhs or rhs`. main_token is the `or`.
2531 bool_or,
2532 /// `op lhs`. rhs unused. main_token is op.
2533 bool_not,
2534 /// `op lhs`. rhs unused. main_token is op.
2535 negation,
2536 /// `op lhs`. rhs unused. main_token is op.
2537 bit_not,
2538 /// `op lhs`. rhs unused. main_token is op.
2539 negation_wrap,
2540 /// `op lhs`. rhs unused. main_token is op.
2541 address_of,
2542 /// `op lhs`. rhs unused. main_token is op.
2543 @"try",
2544 /// `op lhs`. rhs unused. main_token is op.
2545 @"await",
2546 /// `?lhs`. rhs unused. main_token is the `?`.
2547 optional_type,
2548 /// `[lhs]rhs`. lhs can be omitted to make it a slice.
2549 array_type,
2550 /// `[lhs:a]b`. `array_type_sentinel[rhs]`.
2551 array_type_sentinel,
2552 /// `[*]align(lhs) rhs`. lhs can be omitted.
2553 /// `*align(lhs) rhs`. lhs can be omitted.
2554 /// `[]rhs`.
2555 /// main_token is the asterisk if a pointer or the lbracket if a slice
2556 /// main_token might be a ** token, which is shared with a parent/child
2557 /// pointer type and may require special handling.
2558 ptr_type_aligned,
2559 /// `[*:lhs]rhs`. lhs can be omitted.
2560 /// `*rhs`.
2561 /// `[:lhs]rhs`.
2562 /// main_token is the asterisk if a pointer or the lbracket if a slice
2563 /// main_token might be a ** token, which is shared with a parent/child
2564 /// pointer type and may require special handling.
2565 ptr_type_sentinel,
2566 /// lhs is index into ptr_type. rhs is the element type expression.
2567 /// main_token is the asterisk if a pointer or the lbracket if a slice
2568 /// main_token might be a ** token, which is shared with a parent/child
2569 /// pointer type and may require special handling.
2570 ptr_type,
2571 /// lhs is index into ptr_type_bit_range. rhs is the element type expression.
2572 /// main_token is the asterisk if a pointer or the lbracket if a slice
2573 /// main_token might be a ** token, which is shared with a parent/child
2574 /// pointer type and may require special handling.
2575 ptr_type_bit_range,
2576 /// `lhs[rhs..]`
2577 /// main_token is the lbracket.
2578 slice_open,
2579 /// `lhs[b..c]`. rhs is index into Slice
2580 /// main_token is the lbracket.
2581 slice,
2582 /// `lhs[b..c :d]`. rhs is index into SliceSentinel
2583 /// main_token is the lbracket.
2584 slice_sentinel,
2585 /// `lhs.*`. rhs is unused.
2586 deref,
2587 /// `lhs[rhs]`.
2588 array_access,
2589 /// `lhs{rhs}`. rhs can be omitted.
2590 array_init_one,
2591 /// `lhs{rhs,}`. rhs can *not* be omitted
2592 array_init_one_comma,
2593 /// `.{lhs, rhs}`. lhs and rhs can be omitted.
2594 array_init_dot_two,
2595 /// Same as `array_init_dot_two` except there is known to be a trailing comma
2596 /// before the final rbrace.
2597 array_init_dot_two_comma,
2598 /// `.{a, b}`. `sub_list[lhs..rhs]`.
2599 array_init_dot,
2600 /// Same as `array_init_dot` except there is known to be a trailing comma
2601 /// before the final rbrace.
2602 array_init_dot_comma,
2603 /// `lhs{a, b}`. `sub_range_list[rhs]`. lhs can be omitted which means `.{a, b}`.
2604 array_init,
2605 /// Same as `array_init` except there is known to be a trailing comma
2606 /// before the final rbrace.
2607 array_init_comma,
2608 /// `lhs{.a = rhs}`. rhs can be omitted making it empty.
2609 /// main_token is the lbrace.
2610 struct_init_one,
2611 /// `lhs{.a = rhs,}`. rhs can *not* be omitted.
2612 /// main_token is the lbrace.
2613 struct_init_one_comma,
2614 /// `.{.a = lhs, .b = rhs}`. lhs and rhs can be omitted.
2615 /// main_token is the lbrace.
2616 /// No trailing comma before the rbrace.
2617 struct_init_dot_two,
2618 /// Same as `struct_init_dot_two` except there is known to be a trailing comma
2619 /// before the final rbrace.
2620 struct_init_dot_two_comma,
2621 /// `.{.a = b, .c = d}`. `sub_list[lhs..rhs]`.
2622 /// main_token is the lbrace.
2623 struct_init_dot,
2624 /// Same as `struct_init_dot` except there is known to be a trailing comma
2625 /// before the final rbrace.
2626 struct_init_dot_comma,
2627 /// `lhs{.a = b, .c = d}`. `sub_range_list[rhs]`.
2628 /// lhs can be omitted which means `.{.a = b, .c = d}`.
2629 /// main_token is the lbrace.
2630 struct_init,
2631 /// Same as `struct_init` except there is known to be a trailing comma
2632 /// before the final rbrace.
2633 struct_init_comma,
2634 /// `lhs(rhs)`. rhs can be omitted.
2635 /// main_token is the lparen.
2636 call_one,
2637 /// `lhs(rhs,)`. rhs can be omitted.
2638 /// main_token is the lparen.
2639 call_one_comma,
2640 /// `async lhs(rhs)`. rhs can be omitted.
2641 async_call_one,
2642 /// `async lhs(rhs,)`.
2643 async_call_one_comma,
2644 /// `lhs(a, b, c)`. `SubRange[rhs]`.
2645 /// main_token is the `(`.
2646 call,
2647 /// `lhs(a, b, c,)`. `SubRange[rhs]`.
2648 /// main_token is the `(`.
2649 call_comma,
2650 /// `async lhs(a, b, c)`. `SubRange[rhs]`.
2651 /// main_token is the `(`.
2652 async_call,
2653 /// `async lhs(a, b, c,)`. `SubRange[rhs]`.
2654 /// main_token is the `(`.
2655 async_call_comma,
2656 /// `switch(lhs) {}`. `SubRange[rhs]`.
2657 @"switch",
2658 /// Same as switch except there is known to be a trailing comma
2659 /// before the final rbrace
2660 switch_comma,
2661 /// `lhs => rhs`. If lhs is omitted it means `else`.
2662 /// main_token is the `=>`
2663 switch_case_one,
2664 /// `a, b, c => rhs`. `SubRange[lhs]`.
2665 /// main_token is the `=>`
2666 switch_case,
2667 /// `lhs...rhs`.
2668 switch_range,
2669 /// `while (lhs) rhs`.
2670 /// `while (lhs) |x| rhs`.
2671 while_simple,
2672 /// `while (lhs) : (a) b`. `WhileCont[rhs]`.
2673 /// `while (lhs) : (a) b`. `WhileCont[rhs]`.
2674 while_cont,
2675 /// `while (lhs) : (a) b else c`. `While[rhs]`.
2676 /// `while (lhs) |x| : (a) b else c`. `While[rhs]`.
2677 /// `while (lhs) |x| : (a) b else |y| c`. `While[rhs]`.
2678 @"while",
2679 /// `for (lhs) rhs`.
2680 for_simple,
2681 /// `for (lhs) a else b`. `if_list[rhs]`.
2682 @"for",
2683 /// `if (lhs) rhs`.
2684 /// `if (lhs) |a| rhs`.
2685 if_simple,
2686 /// `if (lhs) a else b`. `If[rhs]`.
2687 /// `if (lhs) |x| a else b`. `If[rhs]`.
2688 /// `if (lhs) |x| a else |y| b`. `If[rhs]`.
2689 @"if",
2690 /// `suspend lhs`. lhs can be omitted. rhs is unused.
2691 @"suspend",
2692 /// `resume lhs`. rhs is unused.
2693 @"resume",
2694 /// `continue`. lhs is token index of label if any. rhs is unused.
2695 @"continue",
2696 /// `break :lhs rhs`
2697 /// both lhs and rhs may be omitted.
2698 @"break",
2699 /// `return lhs`. lhs can be omitted. rhs is unused.
2700 @"return",
2701 /// `fn(a: lhs) rhs`. lhs can be omitted.
2702 /// anytype and ... parameters are omitted from the AST tree.
2703 /// main_token is the `fn` keyword.
2704 /// extern function declarations use this tag.
2705 fn_proto_simple,
2706 /// `fn(a: b, c: d) rhs`. `sub_range_list[lhs]`.
2707 /// anytype and ... parameters are omitted from the AST tree.
2708 /// main_token is the `fn` keyword.
2709 /// extern function declarations use this tag.
2710 fn_proto_multi,
2711 /// `fn(a: b) rhs linksection(e) callconv(f)`. `FnProtoOne[lhs]`.
2712 /// zero or one parameters.
2713 /// anytype and ... parameters are omitted from the AST tree.
2714 /// main_token is the `fn` keyword.
2715 /// extern function declarations use this tag.
2716 fn_proto_one,
2717 /// `fn(a: b, c: d) rhs linksection(e) callconv(f)`. `FnProto[lhs]`.
2718 /// anytype and ... parameters are omitted from the AST tree.
2719 /// main_token is the `fn` keyword.
2720 /// extern function declarations use this tag.
2721 fn_proto,
2722 /// lhs is the fn_proto.
2723 /// rhs is the function body block.
2724 /// Note that extern function declarations use the fn_proto tags rather
2725 /// than this one.
2726 fn_decl,
2727 /// `anyframe->rhs`. main_token is `anyframe`. `lhs` is arrow token index.
2728 anyframe_type,
2729 /// Both lhs and rhs unused.
2730 anyframe_literal,
2731 /// Both lhs and rhs unused.
2732 char_literal,
2733 /// Both lhs and rhs unused.
2734 integer_literal,
2735 /// Both lhs and rhs unused.
2736 float_literal,
2737 /// Both lhs and rhs unused.
2738 false_literal,
2739 /// Both lhs and rhs unused.
2740 true_literal,
2741 /// Both lhs and rhs unused.
2742 null_literal,
2743 /// Both lhs and rhs unused.
2744 undefined_literal,
2745 /// Both lhs and rhs unused.
2746 unreachable_literal,
2747 /// Both lhs and rhs unused.
2748 /// Most identifiers will not have explicit AST nodes, however for expressions
2749 /// which could be one of many different kinds of AST nodes, there will be an
2750 /// identifier AST node for it.
2751 identifier,
2752 /// lhs is the dot token index, rhs unused, main_token is the identifier.
2753 enum_literal,
2754 /// main_token is the string literal token
2755 /// Both lhs and rhs unused.
2756 string_literal,
2757 /// main_token is the first token index (redundant with lhs)
2758 /// lhs is the first token index; rhs is the last token index.
2759 /// Could be a series of multiline_string_literal_line tokens, or a single
2760 /// string_literal token.
2761 multiline_string_literal,
2762 /// `(lhs)`. main_token is the `(`; rhs is the token index of the `)`.
2763 grouped_expression,
2764 /// `@a(lhs, rhs)`. lhs and rhs may be omitted.
2765 /// main_token is the builtin token.
2766 builtin_call_two,
2767 /// Same as builtin_call_two but there is known to be a trailing comma before the rparen.
2768 builtin_call_two_comma,
2769 /// `@a(b, c)`. `sub_list[lhs..rhs]`.
2770 /// main_token is the builtin token.
2771 builtin_call,
2772 /// Same as builtin_call but there is known to be a trailing comma before the rparen.
2773 builtin_call_comma,
2774 /// `error{a, b}`.
2775 /// rhs is the rbrace, lhs is unused.
2776 error_set_decl,
2777 /// `struct {}`, `union {}`, `opaque {}`, `enum {}`. `extra_data[lhs..rhs]`.
2778 /// main_token is `struct`, `union`, `opaque`, `enum` keyword.
2779 container_decl,
2780 /// Same as ContainerDecl but there is known to be a trailing comma
2781 /// or semicolon before the rbrace.
2782 container_decl_trailing,
2783 /// `struct {lhs, rhs}`, `union {lhs, rhs}`, `opaque {lhs, rhs}`, `enum {lhs, rhs}`.
2784 /// lhs or rhs can be omitted.
2785 /// main_token is `struct`, `union`, `opaque`, `enum` keyword.
2786 container_decl_two,
2787 /// Same as ContainerDeclTwo except there is known to be a trailing comma
2788 /// or semicolon before the rbrace.
2789 container_decl_two_trailing,
2790 /// `union(lhs)` / `enum(lhs)`. `SubRange[rhs]`.
2791 container_decl_arg,
2792 /// Same as container_decl_arg but there is known to be a trailing
2793 /// comma or semicolon before the rbrace.
2794 container_decl_arg_trailing,
2795 /// `union(enum) {}`. `sub_list[lhs..rhs]`.
2796 /// Note that tagged unions with explicitly provided enums are represented
2797 /// by `container_decl_arg`.
2798 tagged_union,
2799 /// Same as tagged_union but there is known to be a trailing comma
2800 /// or semicolon before the rbrace.
2801 tagged_union_trailing,
2802 /// `union(enum) {lhs, rhs}`. lhs or rhs may be omitted.
2803 /// Note that tagged unions with explicitly provided enums are represented
2804 /// by `container_decl_arg`.
2805 tagged_union_two,
2806 /// Same as tagged_union_two but there is known to be a trailing comma
2807 /// or semicolon before the rbrace.
2808 tagged_union_two_trailing,
2809 /// `union(enum(lhs)) {}`. `SubRange[rhs]`.
2810 tagged_union_enum_tag,
2811 /// Same as tagged_union_enum_tag but there is known to be a trailing comma
2812 /// or semicolon before the rbrace.
2813 tagged_union_enum_tag_trailing,
2814 /// `a: lhs = rhs,`. lhs and rhs can be omitted.
2815 /// main_token is the field name identifier.
2816 /// lastToken() does not include the possible trailing comma.
2817 container_field_init,
2818 /// `a: lhs align(rhs),`. rhs can be omitted.
2819 /// main_token is the field name identifier.
2820 /// lastToken() does not include the possible trailing comma.
2821 container_field_align,
2822 /// `a: lhs align(c) = d,`. `container_field_list[rhs]`.
2823 /// main_token is the field name identifier.
2824 /// lastToken() does not include the possible trailing comma.
2825 container_field,
2826 /// `anytype`. both lhs and rhs unused.
2827 /// Used by `ContainerField`.
2828 @"anytype",
2829 /// `comptime lhs`. rhs unused.
2830 @"comptime",
2831 /// `nosuspend lhs`. rhs unused.
2832 @"nosuspend",
2833 /// `{lhs rhs}`. rhs or lhs can be omitted.
2834 /// main_token points at the lbrace.
2835 block_two,
2836 /// Same as block_two but there is known to be a semicolon before the rbrace.
2837 block_two_semicolon,
2838 /// `{}`. `sub_list[lhs..rhs]`.
2839 /// main_token points at the lbrace.
2840 block,
2841 /// Same as block but there is known to be a semicolon before the rbrace.
2842 block_semicolon,
2843 /// `asm(lhs)`. rhs is the token index of the rparen.
2844 asm_simple,
2845 /// `asm(lhs, a)`. `Asm[rhs]`.
2846 @"asm",
2847 /// `[a] "b" (c)`. lhs is 0, rhs is token index of the rparen.
2848 /// `[a] "b" (-> lhs)`. rhs is token index of the rparen.
2849 /// main_token is `a`.
2850 asm_output,
2851 /// `[a] "b" (lhs)`. rhs is token index of the rparen.
2852 /// main_token is `a`.
2853 asm_input,
2854 /// `error.a`. lhs is token index of `.`. rhs is token index of `a`.
2855 error_value,
2856 /// `lhs!rhs`. main_token is the `!`.
2857 error_union,
2858
2859 pub fn isContainerField(tag: Tag) bool {
2860 return switch (tag) {
2861 .container_field_init,
2862 .container_field_align,
2863 .container_field,
2864 => true,
31932865
3194 if (i < self.inputs.len * 3) switch (i % 3) {
3195 0 => return self.inputs[i / 3].symbolic_name,
3196 1 => return self.inputs[i / 3].constraint,
3197 2 => return self.inputs[i / 3].expr,
3198 else => unreachable,
2866 else => false,
31992867 };
3200 i -= self.inputs.len * 3;
3201
3202 return null;
32032868 }
2869 };
32042870
3205 pub fn firstToken(self: *const Asm) TokenIndex {
3206 return self.asm_token;
3207 }
2871 pub const Data = struct {
2872 lhs: Index,
2873 rhs: Index,
2874 };
32082875
3209 pub fn lastToken(self: *const Asm) TokenIndex {
3210 return self.rparen;
3211 }
2876 pub const LocalVarDecl = struct {
2877 type_node: Index,
2878 align_node: Index,
32122879 };
32132880
3214 /// TODO remove from the Node base struct
3215 /// TODO actually maybe remove entirely in favor of iterating backward from Node.firstToken()
3216 /// and forwards to find same-line doc comments.
3217 pub const DocComment = struct {
3218 base: Node = Node{ .tag = .DocComment },
3219 /// Points to the first doc comment token. API users are expected to iterate over the
3220 /// tokens array, looking for more doc comments, ignoring line comments, and stopping
3221 /// at the first other token.
3222 first_line: TokenIndex,
3223
3224 pub fn iterate(self: *const DocComment, index: usize) ?*Node {
3225 return null;
3226 }
2881 pub const ArrayTypeSentinel = struct {
2882 elem_type: Index,
2883 sentinel: Index,
2884 };
32272885
3228 pub fn firstToken(self: *const DocComment) TokenIndex {
3229 return self.first_line;
3230 }
2886 pub const PtrType = struct {
2887 sentinel: Index,
2888 align_node: Index,
2889 };
32312890
3232 /// Returns the first doc comment line. Be careful, this may not be the desired behavior,
3233 /// which would require the tokens array.
3234 pub fn lastToken(self: *const DocComment) TokenIndex {
3235 return self.first_line;
3236 }
2891 pub const PtrTypeBitRange = struct {
2892 sentinel: Index,
2893 align_node: Index,
2894 bit_range_start: Index,
2895 bit_range_end: Index,
32372896 };
32382897
3239 pub const TestDecl = struct {
3240 base: Node = Node{ .tag = .TestDecl },
3241 doc_comments: ?*DocComment,
3242 test_token: TokenIndex,
3243 name: ?*Node,
3244 body_node: *Node,
2898 pub const SubRange = struct {
2899 /// Index into sub_list.
2900 start: Index,
2901 /// Index into sub_list.
2902 end: Index,
2903 };
32452904
3246 pub fn iterate(self: *const TestDecl, index: usize) ?*Node {
3247 var i = index;
2905 pub const If = struct {
2906 then_expr: Index,
2907 else_expr: Index,
2908 };
32482909
3249 if (i < 1) return self.body_node;
3250 i -= 1;
2910 pub const ContainerField = struct {
2911 value_expr: Index,
2912 align_expr: Index,
2913 };
32512914
3252 return null;
3253 }
2915 pub const GlobalVarDecl = struct {
2916 type_node: Index,
2917 align_node: Index,
2918 section_node: Index,
2919 };
32542920
3255 pub fn firstToken(self: *const TestDecl) TokenIndex {
3256 return self.test_token;
3257 }
2921 pub const Slice = struct {
2922 start: Index,
2923 end: Index,
2924 };
32582925
3259 pub fn lastToken(self: *const TestDecl) TokenIndex {
3260 return self.body_node.lastToken();
3261 }
2926 pub const SliceSentinel = struct {
2927 start: Index,
2928 end: Index,
2929 sentinel: Index,
2930 };
2931
2932 pub const While = struct {
2933 cont_expr: Index,
2934 then_expr: Index,
2935 else_expr: Index,
32622936 };
3263};
32642937
3265pub const PtrInfo = struct {
3266 allowzero_token: ?TokenIndex = null,
3267 align_info: ?Align = null,
3268 const_token: ?TokenIndex = null,
3269 volatile_token: ?TokenIndex = null,
3270 sentinel: ?*Node = null,
2938 pub const WhileCont = struct {
2939 cont_expr: Index,
2940 then_expr: Index,
2941 };
32712942
3272 pub const Align = struct {
3273 node: *Node,
3274 bit_range: ?BitRange = null,
2943 pub const FnProtoOne = struct {
2944 /// Populated if there is exactly 1 parameter. Otherwise there are 0 parameters.
2945 param: Index,
2946 /// Populated if align(A) is present.
2947 align_expr: Index,
2948 /// Populated if linksection(A) is present.
2949 section_expr: Index,
2950 /// Populated if callconv(A) is present.
2951 callconv_expr: Index,
2952 };
32752953
3276 pub const BitRange = struct {
3277 start: *Node,
3278 end: *Node,
3279 };
2954 pub const FnProto = struct {
2955 params_start: Index,
2956 params_end: Index,
2957 /// Populated if align(A) is present.
2958 align_expr: Index,
2959 /// Populated if linksection(A) is present.
2960 section_expr: Index,
2961 /// Populated if callconv(A) is present.
2962 callconv_expr: Index,
32802963 };
3281};
32822964
3283test "iterate" {
3284 var root = Node.Root{
3285 .base = Node{ .tag = Node.Tag.Root },
3286 .decls_len = 0,
3287 .eof_token = 0,
2965 pub const Asm = struct {
2966 items_start: Index,
2967 items_end: Index,
2968 /// Needed to make lastToken() work.
2969 rparen: TokenIndex,
32882970 };
3289 var base = &root.base;
3290 testing.expect(base.iterate(0) == null);
3291}
2971};
lib/std/zig/parse.zig+3505-2912
......@@ -11,85 +11,181 @@ const Node = ast.Node;
1111const Tree = ast.Tree;
1212const AstError = ast.Error;
1313const TokenIndex = ast.TokenIndex;
14const NodeIndex = ast.NodeIndex;
1514const Token = std.zig.Token;
1615
1716pub const Error = error{ParseError} || Allocator.Error;
1817
1918/// Result should be freed with tree.deinit() when there are
2019/// no more references to any of the tokens or nodes.
21pub fn parse(gpa: *Allocator, source: []const u8) Allocator.Error!*Tree {
22 var token_ids = std.ArrayList(Token.Id).init(gpa);
23 defer token_ids.deinit();
24 var token_locs = std.ArrayList(Token.Loc).init(gpa);
25 defer token_locs.deinit();
20pub fn parse(gpa: *Allocator, source: []const u8) Allocator.Error!Tree {
21 var tokens = ast.TokenList{};
22 defer tokens.deinit(gpa);
2623
2724 // Empirically, the zig std lib has an 8:1 ratio of source bytes to token count.
2825 const estimated_token_count = source.len / 8;
29 try token_ids.ensureCapacity(estimated_token_count);
30 try token_locs.ensureCapacity(estimated_token_count);
26 try tokens.ensureCapacity(gpa, estimated_token_count);
3127
3228 var tokenizer = std.zig.Tokenizer.init(source);
3329 while (true) {
3430 const token = tokenizer.next();
35 try token_ids.append(token.id);
36 try token_locs.append(token.loc);
37 if (token.id == .Eof) break;
31 try tokens.append(gpa, .{
32 .tag = token.tag,
33 .start = @intCast(u32, token.loc.start),
34 });
35 if (token.tag == .eof) break;
3836 }
3937
4038 var parser: Parser = .{
4139 .source = source,
42 .arena = std.heap.ArenaAllocator.init(gpa),
4340 .gpa = gpa,
44 .token_ids = token_ids.items,
45 .token_locs = token_locs.items,
41 .token_tags = tokens.items(.tag),
42 .token_starts = tokens.items(.start),
4643 .errors = .{},
44 .nodes = .{},
45 .extra_data = .{},
4746 .tok_i = 0,
4847 };
4948 defer parser.errors.deinit(gpa);
50 errdefer parser.arena.deinit();
51
52 while (token_ids.items[parser.tok_i] == .LineComment) parser.tok_i += 1;
53
54 const root_node = try parser.parseRoot();
49 defer parser.nodes.deinit(gpa);
50 defer parser.extra_data.deinit(gpa);
51
52 // Empirically, Zig source code has a 2:1 ratio of tokens to AST nodes.
53 // Make sure at least 1 so we can use appendAssumeCapacity on the root node below.
54 const estimated_node_count = (tokens.len + 2) / 2;
55 try parser.nodes.ensureCapacity(gpa, estimated_node_count);
56
57 // Root node must be index 0.
58 // Root <- skip ContainerMembers eof
59 parser.nodes.appendAssumeCapacity(.{
60 .tag = .root,
61 .main_token = 0,
62 .data = .{
63 .lhs = undefined,
64 .rhs = undefined,
65 },
66 });
67 const root_members = try parser.parseContainerMembers();
68 const root_decls = try root_members.toSpan(&parser);
69 if (parser.token_tags[parser.tok_i] != .eof) {
70 try parser.warnExpected(.eof);
71 }
72 parser.nodes.items(.data)[0] = .{
73 .lhs = root_decls.start,
74 .rhs = root_decls.end,
75 };
5576
56 const tree = try parser.arena.allocator.create(Tree);
57 tree.* = .{
58 .gpa = gpa,
77 // TODO experiment with compacting the MultiArrayList slices here
78 return Tree{
5979 .source = source,
60 .token_ids = token_ids.toOwnedSlice(),
61 .token_locs = token_locs.toOwnedSlice(),
80 .tokens = tokens.toOwnedSlice(),
81 .nodes = parser.nodes.toOwnedSlice(),
82 .extra_data = parser.extra_data.toOwnedSlice(gpa),
6283 .errors = parser.errors.toOwnedSlice(gpa),
63 .root_node = root_node,
64 .arena = parser.arena.state,
6584 };
66 return tree;
6785}
6886
87const null_node: Node.Index = 0;
88
6989/// Represents in-progress parsing, will be converted to an ast.Tree after completion.
7090const Parser = struct {
71 arena: std.heap.ArenaAllocator,
7291 gpa: *Allocator,
7392 source: []const u8,
74 token_ids: []const Token.Id,
75 token_locs: []const Token.Loc,
93 token_tags: []const Token.Tag,
94 token_starts: []const ast.ByteOffset,
7695 tok_i: TokenIndex,
7796 errors: std.ArrayListUnmanaged(AstError),
97 nodes: ast.NodeList,
98 extra_data: std.ArrayListUnmanaged(Node.Index),
99
100 const SmallSpan = union(enum) {
101 zero_or_one: Node.Index,
102 multi: []Node.Index,
78103
79 /// Root <- skip ContainerMembers eof
80 fn parseRoot(p: *Parser) Allocator.Error!*Node.Root {
81 const decls = try parseContainerMembers(p, true);
82 defer p.gpa.free(decls);
104 fn deinit(self: SmallSpan, gpa: *Allocator) void {
105 switch (self) {
106 .zero_or_one => {},
107 .multi => |list| gpa.free(list),
108 }
109 }
110 };
83111
84 // parseContainerMembers will try to skip as much
85 // invalid tokens as it can so this can only be the EOF
86 const eof_token = p.eatToken(.Eof).?;
112 const Members = struct {
113 len: usize,
114 lhs: Node.Index,
115 rhs: Node.Index,
116 trailing: bool,
87117
88 const decls_len = @intCast(NodeIndex, decls.len);
89 const node = try Node.Root.create(&p.arena.allocator, decls_len, eof_token);
90 std.mem.copy(*Node, node.decls(), decls);
118 fn toSpan(self: Members, p: *Parser) !Node.SubRange {
119 if (self.len <= 2) {
120 const nodes = [2]Node.Index{ self.lhs, self.rhs };
121 return p.listToSpan(nodes[0..self.len]);
122 } else {
123 return Node.SubRange{ .start = self.lhs, .end = self.rhs };
124 }
125 }
126 };
91127
92 return node;
128 fn listToSpan(p: *Parser, list: []const Node.Index) !Node.SubRange {
129 try p.extra_data.appendSlice(p.gpa, list);
130 return Node.SubRange{
131 .start = @intCast(Node.Index, p.extra_data.items.len - list.len),
132 .end = @intCast(Node.Index, p.extra_data.items.len),
133 };
134 }
135
136 fn addNode(p: *Parser, elem: ast.NodeList.Elem) Allocator.Error!Node.Index {
137 const result = @intCast(Node.Index, p.nodes.len);
138 try p.nodes.append(p.gpa, elem);
139 return result;
140 }
141
142 fn addExtra(p: *Parser, extra: anytype) Allocator.Error!Node.Index {
143 const fields = std.meta.fields(@TypeOf(extra));
144 try p.extra_data.ensureCapacity(p.gpa, p.extra_data.items.len + fields.len);
145 const result = @intCast(u32, p.extra_data.items.len);
146 inline for (fields) |field| {
147 comptime assert(field.field_type == Node.Index);
148 p.extra_data.appendAssumeCapacity(@field(extra, field.name));
149 }
150 return result;
151 }
152
153 fn warn(p: *Parser, tag: ast.Error.Tag) error{OutOfMemory}!void {
154 @setCold(true);
155 try p.warnMsg(.{ .tag = tag, .token = p.tok_i });
156 }
157
158 fn warnExpected(p: *Parser, expected_token: Token.Tag) error{OutOfMemory}!void {
159 @setCold(true);
160 try p.warnMsg(.{
161 .tag = .expected_token,
162 .token = p.tok_i,
163 .extra = .{ .expected_tag = expected_token },
164 });
165 }
166 fn warnMsg(p: *Parser, msg: ast.Error) error{OutOfMemory}!void {
167 @setCold(true);
168 try p.errors.append(p.gpa, msg);
169 }
170
171 fn fail(p: *Parser, tag: ast.Error.Tag) error{ ParseError, OutOfMemory } {
172 @setCold(true);
173 return p.failMsg(.{ .tag = tag, .token = p.tok_i });
174 }
175
176 fn failExpected(p: *Parser, expected_token: Token.Tag) error{ ParseError, OutOfMemory } {
177 @setCold(true);
178 return p.failMsg(.{
179 .tag = .expected_token,
180 .token = p.tok_i,
181 .extra = .{ .expected_tag = expected_token },
182 });
183 }
184
185 fn failMsg(p: *Parser, msg: ast.Error) error{ ParseError, OutOfMemory } {
186 @setCold(true);
187 try p.warnMsg(msg);
188 return error.ParseError;
93189 }
94190
95191 /// ContainerMembers
......@@ -99,176 +195,226 @@ const Parser = struct {
99195 /// / ContainerField COMMA ContainerMembers
100196 /// / ContainerField
101197 /// /
102 fn parseContainerMembers(p: *Parser, top_level: bool) ![]*Node {
103 var list = std.ArrayList(*Node).init(p.gpa);
198 /// TopLevelComptime <- KEYWORD_comptime BlockExpr
199 fn parseContainerMembers(p: *Parser) !Members {
200 var list = std.ArrayList(Node.Index).init(p.gpa);
104201 defer list.deinit();
105202
106203 var field_state: union(enum) {
107 /// no fields have been seen
204 /// No fields have been seen.
108205 none,
109 /// currently parsing fields
206 /// Currently parsing fields.
110207 seen,
111 /// saw fields and then a declaration after them.
112 /// payload is first token of previous declaration.
113 end: TokenIndex,
114 /// ther was a declaration between fields, don't report more errors
208 /// Saw fields and then a declaration after them.
209 /// Payload is first token of previous declaration.
210 end: Node.Index,
211 /// There was a declaration between fields, don't report more errors.
115212 err,
116213 } = .none;
117214
118 while (true) {
119 if (try p.parseContainerDocComments()) |node| {
120 try list.append(node);
121 continue;
122 }
123
124 const doc_comments = try p.parseDocComment();
215 // Skip container doc comments.
216 while (p.eatToken(.container_doc_comment)) |_| {}
125217
126 if (p.parseTestDecl() catch |err| switch (err) {
127 error.OutOfMemory => return error.OutOfMemory,
128 error.ParseError => {
129 p.findNextContainerMember();
130 continue;
218 var trailing = false;
219 while (true) {
220 const doc_comment = try p.eatDocComments();
221
222 switch (p.token_tags[p.tok_i]) {
223 .keyword_test => {
224 const test_decl_node = try p.expectTestDeclRecoverable();
225 if (test_decl_node != 0) {
226 if (field_state == .seen) {
227 field_state = .{ .end = test_decl_node };
228 }
229 try list.append(test_decl_node);
230 }
231 trailing = false;
131232 },
132 }) |node| {
133 if (field_state == .seen) {
134 field_state = .{ .end = node.firstToken() };
135 }
136 node.cast(Node.TestDecl).?.doc_comments = doc_comments;
137 try list.append(node);
138 continue;
139 }
140
141 if (p.parseTopLevelComptime() catch |err| switch (err) {
142 error.OutOfMemory => return error.OutOfMemory,
143 error.ParseError => {
144 p.findNextContainerMember();
145 continue;
233 .keyword_comptime => switch (p.token_tags[p.tok_i + 1]) {
234 .identifier => {
235 p.tok_i += 1;
236 const container_field = try p.expectContainerFieldRecoverable();
237 if (container_field != 0) {
238 switch (field_state) {
239 .none => field_state = .seen,
240 .err, .seen => {},
241 .end => |node| {
242 try p.warnMsg(.{
243 .tag = .decl_between_fields,
244 .token = p.nodes.items(.main_token)[node],
245 });
246 // Continue parsing; error will be reported later.
247 field_state = .err;
248 },
249 }
250 try list.append(container_field);
251 switch (p.token_tags[p.tok_i]) {
252 .comma => {
253 p.tok_i += 1;
254 trailing = true;
255 continue;
256 },
257 .r_brace, .eof => {
258 trailing = false;
259 break;
260 },
261 else => {},
262 }
263 // There is not allowed to be a decl after a field with no comma.
264 // Report error but recover parser.
265 try p.warnExpected(.comma);
266 p.findNextContainerMember();
267 }
268 },
269 .l_brace => {
270 const comptime_token = p.nextToken();
271 const block = p.parseBlock() catch |err| switch (err) {
272 error.OutOfMemory => return error.OutOfMemory,
273 error.ParseError => blk: {
274 p.findNextContainerMember();
275 break :blk null_node;
276 },
277 };
278 if (block != 0) {
279 const comptime_node = try p.addNode(.{
280 .tag = .@"comptime",
281 .main_token = comptime_token,
282 .data = .{
283 .lhs = block,
284 .rhs = undefined,
285 },
286 });
287 if (field_state == .seen) {
288 field_state = .{ .end = comptime_node };
289 }
290 try list.append(comptime_node);
291 }
292 trailing = false;
293 },
294 else => {
295 p.tok_i += 1;
296 try p.warn(.expected_block_or_field);
297 },
146298 },
147 }) |node| {
148 if (field_state == .seen) {
149 field_state = .{ .end = node.firstToken() };
150 }
151 node.cast(Node.Comptime).?.doc_comments = doc_comments;
152 try list.append(node);
153 continue;
154 }
155
156 const visib_token = p.eatToken(.Keyword_pub);
157
158 if (p.parseTopLevelDecl(doc_comments, visib_token) catch |err| switch (err) {
159 error.OutOfMemory => return error.OutOfMemory,
160 error.ParseError => {
161 p.findNextContainerMember();
162 continue;
299 .keyword_pub => {
300 p.tok_i += 1;
301 const top_level_decl = try p.expectTopLevelDeclRecoverable();
302 if (top_level_decl != 0) {
303 if (field_state == .seen) {
304 field_state = .{ .end = top_level_decl };
305 }
306 try list.append(top_level_decl);
307 }
308 trailing = p.token_tags[p.tok_i - 1] == .semicolon;
163309 },
164 }) |node| {
165 if (field_state == .seen) {
166 field_state = .{ .end = visib_token orelse node.firstToken() };
167 }
168 try list.append(node);
169 continue;
170 }
171
172 if (visib_token != null) {
173 try p.errors.append(p.gpa, .{
174 .ExpectedPubItem = .{ .token = p.tok_i },
175 });
176 // ignore this pub
177 continue;
178 }
179
180 if (p.parseContainerField() catch |err| switch (err) {
181 error.OutOfMemory => return error.OutOfMemory,
182 error.ParseError => {
183 // attempt to recover
184 p.findNextContainerMember();
185 continue;
310 .keyword_usingnamespace => {
311 const node = try p.expectUsingNamespaceRecoverable();
312 if (node != 0) {
313 if (field_state == .seen) {
314 field_state = .{ .end = node };
315 }
316 try list.append(node);
317 }
318 trailing = p.token_tags[p.tok_i - 1] == .semicolon;
319 },
320 .keyword_const,
321 .keyword_var,
322 .keyword_threadlocal,
323 .keyword_export,
324 .keyword_extern,
325 .keyword_inline,
326 .keyword_noinline,
327 .keyword_fn,
328 => {
329 const top_level_decl = try p.expectTopLevelDeclRecoverable();
330 if (top_level_decl != 0) {
331 if (field_state == .seen) {
332 field_state = .{ .end = top_level_decl };
333 }
334 try list.append(top_level_decl);
335 }
336 trailing = p.token_tags[p.tok_i - 1] == .semicolon;
337 },
338 .identifier => {
339 const container_field = try p.expectContainerFieldRecoverable();
340 if (container_field != 0) {
341 switch (field_state) {
342 .none => field_state = .seen,
343 .err, .seen => {},
344 .end => |node| {
345 try p.warnMsg(.{
346 .tag = .decl_between_fields,
347 .token = p.nodes.items(.main_token)[node],
348 });
349 // Continue parsing; error will be reported later.
350 field_state = .err;
351 },
352 }
353 try list.append(container_field);
354 switch (p.token_tags[p.tok_i]) {
355 .comma => {
356 p.tok_i += 1;
357 trailing = true;
358 continue;
359 },
360 .r_brace, .eof => {
361 trailing = false;
362 break;
363 },
364 else => {},
365 }
366 // There is not allowed to be a decl after a field with no comma.
367 // Report error but recover parser.
368 try p.warnExpected(.comma);
369 p.findNextContainerMember();
370 }
186371 },
187 }) |node| {
188 switch (field_state) {
189 .none => field_state = .seen,
190 .err, .seen => {},
191 .end => |tok| {
192 try p.errors.append(p.gpa, .{
193 .DeclBetweenFields = .{ .token = tok },
372 .eof, .r_brace => {
373 if (doc_comment) |tok| {
374 try p.warnMsg(.{
375 .tag = .unattached_doc_comment,
376 .token = tok,
194377 });
195 // continue parsing, error will be reported later
196 field_state = .err;
197 },
198 }
199
200 const field = node.cast(Node.ContainerField).?;
201 field.doc_comments = doc_comments;
202 try list.append(node);
203 const comma = p.eatToken(.Comma) orelse {
204 // try to continue parsing
205 const index = p.tok_i;
206 p.findNextContainerMember();
207 const next = p.token_ids[p.tok_i];
208 switch (next) {
209 .Eof => {
210 // no invalid tokens were found
211 if (index == p.tok_i) break;
212
213 // Invalid tokens, add error and exit
214 try p.errors.append(p.gpa, .{
215 .ExpectedToken = .{ .token = index, .expected_id = .Comma },
216 });
217 break;
218 },
219 else => {
220 if (next == .RBrace) {
221 if (!top_level) break;
222 _ = p.nextToken();
223 }
224
225 // add error and continue
226 try p.errors.append(p.gpa, .{
227 .ExpectedToken = .{ .token = index, .expected_id = .Comma },
228 });
229 continue;
230 },
231378 }
232 };
233 if (try p.parseAppendedDocComment(comma)) |appended_comment|
234 field.doc_comments = appended_comment;
235 continue;
236 }
237
238 // Dangling doc comment
239 if (doc_comments != null) {
240 try p.errors.append(p.gpa, .{
241 .UnattachedDocComment = .{ .token = doc_comments.?.firstToken() },
242 });
243 }
244
245 const next = p.token_ids[p.tok_i];
246 switch (next) {
247 .Eof => break,
248 .Keyword_comptime => {
249 _ = p.nextToken();
250 try p.errors.append(p.gpa, .{
251 .ExpectedBlockOrField = .{ .token = p.tok_i },
252 });
379 break;
253380 },
254381 else => {
255 const index = p.tok_i;
256 if (next == .RBrace) {
257 if (!top_level) break;
258 _ = p.nextToken();
259 }
260
261 // this was likely not supposed to end yet,
262 // try to find the next declaration
382 try p.warn(.expected_container_members);
383 // This was likely not supposed to end yet; try to find the next declaration.
263384 p.findNextContainerMember();
264 try p.errors.append(p.gpa, .{
265 .ExpectedContainerMembers = .{ .token = index },
266 });
267385 },
268386 }
269387 }
270388
271 return list.toOwnedSlice();
389 switch (list.items.len) {
390 0 => return Members{
391 .len = 0,
392 .lhs = 0,
393 .rhs = 0,
394 .trailing = trailing,
395 },
396 1 => return Members{
397 .len = 1,
398 .lhs = list.items[0],
399 .rhs = 0,
400 .trailing = trailing,
401 },
402 2 => return Members{
403 .len = 2,
404 .lhs = list.items[0],
405 .rhs = list.items[1],
406 .trailing = trailing,
407 },
408 else => {
409 const span = try p.listToSpan(list.items);
410 return Members{
411 .len = list.items.len,
412 .lhs = span.start,
413 .rhs = span.end,
414 .trailing = trailing,
415 };
416 },
417 }
272418 }
273419
274420 /// Attempts to find next container member by searching for certain tokens
......@@ -276,47 +422,52 @@ const Parser = struct {
276422 var level: u32 = 0;
277423 while (true) {
278424 const tok = p.nextToken();
279 switch (p.token_ids[tok]) {
280 // any of these can start a new top level declaration
281 .Keyword_test,
282 .Keyword_comptime,
283 .Keyword_pub,
284 .Keyword_export,
285 .Keyword_extern,
286 .Keyword_inline,
287 .Keyword_noinline,
288 .Keyword_usingnamespace,
289 .Keyword_threadlocal,
290 .Keyword_const,
291 .Keyword_var,
292 .Keyword_fn,
293 .Identifier,
425 switch (p.token_tags[tok]) {
426 // Any of these can start a new top level declaration.
427 .keyword_test,
428 .keyword_comptime,
429 .keyword_pub,
430 .keyword_export,
431 .keyword_extern,
432 .keyword_inline,
433 .keyword_noinline,
434 .keyword_usingnamespace,
435 .keyword_threadlocal,
436 .keyword_const,
437 .keyword_var,
438 .keyword_fn,
294439 => {
295440 if (level == 0) {
296 p.putBackToken(tok);
441 p.tok_i -= 1;
442 return;
443 }
444 },
445 .identifier => {
446 if (p.token_tags[tok + 1] == .comma and level == 0) {
447 p.tok_i -= 1;
297448 return;
298449 }
299450 },
300 .Comma, .Semicolon => {
451 .comma, .semicolon => {
301452 // this decl was likely meant to end here
302453 if (level == 0) {
303454 return;
304455 }
305456 },
306 .LParen, .LBracket, .LBrace => level += 1,
307 .RParen, .RBracket => {
457 .l_paren, .l_bracket, .l_brace => level += 1,
458 .r_paren, .r_bracket => {
308459 if (level != 0) level -= 1;
309460 },
310 .RBrace => {
461 .r_brace => {
311462 if (level == 0) {
312463 // end of container, exit
313 p.putBackToken(tok);
464 p.tok_i -= 1;
314465 return;
315466 }
316467 level -= 1;
317468 },
318 .Eof => {
319 p.putBackToken(tok);
469 .eof => {
470 p.tok_i -= 1;
320471 return;
321472 },
322473 else => {},
......@@ -329,22 +480,22 @@ const Parser = struct {
329480 var level: u32 = 0;
330481 while (true) {
331482 const tok = p.nextToken();
332 switch (p.token_ids[tok]) {
333 .LBrace => level += 1,
334 .RBrace => {
483 switch (p.token_tags[tok]) {
484 .l_brace => level += 1,
485 .r_brace => {
335486 if (level == 0) {
336 p.putBackToken(tok);
487 p.tok_i -= 1;
337488 return;
338489 }
339490 level -= 1;
340491 },
341 .Semicolon => {
492 .semicolon => {
342493 if (level == 0) {
343494 return;
344495 }
345496 },
346 .Eof => {
347 p.putBackToken(tok);
497 .eof => {
498 p.tok_i -= 1;
348499 return;
349500 },
350501 else => {},
......@@ -352,335 +503,337 @@ const Parser = struct {
352503 }
353504 }
354505
355 /// Eat a multiline container doc comment
356 fn parseContainerDocComments(p: *Parser) !?*Node {
357 if (p.eatToken(.ContainerDocComment)) |first_line| {
358 while (p.eatToken(.ContainerDocComment)) |_| {}
359 const node = try p.arena.allocator.create(Node.DocComment);
360 node.* = .{ .first_line = first_line };
361 return &node.base;
362 }
363 return null;
364 }
365
366 /// TestDecl <- KEYWORD_test STRINGLITERALSINGLE Block
367 fn parseTestDecl(p: *Parser) !?*Node {
368 const test_token = p.eatToken(.Keyword_test) orelse return null;
369 const name_node = try p.parseStringLiteralSingle();
370 const block_node = (try p.parseBlock(null)) orelse {
371 try p.errors.append(p.gpa, .{ .ExpectedLBrace = .{ .token = p.tok_i } });
372 return error.ParseError;
373 };
374
375 const test_node = try p.arena.allocator.create(Node.TestDecl);
376 test_node.* = .{
377 .doc_comments = null,
378 .test_token = test_token,
379 .name = name_node,
380 .body_node = block_node,
381 };
382 return &test_node.base;
383 }
384
385 /// TopLevelComptime <- KEYWORD_comptime BlockExpr
386 fn parseTopLevelComptime(p: *Parser) !?*Node {
387 const tok = p.eatToken(.Keyword_comptime) orelse return null;
388 const lbrace = p.eatToken(.LBrace) orelse {
389 p.putBackToken(tok);
390 return null;
391 };
392 p.putBackToken(lbrace);
393 const block_node = try p.expectNode(parseBlockExpr, .{
394 .ExpectedLabelOrLBrace = .{ .token = p.tok_i },
506 /// TestDecl <- KEYWORD_test STRINGLITERALSINGLE? Block
507 fn expectTestDecl(p: *Parser) !Node.Index {
508 const test_token = p.assertToken(.keyword_test);
509 const name_token = p.eatToken(.string_literal);
510 const block_node = try p.parseBlock();
511 if (block_node == 0) return p.fail(.expected_block);
512 return p.addNode(.{
513 .tag = .test_decl,
514 .main_token = test_token,
515 .data = .{
516 .lhs = name_token orelse 0,
517 .rhs = block_node,
518 },
395519 });
520 }
396521
397 const comptime_node = try p.arena.allocator.create(Node.Comptime);
398 comptime_node.* = .{
399 .doc_comments = null,
400 .comptime_token = tok,
401 .expr = block_node,
522 fn expectTestDeclRecoverable(p: *Parser) error{OutOfMemory}!Node.Index {
523 return p.expectTestDecl() catch |err| switch (err) {
524 error.OutOfMemory => return error.OutOfMemory,
525 error.ParseError => {
526 p.findNextContainerMember();
527 return null_node;
528 },
402529 };
403 return &comptime_node.base;
404530 }
405531
406532 /// TopLevelDecl
407533 /// <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / (KEYWORD_inline / KEYWORD_noinline))? FnProto (SEMICOLON / Block)
408534 /// / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl
409535 /// / KEYWORD_usingnamespace Expr SEMICOLON
410 fn parseTopLevelDecl(p: *Parser, doc_comments: ?*Node.DocComment, visib_token: ?TokenIndex) !?*Node {
411 var lib_name: ?*Node = null;
412 const extern_export_inline_token = blk: {
413 if (p.eatToken(.Keyword_export)) |token| break :blk token;
414 if (p.eatToken(.Keyword_extern)) |token| {
415 lib_name = try p.parseStringLiteralSingle();
416 break :blk token;
536 fn expectTopLevelDecl(p: *Parser) !Node.Index {
537 const extern_export_inline_token = p.nextToken();
538 var expect_fn: bool = false;
539 var expect_var_or_fn: bool = false;
540 switch (p.token_tags[extern_export_inline_token]) {
541 .keyword_extern => {
542 _ = p.eatToken(.string_literal);
543 expect_var_or_fn = true;
544 },
545 .keyword_export => expect_var_or_fn = true,
546 .keyword_inline, .keyword_noinline => expect_fn = true,
547 else => p.tok_i -= 1,
548 }
549 const fn_proto = try p.parseFnProto();
550 if (fn_proto != 0) {
551 switch (p.token_tags[p.tok_i]) {
552 .semicolon => {
553 p.tok_i += 1;
554 return fn_proto;
555 },
556 .l_brace => {
557 const body_block = try p.parseBlock();
558 assert(body_block != 0);
559 return p.addNode(.{
560 .tag = .fn_decl,
561 .main_token = p.nodes.items(.main_token)[fn_proto],
562 .data = .{
563 .lhs = fn_proto,
564 .rhs = body_block,
565 },
566 });
567 },
568 else => {
569 // Since parseBlock only return error.ParseError on
570 // a missing '}' we can assume this function was
571 // supposed to end here.
572 try p.warn(.expected_semi_or_lbrace);
573 return null_node;
574 },
417575 }
418 if (p.eatToken(.Keyword_inline)) |token| break :blk token;
419 if (p.eatToken(.Keyword_noinline)) |token| break :blk token;
420 break :blk null;
421 };
422
423 if (try p.parseFnProto(.top_level, .{
424 .doc_comments = doc_comments,
425 .visib_token = visib_token,
426 .extern_export_inline_token = extern_export_inline_token,
427 .lib_name = lib_name,
428 })) |node| {
429 return node;
430576 }
431
432 if (extern_export_inline_token) |token| {
433 if (p.token_ids[token] == .Keyword_inline or
434 p.token_ids[token] == .Keyword_noinline)
435 {
436 try p.errors.append(p.gpa, .{
437 .ExpectedFn = .{ .token = p.tok_i },
438 });
439 return error.ParseError;
440 }
577 if (expect_fn) {
578 try p.warn(.expected_fn);
579 return error.ParseError;
441580 }
442581
443 const thread_local_token = p.eatToken(.Keyword_threadlocal);
444
445 if (try p.parseVarDecl(.{
446 .doc_comments = doc_comments,
447 .visib_token = visib_token,
448 .thread_local_token = thread_local_token,
449 .extern_export_token = extern_export_inline_token,
450 .lib_name = lib_name,
451 })) |node| {
452 return node;
582 const thread_local_token = p.eatToken(.keyword_threadlocal);
583 const var_decl = try p.parseVarDecl();
584 if (var_decl != 0) {
585 const semicolon_token = try p.expectToken(.semicolon);
586 return var_decl;
453587 }
454
455588 if (thread_local_token != null) {
456 try p.errors.append(p.gpa, .{
457 .ExpectedVarDecl = .{ .token = p.tok_i },
458 });
459 // ignore this and try again;
460 return error.ParseError;
589 return p.fail(.expected_var_decl);
461590 }
462
463 if (extern_export_inline_token) |token| {
464 try p.errors.append(p.gpa, .{
465 .ExpectedVarDeclOrFn = .{ .token = p.tok_i },
466 });
467 // ignore this and try again;
468 return error.ParseError;
591 if (expect_var_or_fn) {
592 return p.fail(.expected_var_decl_or_fn);
469593 }
594 if (p.token_tags[p.tok_i] != .keyword_usingnamespace) {
595 return p.fail(.expected_pub_item);
596 }
597 return p.expectUsingNamespace();
598 }
470599
471 const use_token = p.eatToken(.Keyword_usingnamespace) orelse return null;
472 const expr = try p.expectNode(parseExpr, .{
473 .ExpectedExpr = .{ .token = p.tok_i },
474 });
475 const semicolon_token = try p.expectToken(.Semicolon);
476
477 const node = try p.arena.allocator.create(Node.Use);
478 node.* = .{
479 .doc_comments = doc_comments orelse try p.parseAppendedDocComment(semicolon_token),
480 .visib_token = visib_token,
481 .use_token = use_token,
482 .expr = expr,
483 .semicolon_token = semicolon_token,
600 fn expectTopLevelDeclRecoverable(p: *Parser) error{OutOfMemory}!Node.Index {
601 return p.expectTopLevelDecl() catch |err| switch (err) {
602 error.OutOfMemory => return error.OutOfMemory,
603 error.ParseError => {
604 p.findNextContainerMember();
605 return null_node;
606 },
484607 };
608 }
485609
486 return &node.base;
610 fn expectUsingNamespace(p: *Parser) !Node.Index {
611 const usingnamespace_token = p.assertToken(.keyword_usingnamespace);
612 const expr = try p.expectExpr();
613 const semicolon_token = try p.expectToken(.semicolon);
614 return p.addNode(.{
615 .tag = .@"usingnamespace",
616 .main_token = usingnamespace_token,
617 .data = .{
618 .lhs = expr,
619 .rhs = undefined,
620 },
621 });
487622 }
488623
489 /// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? CallConv? EXCLAMATIONMARK? (Keyword_anytype / TypeExpr)
490 fn parseFnProto(p: *Parser, level: enum { top_level, as_type }, fields: struct {
491 doc_comments: ?*Node.DocComment = null,
492 visib_token: ?TokenIndex = null,
493 extern_export_inline_token: ?TokenIndex = null,
494 lib_name: ?*Node = null,
495 }) !?*Node {
496 // TODO: Remove once extern/async/inline fn rewriting is
497 var is_extern_prototype: ?void = null;
498 var is_async: ?void = null;
499 var is_inline: ?void = null;
500 if (fields.extern_export_inline_token != null and
501 p.token_ids[fields.extern_export_inline_token.?] == .Keyword_inline)
502 {
503 is_inline = {};
504 }
505 const cc_token: ?TokenIndex = blk: {
506 if (p.eatToken(.Keyword_extern)) |token| {
507 is_extern_prototype = {};
508 break :blk token;
509 }
510 if (p.eatToken(.Keyword_async)) |token| {
511 is_async = {};
512 break :blk token;
513 }
514 break :blk null;
515 };
516 const fn_token = p.eatToken(.Keyword_fn) orelse {
517 if (cc_token) |token|
518 p.putBackToken(token);
519 return null;
624 fn expectUsingNamespaceRecoverable(p: *Parser) error{OutOfMemory}!Node.Index {
625 return p.expectUsingNamespace() catch |err| switch (err) {
626 error.OutOfMemory => return error.OutOfMemory,
627 error.ParseError => {
628 p.findNextContainerMember();
629 return null_node;
630 },
520631 };
521 const name_token = p.eatToken(.Identifier);
522 const lparen = try p.expectToken(.LParen);
632 }
633
634 /// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? CallConv? EXCLAMATIONMARK? (Keyword_anytype / TypeExpr)
635 fn parseFnProto(p: *Parser) !Node.Index {
636 const fn_token = p.eatToken(.keyword_fn) orelse return null_node;
637 _ = p.eatToken(.identifier);
523638 const params = try p.parseParamDeclList();
524 defer p.gpa.free(params);
525 const var_args_token = p.eatToken(.Ellipsis3);
526 const rparen = try p.expectToken(.RParen);
639 defer params.deinit(p.gpa);
527640 const align_expr = try p.parseByteAlign();
528641 const section_expr = try p.parseLinkSection();
529642 const callconv_expr = try p.parseCallconv();
530 const exclamation_token = p.eatToken(.Bang);
643 const bang_token = p.eatToken(.bang);
531644
532 const return_type_expr = (try p.parseAnyType()) orelse
533 try p.expectNodeRecoverable(parseTypeExpr, .{
645 const return_type_expr = try p.parseTypeExpr();
646 if (return_type_expr == 0) {
534647 // most likely the user forgot to specify the return type.
535648 // Mark return type as invalid and try to continue.
536 .ExpectedReturnType = .{ .token = p.tok_i },
537 });
649 try p.warn(.expected_return_type);
650 }
538651
539 // TODO https://github.com/ziglang/zig/issues/3750
540 const R = Node.FnProto.ReturnType;
541 const return_type = if (return_type_expr == null)
542 R{ .Invalid = rparen }
543 else if (exclamation_token != null)
544 R{ .InferErrorSet = return_type_expr.? }
545 else
546 R{ .Explicit = return_type_expr.? };
547
548 const body_node: ?*Node = switch (level) {
549 .top_level => blk: {
550 if (p.eatToken(.Semicolon)) |_| {
551 break :blk null;
552 }
553 const body_block = (try p.parseBlock(null)) orelse {
554 // Since parseBlock only return error.ParseError on
555 // a missing '}' we can assume this function was
556 // supposed to end here.
557 try p.errors.append(p.gpa, .{ .ExpectedSemiOrLBrace = .{ .token = p.tok_i } });
558 break :blk null;
559 };
560 break :blk body_block;
652 if (align_expr == 0 and section_expr == 0 and callconv_expr == 0) {
653 switch (params) {
654 .zero_or_one => |param| return p.addNode(.{
655 .tag = .fn_proto_simple,
656 .main_token = fn_token,
657 .data = .{
658 .lhs = param,
659 .rhs = return_type_expr,
660 },
661 }),
662 .multi => |list| {
663 const span = try p.listToSpan(list);
664 return p.addNode(.{
665 .tag = .fn_proto_multi,
666 .main_token = fn_token,
667 .data = .{
668 .lhs = try p.addExtra(Node.SubRange{
669 .start = span.start,
670 .end = span.end,
671 }),
672 .rhs = return_type_expr,
673 },
674 });
675 },
676 }
677 }
678 switch (params) {
679 .zero_or_one => |param| return p.addNode(.{
680 .tag = .fn_proto_one,
681 .main_token = fn_token,
682 .data = .{
683 .lhs = try p.addExtra(Node.FnProtoOne{
684 .param = param,
685 .align_expr = align_expr,
686 .section_expr = section_expr,
687 .callconv_expr = callconv_expr,
688 }),
689 .rhs = return_type_expr,
690 },
691 }),
692 .multi => |list| {
693 const span = try p.listToSpan(list);
694 return p.addNode(.{
695 .tag = .fn_proto,
696 .main_token = fn_token,
697 .data = .{
698 .lhs = try p.addExtra(Node.FnProto{
699 .params_start = span.start,
700 .params_end = span.end,
701 .align_expr = align_expr,
702 .section_expr = section_expr,
703 .callconv_expr = callconv_expr,
704 }),
705 .rhs = return_type_expr,
706 },
707 });
561708 },
562 .as_type => null,
563 };
564
565 const fn_proto_node = try Node.FnProto.create(&p.arena.allocator, .{
566 .params_len = params.len,
567 .fn_token = fn_token,
568 .return_type = return_type,
569 }, .{
570 .doc_comments = fields.doc_comments,
571 .visib_token = fields.visib_token,
572 .name_token = name_token,
573 .var_args_token = var_args_token,
574 .extern_export_inline_token = fields.extern_export_inline_token,
575 .body_node = body_node,
576 .lib_name = fields.lib_name,
577 .align_expr = align_expr,
578 .section_expr = section_expr,
579 .callconv_expr = callconv_expr,
580 .is_extern_prototype = is_extern_prototype,
581 .is_async = is_async,
582 .is_inline = is_inline,
583 });
584 std.mem.copy(Node.FnProto.ParamDecl, fn_proto_node.params(), params);
585
586 return &fn_proto_node.base;
709 }
587710 }
588711
589712 /// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON
590 fn parseVarDecl(p: *Parser, fields: struct {
591 doc_comments: ?*Node.DocComment = null,
592 visib_token: ?TokenIndex = null,
593 thread_local_token: ?TokenIndex = null,
594 extern_export_token: ?TokenIndex = null,
595 lib_name: ?*Node = null,
596 comptime_token: ?TokenIndex = null,
597 }) !?*Node {
598 const mut_token = p.eatToken(.Keyword_const) orelse
599 p.eatToken(.Keyword_var) orelse
600 return null;
713 fn parseVarDecl(p: *Parser) !Node.Index {
714 const mut_token = p.eatToken(.keyword_const) orelse
715 p.eatToken(.keyword_var) orelse
716 return null_node;
601717
602 const name_token = try p.expectToken(.Identifier);
603 const type_node = if (p.eatToken(.Colon) != null)
604 try p.expectNode(parseTypeExpr, .{
605 .ExpectedTypeExpr = .{ .token = p.tok_i },
606 })
607 else
608 null;
718 _ = try p.expectToken(.identifier);
719 const type_node: Node.Index = if (p.eatToken(.colon) == null) 0 else try p.expectTypeExpr();
609720 const align_node = try p.parseByteAlign();
610721 const section_node = try p.parseLinkSection();
611 const eq_token = p.eatToken(.Equal);
612 const init_node = if (eq_token != null) blk: {
613 break :blk try p.expectNode(parseExpr, .{
614 .ExpectedExpr = .{ .token = p.tok_i },
722 const init_node: Node.Index = if (p.eatToken(.equal) == null) 0 else try p.expectExpr();
723 if (section_node == 0) {
724 if (align_node == 0) {
725 return p.addNode(.{
726 .tag = .simple_var_decl,
727 .main_token = mut_token,
728 .data = .{
729 .lhs = type_node,
730 .rhs = init_node,
731 },
732 });
733 } else if (type_node == 0) {
734 return p.addNode(.{
735 .tag = .aligned_var_decl,
736 .main_token = mut_token,
737 .data = .{
738 .lhs = align_node,
739 .rhs = init_node,
740 },
741 });
742 } else {
743 return p.addNode(.{
744 .tag = .local_var_decl,
745 .main_token = mut_token,
746 .data = .{
747 .lhs = try p.addExtra(Node.LocalVarDecl{
748 .type_node = type_node,
749 .align_node = align_node,
750 }),
751 .rhs = init_node,
752 },
753 });
754 }
755 } else {
756 return p.addNode(.{
757 .tag = .global_var_decl,
758 .main_token = mut_token,
759 .data = .{
760 .lhs = try p.addExtra(Node.GlobalVarDecl{
761 .type_node = type_node,
762 .align_node = align_node,
763 .section_node = section_node,
764 }),
765 .rhs = init_node,
766 },
615767 });
616 } else null;
617 const semicolon_token = try p.expectToken(.Semicolon);
618
619 const doc_comments = fields.doc_comments orelse try p.parseAppendedDocComment(semicolon_token);
620
621 const node = try Node.VarDecl.create(&p.arena.allocator, .{
622 .mut_token = mut_token,
623 .name_token = name_token,
624 .semicolon_token = semicolon_token,
625 }, .{
626 .doc_comments = doc_comments,
627 .visib_token = fields.visib_token,
628 .thread_local_token = fields.thread_local_token,
629 .eq_token = eq_token,
630 .comptime_token = fields.comptime_token,
631 .extern_export_token = fields.extern_export_token,
632 .lib_name = fields.lib_name,
633 .type_node = type_node,
634 .align_node = align_node,
635 .section_node = section_node,
636 .init_node = init_node,
637 });
638 return &node.base;
768 }
639769 }
640770
641771 /// ContainerField <- KEYWORD_comptime? IDENTIFIER (COLON TypeExpr ByteAlign?)? (EQUAL Expr)?
642 fn parseContainerField(p: *Parser) !?*Node {
643 const comptime_token = p.eatToken(.Keyword_comptime);
644 const name_token = p.eatToken(.Identifier) orelse {
645 if (comptime_token) |t| p.putBackToken(t);
646 return null;
647 };
648
649 var align_expr: ?*Node = null;
650 var type_expr: ?*Node = null;
651 if (p.eatToken(.Colon)) |_| {
652 if (p.eatToken(.Keyword_anytype) orelse p.eatToken(.Keyword_var)) |anytype_tok| {
653 const node = try p.arena.allocator.create(Node.OneToken);
654 node.* = .{
655 .base = .{ .tag = .AnyType },
656 .token = anytype_tok,
657 };
658 type_expr = &node.base;
659 } else {
660 type_expr = try p.expectNode(parseTypeExpr, .{
661 .ExpectedTypeExpr = .{ .token = p.tok_i },
772 fn expectContainerField(p: *Parser) !Node.Index {
773 const comptime_token = p.eatToken(.keyword_comptime);
774 const name_token = p.assertToken(.identifier);
775
776 var align_expr: Node.Index = 0;
777 var type_expr: Node.Index = 0;
778 if (p.eatToken(.colon)) |_| {
779 if (p.eatToken(.keyword_anytype)) |anytype_tok| {
780 type_expr = try p.addNode(.{
781 .tag = .@"anytype",
782 .main_token = anytype_tok,
783 .data = .{
784 .lhs = undefined,
785 .rhs = undefined,
786 },
662787 });
788 } else {
789 type_expr = try p.expectTypeExpr();
663790 align_expr = try p.parseByteAlign();
664791 }
665792 }
666793
667 const value_expr = if (p.eatToken(.Equal)) |_|
668 try p.expectNode(parseExpr, .{
669 .ExpectedExpr = .{ .token = p.tok_i },
670 })
671 else
672 null;
673
674 const node = try p.arena.allocator.create(Node.ContainerField);
675 node.* = .{
676 .doc_comments = null,
677 .comptime_token = comptime_token,
678 .name_token = name_token,
679 .type_expr = type_expr,
680 .value_expr = value_expr,
681 .align_expr = align_expr,
794 const value_expr: Node.Index = if (p.eatToken(.equal) == null) 0 else try p.expectExpr();
795
796 if (align_expr == 0) {
797 return p.addNode(.{
798 .tag = .container_field_init,
799 .main_token = name_token,
800 .data = .{
801 .lhs = type_expr,
802 .rhs = value_expr,
803 },
804 });
805 } else if (value_expr == 0) {
806 return p.addNode(.{
807 .tag = .container_field_align,
808 .main_token = name_token,
809 .data = .{
810 .lhs = type_expr,
811 .rhs = align_expr,
812 },
813 });
814 } else {
815 return p.addNode(.{
816 .tag = .container_field,
817 .main_token = name_token,
818 .data = .{
819 .lhs = type_expr,
820 .rhs = try p.addExtra(Node.ContainerField{
821 .value_expr = value_expr,
822 .align_expr = align_expr,
823 }),
824 },
825 });
826 }
827 }
828
829 fn expectContainerFieldRecoverable(p: *Parser) error{OutOfMemory}!Node.Index {
830 return p.expectContainerField() catch |err| switch (err) {
831 error.OutOfMemory => return error.OutOfMemory,
832 error.ParseError => {
833 p.findNextContainerMember();
834 return null_node;
835 },
682836 };
683 return &node.base;
684837 }
685838
686839 /// Statement
......@@ -694,833 +847,1632 @@ const Parser = struct {
694847 /// / LabeledStatement
695848 /// / SwitchExpr
696849 /// / AssignExpr SEMICOLON
697 fn parseStatement(p: *Parser) Error!?*Node {
698 const comptime_token = p.eatToken(.Keyword_comptime);
850 fn parseStatement(p: *Parser) Error!Node.Index {
851 const comptime_token = p.eatToken(.keyword_comptime);
699852
700 if (try p.parseVarDecl(.{
701 .comptime_token = comptime_token,
702 })) |node| {
703 return node;
853 const var_decl = try p.parseVarDecl();
854 if (var_decl != 0) {
855 _ = try p.expectTokenRecoverable(.semicolon);
856 return var_decl;
704857 }
705858
706859 if (comptime_token) |token| {
707 const block_expr = try p.expectNode(parseBlockExprStatement, .{
708 .ExpectedBlockOrAssignment = .{ .token = p.tok_i },
709 });
710
711 const node = try p.arena.allocator.create(Node.Comptime);
712 node.* = .{
713 .doc_comments = null,
714 .comptime_token = token,
715 .expr = block_expr,
716 };
717 return &node.base;
718 }
719
720 if (p.eatToken(.Keyword_nosuspend)) |nosuspend_token| {
721 const block_expr = try p.expectNode(parseBlockExprStatement, .{
722 .ExpectedBlockOrAssignment = .{ .token = p.tok_i },
860 return p.addNode(.{
861 .tag = .@"comptime",
862 .main_token = token,
863 .data = .{
864 .lhs = try p.expectBlockExprStatement(),
865 .rhs = undefined,
866 },
723867 });
724
725 const node = try p.arena.allocator.create(Node.Nosuspend);
726 node.* = .{
727 .nosuspend_token = nosuspend_token,
728 .expr = block_expr,
729 };
730 return &node.base;
731868 }
732869
733 if (p.eatToken(.Keyword_suspend)) |suspend_token| {
734 const semicolon = p.eatToken(.Semicolon);
735
736 const body_node = if (semicolon == null) blk: {
737 break :blk try p.expectNode(parseBlockExprStatement, .{
738 .ExpectedBlockOrExpression = .{ .token = p.tok_i },
870 switch (p.token_tags[p.tok_i]) {
871 .keyword_nosuspend => {
872 return p.addNode(.{
873 .tag = .@"nosuspend",
874 .main_token = p.nextToken(),
875 .data = .{
876 .lhs = try p.expectBlockExprStatement(),
877 .rhs = undefined,
878 },
739879 });
740 } else null;
741
742 const node = try p.arena.allocator.create(Node.Suspend);
743 node.* = .{
744 .suspend_token = suspend_token,
745 .body = body_node,
746 };
747 return &node.base;
880 },
881 .keyword_suspend => {
882 const token = p.nextToken();
883 const block_expr: Node.Index = if (p.eatToken(.semicolon) != null)
884 0
885 else
886 try p.expectBlockExprStatement();
887 return p.addNode(.{
888 .tag = .@"suspend",
889 .main_token = token,
890 .data = .{
891 .lhs = block_expr,
892 .rhs = undefined,
893 },
894 });
895 },
896 .keyword_defer => return p.addNode(.{
897 .tag = .@"defer",
898 .main_token = p.nextToken(),
899 .data = .{
900 .lhs = undefined,
901 .rhs = try p.expectBlockExprStatement(),
902 },
903 }),
904 .keyword_errdefer => return p.addNode(.{
905 .tag = .@"errdefer",
906 .main_token = p.nextToken(),
907 .data = .{
908 .lhs = try p.parsePayload(),
909 .rhs = try p.expectBlockExprStatement(),
910 },
911 }),
912 .keyword_switch => return p.expectSwitchExpr(),
913 .keyword_if => return p.expectIfStatement(),
914 else => {},
748915 }
749916
750 const defer_token = p.eatToken(.Keyword_defer) orelse p.eatToken(.Keyword_errdefer);
751 if (defer_token) |token| {
752 const payload = if (p.token_ids[token] == .Keyword_errdefer)
753 try p.parsePayload()
754 else
755 null;
756 const expr_node = try p.expectNode(parseBlockExprStatement, .{
757 .ExpectedBlockOrExpression = .{ .token = p.tok_i },
758 });
759 const node = try p.arena.allocator.create(Node.Defer);
760 node.* = .{
761 .defer_token = token,
762 .expr = expr_node,
763 .payload = payload,
764 };
765 return &node.base;
766 }
917 const labeled_statement = try p.parseLabeledStatement();
918 if (labeled_statement != 0) return labeled_statement;
767919
768 if (try p.parseIfStatement()) |node| return node;
769 if (try p.parseLabeledStatement()) |node| return node;
770 if (try p.parseSwitchExpr()) |node| return node;
771 if (try p.parseAssignExpr()) |node| {
772 _ = try p.expectTokenRecoverable(.Semicolon);
773 return node;
920 const assign_expr = try p.parseAssignExpr();
921 if (assign_expr != 0) {
922 _ = try p.expectTokenRecoverable(.semicolon);
923 return assign_expr;
774924 }
775925
776 return null;
926 return null_node;
777927 }
778928
779 /// IfStatement
780 /// <- IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )?
781 /// / IfPrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement )
782 fn parseIfStatement(p: *Parser) !?*Node {
783 const if_node = (try p.parseIfPrefix()) orelse return null;
784 const if_prefix = if_node.cast(Node.If).?;
785
786 const block_expr = (try p.parseBlockExpr());
787 const assign_expr = if (block_expr == null)
788 try p.expectNode(parseAssignExpr, .{
789 .ExpectedBlockOrAssignment = .{ .token = p.tok_i },
790 })
791 else
792 null;
793
794 const semicolon = if (assign_expr != null) p.eatToken(.Semicolon) else null;
795
796 const else_node = if (semicolon == null) blk: {
797 const else_token = p.eatToken(.Keyword_else) orelse break :blk null;
798 const payload = try p.parsePayload();
799 const else_body = try p.expectNode(parseStatement, .{
800 .InvalidToken = .{ .token = p.tok_i },
801 });
929 fn expectStatement(p: *Parser) !Node.Index {
930 const statement = try p.parseStatement();
931 if (statement == 0) {
932 return p.fail(.expected_statement);
933 }
934 return statement;
935 }
802936
803 const node = try p.arena.allocator.create(Node.Else);
804 node.* = .{
805 .else_token = else_token,
806 .payload = payload,
807 .body = else_body,
937 /// If a parse error occurs, reports an error, but then finds the next statement
938 /// and returns that one instead. If a parse error occurs but there is no following
939 /// statement, returns 0.
940 fn expectStatementRecoverable(p: *Parser) error{OutOfMemory}!Node.Index {
941 while (true) {
942 return p.expectStatement() catch |err| switch (err) {
943 error.OutOfMemory => return error.OutOfMemory,
944 error.ParseError => {
945 p.findNextStmt(); // Try to skip to the next statement.
946 if (p.token_tags[p.tok_i] == .r_brace) return null_node;
947 continue;
948 },
808949 };
809
810 break :blk node;
811 } else null;
812
813 if (block_expr) |body| {
814 if_prefix.body = body;
815 if_prefix.@"else" = else_node;
816 return if_node;
817950 }
951 }
818952
819 if (assign_expr) |body| {
820 if_prefix.body = body;
821 if (semicolon != null) return if_node;
822 if (else_node != null) {
823 if_prefix.@"else" = else_node;
824 return if_node;
953 /// IfStatement
954 /// <- IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )?
955 /// / IfPrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement )
956 fn expectIfStatement(p: *Parser) !Node.Index {
957 const if_token = p.assertToken(.keyword_if);
958 _ = try p.expectToken(.l_paren);
959 const condition = try p.expectExpr();
960 _ = try p.expectToken(.r_paren);
961 const then_payload = try p.parsePtrPayload();
962
963 // TODO propose to change the syntax so that semicolons are always required
964 // inside if statements, even if there is an `else`.
965 var else_required = false;
966 const then_expr = blk: {
967 const block_expr = try p.parseBlockExpr();
968 if (block_expr != 0) break :blk block_expr;
969 const assign_expr = try p.parseAssignExpr();
970 if (assign_expr == 0) {
971 return p.fail(.expected_block_or_assignment);
825972 }
826 try p.errors.append(p.gpa, .{
827 .ExpectedSemiOrElse = .{ .token = p.tok_i },
973 if (p.eatToken(.semicolon)) |_| {
974 return p.addNode(.{
975 .tag = .if_simple,
976 .main_token = if_token,
977 .data = .{
978 .lhs = condition,
979 .rhs = assign_expr,
980 },
981 });
982 }
983 else_required = true;
984 break :blk assign_expr;
985 };
986 const else_token = p.eatToken(.keyword_else) orelse {
987 if (else_required) {
988 try p.warn(.expected_semi_or_else);
989 }
990 return p.addNode(.{
991 .tag = .if_simple,
992 .main_token = if_token,
993 .data = .{
994 .lhs = condition,
995 .rhs = then_expr,
996 },
828997 });
829 }
830
831 return if_node;
998 };
999 const else_payload = try p.parsePayload();
1000 const else_expr = try p.expectStatement();
1001 return p.addNode(.{
1002 .tag = .@"if",
1003 .main_token = if_token,
1004 .data = .{
1005 .lhs = condition,
1006 .rhs = try p.addExtra(Node.If{
1007 .then_expr = then_expr,
1008 .else_expr = else_expr,
1009 }),
1010 },
1011 });
8321012 }
8331013
8341014 /// LabeledStatement <- BlockLabel? (Block / LoopStatement)
835 fn parseLabeledStatement(p: *Parser) !?*Node {
836 var colon: TokenIndex = undefined;
837 const label_token = p.parseBlockLabel(&colon);
838
839 if (try p.parseBlock(label_token)) |node| return node;
840
841 if (try p.parseLoopStatement()) |node| {
842 if (node.cast(Node.For)) |for_node| {
843 for_node.label = label_token;
844 } else if (node.cast(Node.While)) |while_node| {
845 while_node.label = label_token;
846 } else unreachable;
847 return node;
848 }
1015 fn parseLabeledStatement(p: *Parser) !Node.Index {
1016 const label_token = p.parseBlockLabel();
1017 const block = try p.parseBlock();
1018 if (block != 0) return block;
8491019
850 if (label_token != null) {
851 try p.errors.append(p.gpa, .{
852 .ExpectedLabelable = .{ .token = p.tok_i },
853 });
854 return error.ParseError;
1020 const loop_stmt = try p.parseLoopStatement();
1021 if (loop_stmt != 0) return loop_stmt;
1022
1023 if (label_token != 0) {
1024 return p.fail(.expected_labelable);
8551025 }
8561026
857 return null;
1027 return null_node;
8581028 }
8591029
8601030 /// LoopStatement <- KEYWORD_inline? (ForStatement / WhileStatement)
861 fn parseLoopStatement(p: *Parser) !?*Node {
862 const inline_token = p.eatToken(.Keyword_inline);
1031 fn parseLoopStatement(p: *Parser) !Node.Index {
1032 const inline_token = p.eatToken(.keyword_inline);
8631033
864 if (try p.parseForStatement()) |node| {
865 node.cast(Node.For).?.inline_token = inline_token;
866 return node;
867 }
1034 const for_statement = try p.parseForStatement();
1035 if (for_statement != 0) return for_statement;
8681036
869 if (try p.parseWhileStatement()) |node| {
870 node.cast(Node.While).?.inline_token = inline_token;
871 return node;
872 }
873 if (inline_token == null) return null;
1037 const while_statement = try p.parseWhileStatement();
1038 if (while_statement != 0) return while_statement;
1039
1040 if (inline_token == null) return null_node;
8741041
8751042 // If we've seen "inline", there should have been a "for" or "while"
876 try p.errors.append(p.gpa, .{
877 .ExpectedInlinable = .{ .token = p.tok_i },
878 });
879 return error.ParseError;
1043 return p.fail(.expected_inlinable);
8801044 }
8811045
1046 /// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload
8821047 /// ForStatement
8831048 /// <- ForPrefix BlockExpr ( KEYWORD_else Statement )?
8841049 /// / ForPrefix AssignExpr ( SEMICOLON / KEYWORD_else Statement )
885 fn parseForStatement(p: *Parser) !?*Node {
886 const node = (try p.parseForPrefix()) orelse return null;
887 const for_prefix = node.cast(Node.For).?;
888
889 if (try p.parseBlockExpr()) |block_expr_node| {
890 for_prefix.body = block_expr_node;
891
892 if (p.eatToken(.Keyword_else)) |else_token| {
893 const statement_node = try p.expectNode(parseStatement, .{
894 .InvalidToken = .{ .token = p.tok_i },
1050 fn parseForStatement(p: *Parser) !Node.Index {
1051 const for_token = p.eatToken(.keyword_for) orelse return null_node;
1052 _ = try p.expectToken(.l_paren);
1053 const array_expr = try p.expectExpr();
1054 _ = try p.expectToken(.r_paren);
1055 const found_payload = try p.parsePtrIndexPayload();
1056 if (found_payload == 0) try p.warn(.expected_loop_payload);
1057
1058 // TODO propose to change the syntax so that semicolons are always required
1059 // inside while statements, even if there is an `else`.
1060 var else_required = false;
1061 const then_expr = blk: {
1062 const block_expr = try p.parseBlockExpr();
1063 if (block_expr != 0) break :blk block_expr;
1064 const assign_expr = try p.parseAssignExpr();
1065 if (assign_expr == 0) {
1066 return p.fail(.expected_block_or_assignment);
1067 }
1068 if (p.eatToken(.semicolon)) |_| {
1069 return p.addNode(.{
1070 .tag = .for_simple,
1071 .main_token = for_token,
1072 .data = .{
1073 .lhs = array_expr,
1074 .rhs = assign_expr,
1075 },
8951076 });
896
897 const else_node = try p.arena.allocator.create(Node.Else);
898 else_node.* = .{
899 .else_token = else_token,
900 .payload = null,
901 .body = statement_node,
902 };
903 for_prefix.@"else" = else_node;
904
905 return node;
9061077 }
907
908 return node;
909 }
910
911 for_prefix.body = try p.expectNode(parseAssignExpr, .{
912 .ExpectedBlockOrAssignment = .{ .token = p.tok_i },
913 });
914
915 if (p.eatToken(.Semicolon) != null) return node;
916
917 if (p.eatToken(.Keyword_else)) |else_token| {
918 const statement_node = try p.expectNode(parseStatement, .{
919 .ExpectedStatement = .{ .token = p.tok_i },
1078 else_required = true;
1079 break :blk assign_expr;
1080 };
1081 const else_token = p.eatToken(.keyword_else) orelse {
1082 if (else_required) {
1083 try p.warn(.expected_semi_or_else);
1084 }
1085 return p.addNode(.{
1086 .tag = .for_simple,
1087 .main_token = for_token,
1088 .data = .{
1089 .lhs = array_expr,
1090 .rhs = then_expr,
1091 },
9201092 });
921
922 const else_node = try p.arena.allocator.create(Node.Else);
923 else_node.* = .{
924 .else_token = else_token,
925 .payload = null,
926 .body = statement_node,
927 };
928 for_prefix.@"else" = else_node;
929 return node;
930 }
931
932 try p.errors.append(p.gpa, .{
933 .ExpectedSemiOrElse = .{ .token = p.tok_i },
1093 };
1094 return p.addNode(.{
1095 .tag = .@"for",
1096 .main_token = for_token,
1097 .data = .{
1098 .lhs = array_expr,
1099 .rhs = try p.addExtra(Node.If{
1100 .then_expr = then_expr,
1101 .else_expr = try p.expectStatement(),
1102 }),
1103 },
9341104 });
935
936 return node;
9371105 }
9381106
1107 /// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr?
9391108 /// WhileStatement
9401109 /// <- WhilePrefix BlockExpr ( KEYWORD_else Payload? Statement )?
9411110 /// / WhilePrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement )
942 fn parseWhileStatement(p: *Parser) !?*Node {
943 const node = (try p.parseWhilePrefix()) orelse return null;
944 const while_prefix = node.cast(Node.While).?;
945
946 if (try p.parseBlockExpr()) |block_expr_node| {
947 while_prefix.body = block_expr_node;
948
949 if (p.eatToken(.Keyword_else)) |else_token| {
950 const payload = try p.parsePayload();
951
952 const statement_node = try p.expectNode(parseStatement, .{
953 .InvalidToken = .{ .token = p.tok_i },
1111 fn parseWhileStatement(p: *Parser) !Node.Index {
1112 const while_token = p.eatToken(.keyword_while) orelse return null_node;
1113 _ = try p.expectToken(.l_paren);
1114 const condition = try p.expectExpr();
1115 _ = try p.expectToken(.r_paren);
1116 const then_payload = try p.parsePtrPayload();
1117 const cont_expr = try p.parseWhileContinueExpr();
1118
1119 // TODO propose to change the syntax so that semicolons are always required
1120 // inside while statements, even if there is an `else`.
1121 var else_required = false;
1122 const then_expr = blk: {
1123 const block_expr = try p.parseBlockExpr();
1124 if (block_expr != 0) break :blk block_expr;
1125 const assign_expr = try p.parseAssignExpr();
1126 if (assign_expr == 0) {
1127 return p.fail(.expected_block_or_assignment);
1128 }
1129 if (p.eatToken(.semicolon)) |_| {
1130 if (cont_expr == 0) {
1131 return p.addNode(.{
1132 .tag = .while_simple,
1133 .main_token = while_token,
1134 .data = .{
1135 .lhs = condition,
1136 .rhs = assign_expr,
1137 },
1138 });
1139 } else {
1140 return p.addNode(.{
1141 .tag = .while_cont,
1142 .main_token = while_token,
1143 .data = .{
1144 .lhs = condition,
1145 .rhs = try p.addExtra(Node.WhileCont{
1146 .cont_expr = cont_expr,
1147 .then_expr = assign_expr,
1148 }),
1149 },
1150 });
1151 }
1152 }
1153 else_required = true;
1154 break :blk assign_expr;
1155 };
1156 const else_token = p.eatToken(.keyword_else) orelse {
1157 if (else_required) {
1158 try p.warn(.expected_semi_or_else);
1159 }
1160 if (cont_expr == 0) {
1161 return p.addNode(.{
1162 .tag = .while_simple,
1163 .main_token = while_token,
1164 .data = .{
1165 .lhs = condition,
1166 .rhs = then_expr,
1167 },
1168 });
1169 } else {
1170 return p.addNode(.{
1171 .tag = .while_cont,
1172 .main_token = while_token,
1173 .data = .{
1174 .lhs = condition,
1175 .rhs = try p.addExtra(Node.WhileCont{
1176 .cont_expr = cont_expr,
1177 .then_expr = then_expr,
1178 }),
1179 },
9541180 });
955
956 const else_node = try p.arena.allocator.create(Node.Else);
957 else_node.* = .{
958 .else_token = else_token,
959 .payload = payload,
960 .body = statement_node,
961 };
962 while_prefix.@"else" = else_node;
963
964 return node;
9651181 }
966
967 return node;
968 }
969
970 while_prefix.body = try p.expectNode(parseAssignExpr, .{
971 .ExpectedBlockOrAssignment = .{ .token = p.tok_i },
972 });
973
974 if (p.eatToken(.Semicolon) != null) return node;
975
976 if (p.eatToken(.Keyword_else)) |else_token| {
977 const payload = try p.parsePayload();
978
979 const statement_node = try p.expectNode(parseStatement, .{
980 .ExpectedStatement = .{ .token = p.tok_i },
981 });
982
983 const else_node = try p.arena.allocator.create(Node.Else);
984 else_node.* = .{
985 .else_token = else_token,
986 .payload = payload,
987 .body = statement_node,
988 };
989 while_prefix.@"else" = else_node;
990 return node;
991 }
992
993 try p.errors.append(p.gpa, .{
994 .ExpectedSemiOrElse = .{ .token = p.tok_i },
1182 };
1183 const else_payload = try p.parsePayload();
1184 const else_expr = try p.expectStatement();
1185 return p.addNode(.{
1186 .tag = .@"while",
1187 .main_token = while_token,
1188 .data = .{
1189 .lhs = condition,
1190 .rhs = try p.addExtra(Node.While{
1191 .cont_expr = cont_expr,
1192 .then_expr = then_expr,
1193 .else_expr = else_expr,
1194 }),
1195 },
9951196 });
996
997 return node;
9981197 }
9991198
10001199 /// BlockExprStatement
10011200 /// <- BlockExpr
10021201 /// / AssignExpr SEMICOLON
1003 fn parseBlockExprStatement(p: *Parser) !?*Node {
1004 if (try p.parseBlockExpr()) |node| return node;
1005 if (try p.parseAssignExpr()) |node| {
1006 _ = try p.expectTokenRecoverable(.Semicolon);
1007 return node;
1202 fn parseBlockExprStatement(p: *Parser) !Node.Index {
1203 const block_expr = try p.parseBlockExpr();
1204 if (block_expr != 0) {
1205 return block_expr;
10081206 }
1009 return null;
1207 const assign_expr = try p.parseAssignExpr();
1208 if (assign_expr != 0) {
1209 _ = try p.expectTokenRecoverable(.semicolon);
1210 return assign_expr;
1211 }
1212 return null_node;
10101213 }
10111214
1012 /// BlockExpr <- BlockLabel? Block
1013 fn parseBlockExpr(p: *Parser) Error!?*Node {
1014 var colon: TokenIndex = undefined;
1015 const label_token = p.parseBlockLabel(&colon);
1016 const block_node = (try p.parseBlock(label_token)) orelse {
1017 if (label_token) |label| {
1018 p.putBackToken(label + 1); // ":"
1019 p.putBackToken(label); // IDENTIFIER
1020 }
1021 return null;
1022 };
1023 return block_node;
1215 fn expectBlockExprStatement(p: *Parser) !Node.Index {
1216 const node = try p.parseBlockExprStatement();
1217 if (node == 0) {
1218 return p.fail(.expected_block_or_expr);
1219 }
1220 return node;
10241221 }
10251222
1026 /// AssignExpr <- Expr (AssignOp Expr)?
1027 fn parseAssignExpr(p: *Parser) !?*Node {
1028 return p.parseBinOpExpr(parseAssignOp, parseExpr, .Once);
1223 /// BlockExpr <- BlockLabel? Block
1224 fn parseBlockExpr(p: *Parser) Error!Node.Index {
1225 switch (p.token_tags[p.tok_i]) {
1226 .identifier => {
1227 if (p.token_tags[p.tok_i + 1] == .colon and
1228 p.token_tags[p.tok_i + 2] == .l_brace)
1229 {
1230 p.tok_i += 2;
1231 return p.parseBlock();
1232 } else {
1233 return null_node;
1234 }
1235 },
1236 .l_brace => return p.parseBlock(),
1237 else => return null_node,
1238 }
10291239 }
10301240
1031 /// Expr <- BoolOrExpr
1032 fn parseExpr(p: *Parser) Error!?*Node {
1033 return p.parsePrefixOpExpr(parseTry, parseBoolOrExpr);
1241 /// AssignExpr <- Expr (AssignOp Expr)?
1242 /// AssignOp
1243 /// <- ASTERISKEQUAL
1244 /// / SLASHEQUAL
1245 /// / PERCENTEQUAL
1246 /// / PLUSEQUAL
1247 /// / MINUSEQUAL
1248 /// / LARROW2EQUAL
1249 /// / RARROW2EQUAL
1250 /// / AMPERSANDEQUAL
1251 /// / CARETEQUAL
1252 /// / PIPEEQUAL
1253 /// / ASTERISKPERCENTEQUAL
1254 /// / PLUSPERCENTEQUAL
1255 /// / MINUSPERCENTEQUAL
1256 /// / EQUAL
1257 fn parseAssignExpr(p: *Parser) !Node.Index {
1258 const expr = try p.parseExpr();
1259 if (expr == 0) return null_node;
1260
1261 const tag: Node.Tag = switch (p.token_tags[p.tok_i]) {
1262 .asterisk_equal => .assign_mul,
1263 .slash_equal => .assign_div,
1264 .percent_equal => .assign_mod,
1265 .plus_equal => .assign_add,
1266 .minus_equal => .assign_sub,
1267 .angle_bracket_angle_bracket_left_equal => .assign_bit_shift_left,
1268 .angle_bracket_angle_bracket_right_equal => .assign_bit_shift_right,
1269 .ampersand_equal => .assign_bit_and,
1270 .caret_equal => .assign_bit_xor,
1271 .pipe_equal => .assign_bit_or,
1272 .asterisk_percent_equal => .assign_mul_wrap,
1273 .plus_percent_equal => .assign_add_wrap,
1274 .minus_percent_equal => .assign_sub_wrap,
1275 .equal => .assign,
1276 else => return expr,
1277 };
1278 return p.addNode(.{
1279 .tag = tag,
1280 .main_token = p.nextToken(),
1281 .data = .{
1282 .lhs = expr,
1283 .rhs = try p.expectExpr(),
1284 },
1285 });
10341286 }
10351287
1036 /// BoolOrExpr <- BoolAndExpr (KEYWORD_or BoolAndExpr)*
1037 fn parseBoolOrExpr(p: *Parser) !?*Node {
1038 return p.parseBinOpExpr(
1039 SimpleBinOpParseFn(.Keyword_or, .BoolOr),
1040 parseBoolAndExpr,
1041 .Infinitely,
1042 );
1288 fn expectAssignExpr(p: *Parser) !Node.Index {
1289 const expr = try p.parseAssignExpr();
1290 if (expr == 0) {
1291 return p.fail(.expected_expr_or_assignment);
1292 }
1293 return expr;
10431294 }
10441295
1045 /// BoolAndExpr <- CompareExpr (KEYWORD_and CompareExpr)*
1046 fn parseBoolAndExpr(p: *Parser) !?*Node {
1047 return p.parseBinOpExpr(
1048 SimpleBinOpParseFn(.Keyword_and, .BoolAnd),
1049 parseCompareExpr,
1050 .Infinitely,
1051 );
1296 /// Expr <- BoolOrExpr
1297 fn parseExpr(p: *Parser) Error!Node.Index {
1298 return p.parseBoolOrExpr();
10521299 }
10531300
1054 /// CompareExpr <- BitwiseExpr (CompareOp BitwiseExpr)?
1055 fn parseCompareExpr(p: *Parser) !?*Node {
1056 return p.parseBinOpExpr(parseCompareOp, parseBitwiseExpr, .Once);
1301 fn expectExpr(p: *Parser) Error!Node.Index {
1302 const node = try p.parseExpr();
1303 if (node == 0) {
1304 return p.fail(.expected_expr);
1305 } else {
1306 return node;
1307 }
10571308 }
10581309
1059 /// BitwiseExpr <- BitShiftExpr (BitwiseOp BitShiftExpr)*
1060 fn parseBitwiseExpr(p: *Parser) !?*Node {
1061 return p.parseBinOpExpr(parseBitwiseOp, parseBitShiftExpr, .Infinitely);
1062 }
1310 /// BoolOrExpr <- BoolAndExpr (KEYWORD_or BoolAndExpr)*
1311 fn parseBoolOrExpr(p: *Parser) Error!Node.Index {
1312 var res = try p.parseBoolAndExpr();
1313 if (res == 0) return null_node;
10631314
1064 /// BitShiftExpr <- AdditionExpr (BitShiftOp AdditionExpr)*
1065 fn parseBitShiftExpr(p: *Parser) !?*Node {
1066 return p.parseBinOpExpr(parseBitShiftOp, parseAdditionExpr, .Infinitely);
1315 while (true) {
1316 switch (p.token_tags[p.tok_i]) {
1317 .keyword_or => {
1318 const or_token = p.nextToken();
1319 const rhs = try p.parseBoolAndExpr();
1320 if (rhs == 0) {
1321 return p.fail(.invalid_token);
1322 }
1323 res = try p.addNode(.{
1324 .tag = .bool_or,
1325 .main_token = or_token,
1326 .data = .{
1327 .lhs = res,
1328 .rhs = rhs,
1329 },
1330 });
1331 },
1332 else => return res,
1333 }
1334 }
10671335 }
10681336
1069 /// AdditionExpr <- MultiplyExpr (AdditionOp MultiplyExpr)*
1070 fn parseAdditionExpr(p: *Parser) !?*Node {
1071 return p.parseBinOpExpr(parseAdditionOp, parseMultiplyExpr, .Infinitely);
1072 }
1337 /// BoolAndExpr <- CompareExpr (KEYWORD_and CompareExpr)*
1338 fn parseBoolAndExpr(p: *Parser) !Node.Index {
1339 var res = try p.parseCompareExpr();
1340 if (res == 0) return null_node;
10731341
1074 /// MultiplyExpr <- PrefixExpr (MultiplyOp PrefixExpr)*
1075 fn parseMultiplyExpr(p: *Parser) !?*Node {
1076 return p.parseBinOpExpr(parseMultiplyOp, parsePrefixExpr, .Infinitely);
1342 while (true) {
1343 switch (p.token_tags[p.tok_i]) {
1344 .keyword_and => {
1345 const and_token = p.nextToken();
1346 const rhs = try p.parseCompareExpr();
1347 if (rhs == 0) {
1348 return p.fail(.invalid_token);
1349 }
1350 res = try p.addNode(.{
1351 .tag = .bool_and,
1352 .main_token = and_token,
1353 .data = .{
1354 .lhs = res,
1355 .rhs = rhs,
1356 },
1357 });
1358 },
1359 .invalid_ampersands => {
1360 try p.warn(.invalid_and);
1361 p.tok_i += 1;
1362 return p.parseCompareExpr();
1363 },
1364 else => return res,
1365 }
1366 }
10771367 }
10781368
1079 /// PrefixExpr <- PrefixOp* PrimaryExpr
1080 fn parsePrefixExpr(p: *Parser) !?*Node {
1081 return p.parsePrefixOpExpr(parsePrefixOp, parsePrimaryExpr);
1369 /// CompareExpr <- BitwiseExpr (CompareOp BitwiseExpr)?
1370 /// CompareOp
1371 /// <- EQUALEQUAL
1372 /// / EXCLAMATIONMARKEQUAL
1373 /// / LARROW
1374 /// / RARROW
1375 /// / LARROWEQUAL
1376 /// / RARROWEQUAL
1377 fn parseCompareExpr(p: *Parser) !Node.Index {
1378 const expr = try p.parseBitwiseExpr();
1379 if (expr == 0) return null_node;
1380
1381 const tag: Node.Tag = switch (p.token_tags[p.tok_i]) {
1382 .equal_equal => .equal_equal,
1383 .bang_equal => .bang_equal,
1384 .angle_bracket_left => .less_than,
1385 .angle_bracket_right => .greater_than,
1386 .angle_bracket_left_equal => .less_or_equal,
1387 .angle_bracket_right_equal => .greater_or_equal,
1388 else => return expr,
1389 };
1390 return p.addNode(.{
1391 .tag = tag,
1392 .main_token = p.nextToken(),
1393 .data = .{
1394 .lhs = expr,
1395 .rhs = try p.expectBitwiseExpr(),
1396 },
1397 });
10821398 }
10831399
1084 /// PrimaryExpr
1085 /// <- AsmExpr
1086 /// / IfExpr
1087 /// / KEYWORD_break BreakLabel? Expr?
1088 /// / KEYWORD_comptime Expr
1089 /// / KEYWORD_nosuspend Expr
1090 /// / KEYWORD_continue BreakLabel?
1091 /// / KEYWORD_resume Expr
1092 /// / KEYWORD_return Expr?
1093 /// / BlockLabel? LoopExpr
1094 /// / Block
1095 /// / CurlySuffixExpr
1096 fn parsePrimaryExpr(p: *Parser) !?*Node {
1097 if (try p.parseAsmExpr()) |node| return node;
1098 if (try p.parseIfExpr()) |node| return node;
1099
1100 if (p.eatToken(.Keyword_break)) |token| {
1101 const label = try p.parseBreakLabel();
1102 const expr_node = try p.parseExpr();
1103 const node = try Node.ControlFlowExpression.create(&p.arena.allocator, .{
1104 .tag = .Break,
1105 .ltoken = token,
1106 }, .{
1107 .label = label,
1108 .rhs = expr_node,
1109 });
1110 return &node.base;
1111 }
1400 /// BitwiseExpr <- BitShiftExpr (BitwiseOp BitShiftExpr)*
1401 /// BitwiseOp
1402 /// <- AMPERSAND
1403 /// / CARET
1404 /// / PIPE
1405 /// / KEYWORD_orelse
1406 /// / KEYWORD_catch Payload?
1407 fn parseBitwiseExpr(p: *Parser) !Node.Index {
1408 var res = try p.parseBitShiftExpr();
1409 if (res == 0) return null_node;
11121410
1113 if (p.eatToken(.Keyword_comptime)) |token| {
1114 const expr_node = try p.expectNode(parseExpr, .{
1115 .ExpectedExpr = .{ .token = p.tok_i },
1116 });
1117 const node = try p.arena.allocator.create(Node.Comptime);
1118 node.* = .{
1119 .doc_comments = null,
1120 .comptime_token = token,
1121 .expr = expr_node,
1411 while (true) {
1412 const tag: Node.Tag = switch (p.token_tags[p.tok_i]) {
1413 .ampersand => .bit_and,
1414 .caret => .bit_xor,
1415 .pipe => .bit_or,
1416 .keyword_orelse => .@"orelse",
1417 .keyword_catch => {
1418 const catch_token = p.nextToken();
1419 _ = try p.parsePayload();
1420 const rhs = try p.parseBitShiftExpr();
1421 if (rhs == 0) {
1422 return p.fail(.invalid_token);
1423 }
1424 res = try p.addNode(.{
1425 .tag = .@"catch",
1426 .main_token = catch_token,
1427 .data = .{
1428 .lhs = res,
1429 .rhs = rhs,
1430 },
1431 });
1432 continue;
1433 },
1434 else => return res,
11221435 };
1123 return &node.base;
1124 }
1125
1126 if (p.eatToken(.Keyword_nosuspend)) |token| {
1127 const expr_node = try p.expectNode(parseExpr, .{
1128 .ExpectedExpr = .{ .token = p.tok_i },
1436 res = try p.addNode(.{
1437 .tag = tag,
1438 .main_token = p.nextToken(),
1439 .data = .{
1440 .lhs = res,
1441 .rhs = try p.expectBitShiftExpr(),
1442 },
11291443 });
1130 const node = try p.arena.allocator.create(Node.Nosuspend);
1131 node.* = .{
1132 .nosuspend_token = token,
1133 .expr = expr_node,
1134 };
1135 return &node.base;
11361444 }
1445 }
11371446
1138 if (p.eatToken(.Keyword_continue)) |token| {
1139 const label = try p.parseBreakLabel();
1140 const node = try Node.ControlFlowExpression.create(&p.arena.allocator, .{
1141 .tag = .Continue,
1142 .ltoken = token,
1143 }, .{
1144 .label = label,
1145 .rhs = null,
1146 });
1147 return &node.base;
1447 fn expectBitwiseExpr(p: *Parser) Error!Node.Index {
1448 const node = try p.parseBitwiseExpr();
1449 if (node == 0) {
1450 return p.fail(.invalid_token);
1451 } else {
1452 return node;
11481453 }
1454 }
11491455
1150 if (p.eatToken(.Keyword_resume)) |token| {
1151 const expr_node = try p.expectNode(parseExpr, .{
1152 .ExpectedExpr = .{ .token = p.tok_i },
1153 });
1154 const node = try p.arena.allocator.create(Node.SimplePrefixOp);
1155 node.* = .{
1156 .base = .{ .tag = .Resume },
1157 .op_token = token,
1158 .rhs = expr_node,
1159 };
1160 return &node.base;
1161 }
1456 /// BitShiftExpr <- AdditionExpr (BitShiftOp AdditionExpr)*
1457 /// BitShiftOp
1458 /// <- LARROW2
1459 /// / RARROW2
1460 fn parseBitShiftExpr(p: *Parser) Error!Node.Index {
1461 var res = try p.parseAdditionExpr();
1462 if (res == 0) return null_node;
11621463
1163 if (p.eatToken(.Keyword_return)) |token| {
1164 const expr_node = try p.parseExpr();
1165 const node = try Node.ControlFlowExpression.create(&p.arena.allocator, .{
1166 .tag = .Return,
1167 .ltoken = token,
1168 }, .{
1169 .rhs = expr_node,
1464 while (true) {
1465 const tag: Node.Tag = switch (p.token_tags[p.tok_i]) {
1466 .angle_bracket_angle_bracket_left => .bit_shift_left,
1467 .angle_bracket_angle_bracket_right => .bit_shift_right,
1468 else => return res,
1469 };
1470 res = try p.addNode(.{
1471 .tag = tag,
1472 .main_token = p.nextToken(),
1473 .data = .{
1474 .lhs = res,
1475 .rhs = try p.expectAdditionExpr(),
1476 },
11701477 });
1171 return &node.base;
11721478 }
1479 }
11731480
1174 var colon: TokenIndex = undefined;
1175 const label = p.parseBlockLabel(&colon);
1176 if (try p.parseLoopExpr()) |node| {
1177 if (node.cast(Node.For)) |for_node| {
1178 for_node.label = label;
1179 } else if (node.cast(Node.While)) |while_node| {
1180 while_node.label = label;
1181 } else unreachable;
1481 fn expectBitShiftExpr(p: *Parser) Error!Node.Index {
1482 const node = try p.parseBitShiftExpr();
1483 if (node == 0) {
1484 return p.fail(.invalid_token);
1485 } else {
11821486 return node;
11831487 }
1184 if (label) |token| {
1185 p.putBackToken(token + 1); // ":"
1186 p.putBackToken(token); // IDENTIFIER
1187 }
1188
1189 if (try p.parseBlock(null)) |node| return node;
1190 if (try p.parseCurlySuffixExpr()) |node| return node;
1488 }
11911489
1192 return null;
1193 }
1490 /// AdditionExpr <- MultiplyExpr (AdditionOp MultiplyExpr)*
1491 /// AdditionOp
1492 /// <- PLUS
1493 /// / MINUS
1494 /// / PLUS2
1495 /// / PLUSPERCENT
1496 /// / MINUSPERCENT
1497 fn parseAdditionExpr(p: *Parser) Error!Node.Index {
1498 var res = try p.parseMultiplyExpr();
1499 if (res == 0) return null_node;
11941500
1195 /// IfExpr <- IfPrefix Expr (KEYWORD_else Payload? Expr)?
1196 fn parseIfExpr(p: *Parser) !?*Node {
1197 return p.parseIf(parseExpr);
1501 while (true) {
1502 const tag: Node.Tag = switch (p.token_tags[p.tok_i]) {
1503 .plus => .add,
1504 .minus => .sub,
1505 .plus_plus => .array_cat,
1506 .plus_percent => .add_wrap,
1507 .minus_percent => .sub_wrap,
1508 else => return res,
1509 };
1510 res = try p.addNode(.{
1511 .tag = tag,
1512 .main_token = p.nextToken(),
1513 .data = .{
1514 .lhs = res,
1515 .rhs = try p.expectMultiplyExpr(),
1516 },
1517 });
1518 }
11981519 }
11991520
1200 /// Block <- LBRACE Statement* RBRACE
1201 fn parseBlock(p: *Parser, label_token: ?TokenIndex) !?*Node {
1202 const lbrace = p.eatToken(.LBrace) orelse return null;
1521 fn expectAdditionExpr(p: *Parser) Error!Node.Index {
1522 const node = try p.parseAdditionExpr();
1523 if (node == 0) {
1524 return p.fail(.invalid_token);
1525 }
1526 return node;
1527 }
12031528
1204 var statements = std.ArrayList(*Node).init(p.gpa);
1205 defer statements.deinit();
1529 /// MultiplyExpr <- PrefixExpr (MultiplyOp PrefixExpr)*
1530 /// MultiplyOp
1531 /// <- PIPE2
1532 /// / ASTERISK
1533 /// / SLASH
1534 /// / PERCENT
1535 /// / ASTERISK2
1536 /// / ASTERISKPERCENT
1537 fn parseMultiplyExpr(p: *Parser) Error!Node.Index {
1538 var res = try p.parsePrefixExpr();
1539 if (res == 0) return null_node;
12061540
12071541 while (true) {
1208 const statement = (p.parseStatement() catch |err| switch (err) {
1209 error.OutOfMemory => return error.OutOfMemory,
1210 error.ParseError => {
1211 // try to skip to the next statement
1212 p.findNextStmt();
1213 continue;
1542 const tag: Node.Tag = switch (p.token_tags[p.tok_i]) {
1543 .pipe_pipe => .merge_error_sets,
1544 .asterisk => .mul,
1545 .slash => .div,
1546 .percent => .mod,
1547 .asterisk_asterisk => .array_mult,
1548 .asterisk_percent => .mul_wrap,
1549 else => return res,
1550 };
1551 res = try p.addNode(.{
1552 .tag = tag,
1553 .main_token = p.nextToken(),
1554 .data = .{
1555 .lhs = res,
1556 .rhs = try p.expectPrefixExpr(),
12141557 },
1215 }) orelse break;
1216 try statements.append(statement);
1558 });
12171559 }
1560 }
12181561
1219 const rbrace = try p.expectToken(.RBrace);
1220
1221 const statements_len = @intCast(NodeIndex, statements.items.len);
1222
1223 if (label_token) |label| {
1224 const block_node = try Node.LabeledBlock.alloc(&p.arena.allocator, statements_len);
1225 block_node.* = .{
1226 .label = label,
1227 .lbrace = lbrace,
1228 .statements_len = statements_len,
1229 .rbrace = rbrace,
1230 };
1231 std.mem.copy(*Node, block_node.statements(), statements.items);
1232 return &block_node.base;
1233 } else {
1234 const block_node = try Node.Block.alloc(&p.arena.allocator, statements_len);
1235 block_node.* = .{
1236 .lbrace = lbrace,
1237 .statements_len = statements_len,
1238 .rbrace = rbrace,
1239 };
1240 std.mem.copy(*Node, block_node.statements(), statements.items);
1241 return &block_node.base;
1562 fn expectMultiplyExpr(p: *Parser) Error!Node.Index {
1563 const node = try p.parseMultiplyExpr();
1564 if (node == 0) {
1565 return p.fail(.invalid_token);
12421566 }
1567 return node;
12431568 }
12441569
1245 /// LoopExpr <- KEYWORD_inline? (ForExpr / WhileExpr)
1246 fn parseLoopExpr(p: *Parser) !?*Node {
1247 const inline_token = p.eatToken(.Keyword_inline);
1570 /// PrefixExpr <- PrefixOp* PrimaryExpr
1571 /// PrefixOp
1572 /// <- EXCLAMATIONMARK
1573 /// / MINUS
1574 /// / TILDE
1575 /// / MINUSPERCENT
1576 /// / AMPERSAND
1577 /// / KEYWORD_try
1578 /// / KEYWORD_await
1579 fn parsePrefixExpr(p: *Parser) Error!Node.Index {
1580 const tag: Node.Tag = switch (p.token_tags[p.tok_i]) {
1581 .bang => .bool_not,
1582 .minus => .negation,
1583 .tilde => .bit_not,
1584 .minus_percent => .negation_wrap,
1585 .ampersand => .address_of,
1586 .keyword_try => .@"try",
1587 .keyword_await => .@"await",
1588 else => return p.parsePrimaryExpr(),
1589 };
1590 return p.addNode(.{
1591 .tag = tag,
1592 .main_token = p.nextToken(),
1593 .data = .{
1594 .lhs = try p.expectPrefixExpr(),
1595 .rhs = undefined,
1596 },
1597 });
1598 }
12481599
1249 if (try p.parseForExpr()) |node| {
1250 node.cast(Node.For).?.inline_token = inline_token;
1251 return node;
1600 fn expectPrefixExpr(p: *Parser) Error!Node.Index {
1601 const node = try p.parsePrefixExpr();
1602 if (node == 0) {
1603 return p.fail(.expected_prefix_expr);
12521604 }
1605 return node;
1606 }
12531607
1254 if (try p.parseWhileExpr()) |node| {
1255 node.cast(Node.While).?.inline_token = inline_token;
1256 return node;
1608 /// TypeExpr <- PrefixTypeOp* ErrorUnionExpr
1609 /// PrefixTypeOp
1610 /// <- QUESTIONMARK
1611 /// / KEYWORD_anyframe MINUSRARROW
1612 /// / ArrayTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*
1613 /// / PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*
1614 /// PtrTypeStart
1615 /// <- ASTERISK
1616 /// / ASTERISK2
1617 /// / LBRACKET ASTERISK (LETTERC / COLON Expr)? RBRACKET
1618 /// ArrayTypeStart <- LBRACKET Expr? (COLON Expr)? RBRACKET
1619 fn parseTypeExpr(p: *Parser) Error!Node.Index {
1620 switch (p.token_tags[p.tok_i]) {
1621 .question_mark => return p.addNode(.{
1622 .tag = .optional_type,
1623 .main_token = p.nextToken(),
1624 .data = .{
1625 .lhs = try p.expectTypeExpr(),
1626 .rhs = undefined,
1627 },
1628 }),
1629 .keyword_anyframe => switch (p.token_tags[p.tok_i + 1]) {
1630 .arrow => return p.addNode(.{
1631 .tag = .anyframe_type,
1632 .main_token = p.nextToken(),
1633 .data = .{
1634 .lhs = p.nextToken(),
1635 .rhs = try p.expectTypeExpr(),
1636 },
1637 }),
1638 else => return p.parseErrorUnionExpr(),
1639 },
1640 .asterisk => {
1641 const asterisk = p.nextToken();
1642 const mods = try p.parsePtrModifiers();
1643 const elem_type = try p.expectTypeExpr();
1644 if (mods.bit_range_start == 0) {
1645 return p.addNode(.{
1646 .tag = .ptr_type_aligned,
1647 .main_token = asterisk,
1648 .data = .{
1649 .lhs = mods.align_node,
1650 .rhs = elem_type,
1651 },
1652 });
1653 } else {
1654 return p.addNode(.{
1655 .tag = .ptr_type_bit_range,
1656 .main_token = asterisk,
1657 .data = .{
1658 .lhs = try p.addExtra(Node.PtrTypeBitRange{
1659 .sentinel = 0,
1660 .align_node = mods.align_node,
1661 .bit_range_start = mods.bit_range_start,
1662 .bit_range_end = mods.bit_range_end,
1663 }),
1664 .rhs = elem_type,
1665 },
1666 });
1667 }
1668 },
1669 .asterisk_asterisk => {
1670 const asterisk = p.nextToken();
1671 const mods = try p.parsePtrModifiers();
1672 const elem_type = try p.expectTypeExpr();
1673 const inner: Node.Index = inner: {
1674 if (mods.bit_range_start == 0) {
1675 break :inner try p.addNode(.{
1676 .tag = .ptr_type_aligned,
1677 .main_token = asterisk,
1678 .data = .{
1679 .lhs = mods.align_node,
1680 .rhs = elem_type,
1681 },
1682 });
1683 } else {
1684 break :inner try p.addNode(.{
1685 .tag = .ptr_type_bit_range,
1686 .main_token = asterisk,
1687 .data = .{
1688 .lhs = try p.addExtra(Node.PtrTypeBitRange{
1689 .sentinel = 0,
1690 .align_node = mods.align_node,
1691 .bit_range_start = mods.bit_range_start,
1692 .bit_range_end = mods.bit_range_end,
1693 }),
1694 .rhs = elem_type,
1695 },
1696 });
1697 }
1698 };
1699 return p.addNode(.{
1700 .tag = .ptr_type_aligned,
1701 .main_token = asterisk,
1702 .data = .{
1703 .lhs = 0,
1704 .rhs = inner,
1705 },
1706 });
1707 },
1708 .l_bracket => switch (p.token_tags[p.tok_i + 1]) {
1709 .asterisk => {
1710 const lbracket = p.nextToken();
1711 const asterisk = p.nextToken();
1712 var sentinel: Node.Index = 0;
1713 prefix: {
1714 if (p.eatToken(.identifier)) |ident| {
1715 const token_slice = p.source[p.token_starts[ident]..][0..2];
1716 if (!std.mem.eql(u8, token_slice, "c]")) {
1717 p.tok_i -= 1;
1718 } else {
1719 break :prefix;
1720 }
1721 }
1722 if (p.eatToken(.colon)) |_| {
1723 sentinel = try p.expectExpr();
1724 }
1725 }
1726 _ = try p.expectToken(.r_bracket);
1727 const mods = try p.parsePtrModifiers();
1728 const elem_type = try p.expectTypeExpr();
1729 if (mods.bit_range_start == 0) {
1730 if (sentinel == 0) {
1731 return p.addNode(.{
1732 .tag = .ptr_type_aligned,
1733 .main_token = asterisk,
1734 .data = .{
1735 .lhs = mods.align_node,
1736 .rhs = elem_type,
1737 },
1738 });
1739 } else if (mods.align_node == 0) {
1740 return p.addNode(.{
1741 .tag = .ptr_type_sentinel,
1742 .main_token = asterisk,
1743 .data = .{
1744 .lhs = sentinel,
1745 .rhs = elem_type,
1746 },
1747 });
1748 } else {
1749 return p.addNode(.{
1750 .tag = .ptr_type,
1751 .main_token = asterisk,
1752 .data = .{
1753 .lhs = try p.addExtra(Node.PtrType{
1754 .sentinel = sentinel,
1755 .align_node = mods.align_node,
1756 }),
1757 .rhs = elem_type,
1758 },
1759 });
1760 }
1761 } else {
1762 return p.addNode(.{
1763 .tag = .ptr_type_bit_range,
1764 .main_token = asterisk,
1765 .data = .{
1766 .lhs = try p.addExtra(Node.PtrTypeBitRange{
1767 .sentinel = sentinel,
1768 .align_node = mods.align_node,
1769 .bit_range_start = mods.bit_range_start,
1770 .bit_range_end = mods.bit_range_end,
1771 }),
1772 .rhs = elem_type,
1773 },
1774 });
1775 }
1776 },
1777 else => {
1778 const lbracket = p.nextToken();
1779 const len_expr = try p.parseExpr();
1780 const sentinel: Node.Index = if (p.eatToken(.colon)) |_|
1781 try p.expectExpr()
1782 else
1783 0;
1784 _ = try p.expectToken(.r_bracket);
1785 const mods = try p.parsePtrModifiers();
1786 const elem_type = try p.expectTypeExpr();
1787 if (mods.bit_range_start != 0) {
1788 try p.warnMsg(.{
1789 .tag = .invalid_bit_range,
1790 .token = p.nodes.items(.main_token)[mods.bit_range_start],
1791 });
1792 }
1793 if (len_expr == 0) {
1794 if (sentinel == 0) {
1795 return p.addNode(.{
1796 .tag = .ptr_type_aligned,
1797 .main_token = lbracket,
1798 .data = .{
1799 .lhs = mods.align_node,
1800 .rhs = elem_type,
1801 },
1802 });
1803 } else if (mods.align_node == 0) {
1804 return p.addNode(.{
1805 .tag = .ptr_type_sentinel,
1806 .main_token = lbracket,
1807 .data = .{
1808 .lhs = sentinel,
1809 .rhs = elem_type,
1810 },
1811 });
1812 } else {
1813 return p.addNode(.{
1814 .tag = .ptr_type,
1815 .main_token = lbracket,
1816 .data = .{
1817 .lhs = try p.addExtra(Node.PtrType{
1818 .sentinel = sentinel,
1819 .align_node = mods.align_node,
1820 }),
1821 .rhs = elem_type,
1822 },
1823 });
1824 }
1825 } else {
1826 if (mods.align_node != 0) {
1827 try p.warnMsg(.{
1828 .tag = .invalid_align,
1829 .token = p.nodes.items(.main_token)[mods.align_node],
1830 });
1831 }
1832 if (sentinel == 0) {
1833 return p.addNode(.{
1834 .tag = .array_type,
1835 .main_token = lbracket,
1836 .data = .{
1837 .lhs = len_expr,
1838 .rhs = elem_type,
1839 },
1840 });
1841 } else {
1842 return p.addNode(.{
1843 .tag = .array_type_sentinel,
1844 .main_token = lbracket,
1845 .data = .{
1846 .lhs = len_expr,
1847 .rhs = try p.addExtra(.{
1848 .elem_type = elem_type,
1849 .sentinel = sentinel,
1850 }),
1851 },
1852 });
1853 }
1854 }
1855 },
1856 },
1857 else => return p.parseErrorUnionExpr(),
12571858 }
1859 }
12581860
1259 if (inline_token == null) return null;
1260
1261 // If we've seen "inline", there should have been a "for" or "while"
1262 try p.errors.append(p.gpa, .{
1263 .ExpectedInlinable = .{ .token = p.tok_i },
1264 });
1265 return error.ParseError;
1861 fn expectTypeExpr(p: *Parser) Error!Node.Index {
1862 const node = try p.parseTypeExpr();
1863 if (node == 0) {
1864 return p.fail(.expected_type_expr);
1865 }
1866 return node;
12661867 }
12671868
1268 /// ForExpr <- ForPrefix Expr (KEYWORD_else Expr)?
1269 fn parseForExpr(p: *Parser) !?*Node {
1270 const node = (try p.parseForPrefix()) orelse return null;
1271 const for_prefix = node.cast(Node.For).?;
1869 /// PrimaryExpr
1870 /// <- AsmExpr
1871 /// / IfExpr
1872 /// / KEYWORD_break BreakLabel? Expr?
1873 /// / KEYWORD_comptime Expr
1874 /// / KEYWORD_nosuspend Expr
1875 /// / KEYWORD_continue BreakLabel?
1876 /// / KEYWORD_resume Expr
1877 /// / KEYWORD_return Expr?
1878 /// / BlockLabel? LoopExpr
1879 /// / Block
1880 /// / CurlySuffixExpr
1881 fn parsePrimaryExpr(p: *Parser) !Node.Index {
1882 switch (p.token_tags[p.tok_i]) {
1883 .keyword_asm => return p.expectAsmExpr(),
1884 .keyword_if => return p.parseIfExpr(),
1885 .keyword_break => {
1886 p.tok_i += 1;
1887 return p.addNode(.{
1888 .tag = .@"break",
1889 .main_token = p.tok_i - 1,
1890 .data = .{
1891 .lhs = try p.parseBreakLabel(),
1892 .rhs = try p.parseExpr(),
1893 },
1894 });
1895 },
1896 .keyword_continue => {
1897 p.tok_i += 1;
1898 return p.addNode(.{
1899 .tag = .@"continue",
1900 .main_token = p.tok_i - 1,
1901 .data = .{
1902 .lhs = try p.parseBreakLabel(),
1903 .rhs = undefined,
1904 },
1905 });
1906 },
1907 .keyword_comptime => {
1908 p.tok_i += 1;
1909 return p.addNode(.{
1910 .tag = .@"comptime",
1911 .main_token = p.tok_i - 1,
1912 .data = .{
1913 .lhs = try p.expectExpr(),
1914 .rhs = undefined,
1915 },
1916 });
1917 },
1918 .keyword_nosuspend => {
1919 p.tok_i += 1;
1920 return p.addNode(.{
1921 .tag = .@"nosuspend",
1922 .main_token = p.tok_i - 1,
1923 .data = .{
1924 .lhs = try p.expectExpr(),
1925 .rhs = undefined,
1926 },
1927 });
1928 },
1929 .keyword_resume => {
1930 p.tok_i += 1;
1931 return p.addNode(.{
1932 .tag = .@"resume",
1933 .main_token = p.tok_i - 1,
1934 .data = .{
1935 .lhs = try p.expectExpr(),
1936 .rhs = undefined,
1937 },
1938 });
1939 },
1940 .keyword_return => {
1941 p.tok_i += 1;
1942 return p.addNode(.{
1943 .tag = .@"return",
1944 .main_token = p.tok_i - 1,
1945 .data = .{
1946 .lhs = try p.parseExpr(),
1947 .rhs = undefined,
1948 },
1949 });
1950 },
1951 .identifier => {
1952 if (p.token_tags[p.tok_i + 1] == .colon) {
1953 switch (p.token_tags[p.tok_i + 2]) {
1954 .keyword_inline => {
1955 p.tok_i += 3;
1956 switch (p.token_tags[p.tok_i]) {
1957 .keyword_for => return p.parseForExpr(),
1958 .keyword_while => return p.parseWhileExpr(),
1959 else => return p.fail(.expected_inlinable),
1960 }
1961 },
1962 .keyword_for => {
1963 p.tok_i += 2;
1964 return p.parseForExpr();
1965 },
1966 .keyword_while => {
1967 p.tok_i += 2;
1968 return p.parseWhileExpr();
1969 },
1970 .l_brace => {
1971 p.tok_i += 2;
1972 return p.parseBlock();
1973 },
1974 else => return p.parseCurlySuffixExpr(),
1975 }
1976 } else {
1977 return p.parseCurlySuffixExpr();
1978 }
1979 },
1980 .keyword_inline => {
1981 p.tok_i += 2;
1982 switch (p.token_tags[p.tok_i]) {
1983 .keyword_for => return p.parseForExpr(),
1984 .keyword_while => return p.parseWhileExpr(),
1985 else => return p.fail(.expected_inlinable),
1986 }
1987 },
1988 .keyword_for => return p.parseForExpr(),
1989 .keyword_while => return p.parseWhileExpr(),
1990 .l_brace => return p.parseBlock(),
1991 else => return p.parseCurlySuffixExpr(),
1992 }
1993 }
12721994
1273 const body_node = try p.expectNode(parseExpr, .{
1274 .ExpectedExpr = .{ .token = p.tok_i },
1275 });
1276 for_prefix.body = body_node;
1995 /// IfExpr <- IfPrefix Expr (KEYWORD_else Payload? Expr)?
1996 fn parseIfExpr(p: *Parser) !Node.Index {
1997 return p.parseIf(parseExpr);
1998 }
12771999
1278 if (p.eatToken(.Keyword_else)) |else_token| {
1279 const body = try p.expectNode(parseExpr, .{
1280 .ExpectedExpr = .{ .token = p.tok_i },
2000 /// Block <- LBRACE Statement* RBRACE
2001 fn parseBlock(p: *Parser) !Node.Index {
2002 const lbrace = p.eatToken(.l_brace) orelse return null_node;
2003
2004 if (p.eatToken(.r_brace)) |_| {
2005 return p.addNode(.{
2006 .tag = .block_two,
2007 .main_token = lbrace,
2008 .data = .{
2009 .lhs = 0,
2010 .rhs = 0,
2011 },
12812012 });
2013 }
12822014
1283 const else_node = try p.arena.allocator.create(Node.Else);
1284 else_node.* = .{
1285 .else_token = else_token,
1286 .payload = null,
1287 .body = body,
1288 };
1289
1290 for_prefix.@"else" = else_node;
2015 const stmt_one = try p.expectStatementRecoverable();
2016 if (p.eatToken(.r_brace)) |_| {
2017 const semicolon = p.token_tags[p.tok_i - 2] == .semicolon;
2018 return p.addNode(.{
2019 .tag = if (semicolon) .block_two_semicolon else .block_two,
2020 .main_token = lbrace,
2021 .data = .{
2022 .lhs = stmt_one,
2023 .rhs = 0,
2024 },
2025 });
2026 }
2027 const stmt_two = try p.expectStatementRecoverable();
2028 if (p.eatToken(.r_brace)) |_| {
2029 const semicolon = p.token_tags[p.tok_i - 2] == .semicolon;
2030 return p.addNode(.{
2031 .tag = if (semicolon) .block_two_semicolon else .block_two,
2032 .main_token = lbrace,
2033 .data = .{
2034 .lhs = stmt_one,
2035 .rhs = stmt_two,
2036 },
2037 });
12912038 }
12922039
1293 return node;
1294 }
2040 var statements = std.ArrayList(Node.Index).init(p.gpa);
2041 defer statements.deinit();
12952042
1296 /// WhileExpr <- WhilePrefix Expr (KEYWORD_else Payload? Expr)?
1297 fn parseWhileExpr(p: *Parser) !?*Node {
1298 const node = (try p.parseWhilePrefix()) orelse return null;
1299 const while_prefix = node.cast(Node.While).?;
2043 try statements.appendSlice(&.{ stmt_one, stmt_two });
13002044
1301 const body_node = try p.expectNode(parseExpr, .{
1302 .ExpectedExpr = .{ .token = p.tok_i },
2045 while (true) {
2046 const statement = try p.expectStatementRecoverable();
2047 if (statement == 0) break;
2048 try statements.append(statement);
2049 if (p.token_tags[p.tok_i] == .r_brace) break;
2050 }
2051 _ = try p.expectToken(.r_brace);
2052 const semicolon = p.token_tags[p.tok_i - 2] == .semicolon;
2053 const statements_span = try p.listToSpan(statements.items);
2054 return p.addNode(.{
2055 .tag = if (semicolon) .block_semicolon else .block,
2056 .main_token = lbrace,
2057 .data = .{
2058 .lhs = statements_span.start,
2059 .rhs = statements_span.end,
2060 },
13032061 });
1304 while_prefix.body = body_node;
2062 }
13052063
1306 if (p.eatToken(.Keyword_else)) |else_token| {
1307 const payload = try p.parsePayload();
1308 const body = try p.expectNode(parseExpr, .{
1309 .ExpectedExpr = .{ .token = p.tok_i },
2064 /// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload
2065 /// ForExpr <- ForPrefix Expr (KEYWORD_else Expr)?
2066 fn parseForExpr(p: *Parser) !Node.Index {
2067 const for_token = p.eatToken(.keyword_for) orelse return null_node;
2068 _ = try p.expectToken(.l_paren);
2069 const array_expr = try p.expectExpr();
2070 _ = try p.expectToken(.r_paren);
2071 const found_payload = try p.parsePtrIndexPayload();
2072 if (found_payload == 0) try p.warn(.expected_loop_payload);
2073
2074 const then_expr = try p.expectExpr();
2075 const else_token = p.eatToken(.keyword_else) orelse {
2076 return p.addNode(.{
2077 .tag = .for_simple,
2078 .main_token = for_token,
2079 .data = .{
2080 .lhs = array_expr,
2081 .rhs = then_expr,
2082 },
13102083 });
1311
1312 const else_node = try p.arena.allocator.create(Node.Else);
1313 else_node.* = .{
1314 .else_token = else_token,
1315 .payload = payload,
1316 .body = body,
1317 };
1318
1319 while_prefix.@"else" = else_node;
1320 }
1321
1322 return node;
2084 };
2085 const else_expr = try p.expectExpr();
2086 return p.addNode(.{
2087 .tag = .@"for",
2088 .main_token = for_token,
2089 .data = .{
2090 .lhs = array_expr,
2091 .rhs = try p.addExtra(Node.If{
2092 .then_expr = then_expr,
2093 .else_expr = else_expr,
2094 }),
2095 },
2096 });
13232097 }
13242098
1325 /// CurlySuffixExpr <- TypeExpr InitList?
1326 fn parseCurlySuffixExpr(p: *Parser) !?*Node {
1327 const lhs = (try p.parseTypeExpr()) orelse return null;
1328 const suffix_op = (try p.parseInitList(lhs)) orelse return lhs;
1329 return suffix_op;
2099 /// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr?
2100 /// WhileExpr <- WhilePrefix Expr (KEYWORD_else Payload? Expr)?
2101 fn parseWhileExpr(p: *Parser) !Node.Index {
2102 const while_token = p.eatToken(.keyword_while) orelse return null_node;
2103 _ = try p.expectToken(.l_paren);
2104 const condition = try p.expectExpr();
2105 _ = try p.expectToken(.r_paren);
2106 const then_payload = try p.parsePtrPayload();
2107 const cont_expr = try p.parseWhileContinueExpr();
2108
2109 const then_expr = try p.expectExpr();
2110 const else_token = p.eatToken(.keyword_else) orelse {
2111 if (cont_expr == 0) {
2112 return p.addNode(.{
2113 .tag = .while_simple,
2114 .main_token = while_token,
2115 .data = .{
2116 .lhs = condition,
2117 .rhs = then_expr,
2118 },
2119 });
2120 } else {
2121 return p.addNode(.{
2122 .tag = .while_cont,
2123 .main_token = while_token,
2124 .data = .{
2125 .lhs = condition,
2126 .rhs = try p.addExtra(Node.WhileCont{
2127 .cont_expr = cont_expr,
2128 .then_expr = then_expr,
2129 }),
2130 },
2131 });
2132 }
2133 };
2134 const else_payload = try p.parsePayload();
2135 const else_expr = try p.expectExpr();
2136 return p.addNode(.{
2137 .tag = .@"while",
2138 .main_token = while_token,
2139 .data = .{
2140 .lhs = condition,
2141 .rhs = try p.addExtra(Node.While{
2142 .cont_expr = cont_expr,
2143 .then_expr = then_expr,
2144 .else_expr = else_expr,
2145 }),
2146 },
2147 });
13302148 }
13312149
2150 /// CurlySuffixExpr <- TypeExpr InitList?
13322151 /// InitList
13332152 /// <- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE
13342153 /// / LBRACE Expr (COMMA Expr)* COMMA? RBRACE
13352154 /// / LBRACE RBRACE
1336 fn parseInitList(p: *Parser, lhs: *Node) !?*Node {
1337 const lbrace = p.eatToken(.LBrace) orelse return null;
1338 var init_list = std.ArrayList(*Node).init(p.gpa);
1339 defer init_list.deinit();
2155 fn parseCurlySuffixExpr(p: *Parser) !Node.Index {
2156 const lhs = try p.parseTypeExpr();
2157 if (lhs == 0) return null_node;
2158 const lbrace = p.eatToken(.l_brace) orelse return lhs;
2159
2160 // If there are 0 or 1 items, we can use ArrayInitOne/StructInitOne;
2161 // otherwise we use the full ArrayInit/StructInit.
2162
2163 if (p.eatToken(.r_brace)) |_| {
2164 return p.addNode(.{
2165 .tag = .struct_init_one,
2166 .main_token = lbrace,
2167 .data = .{
2168 .lhs = lhs,
2169 .rhs = 0,
2170 },
2171 });
2172 }
2173 const field_init = try p.parseFieldInit();
2174 if (field_init != 0) {
2175 const comma_one = p.eatToken(.comma);
2176 if (p.eatToken(.r_brace)) |_| {
2177 return p.addNode(.{
2178 .tag = if (comma_one != null) .struct_init_one_comma else .struct_init_one,
2179 .main_token = lbrace,
2180 .data = .{
2181 .lhs = lhs,
2182 .rhs = field_init,
2183 },
2184 });
2185 }
2186
2187 var init_list = std.ArrayList(Node.Index).init(p.gpa);
2188 defer init_list.deinit();
13402189
1341 if (try p.parseFieldInit()) |field_init| {
13422190 try init_list.append(field_init);
1343 while (p.eatToken(.Comma)) |_| {
1344 const next = (try p.parseFieldInit()) orelse break;
1345 try init_list.append(next);
1346 }
1347 const node = try Node.StructInitializer.alloc(&p.arena.allocator, init_list.items.len);
1348 node.* = .{
1349 .lhs = lhs,
1350 .rtoken = try p.expectToken(.RBrace),
1351 .list_len = init_list.items.len,
1352 };
1353 std.mem.copy(*Node, node.list(), init_list.items);
1354 return &node.base;
1355 }
13562191
1357 if (try p.parseExpr()) |expr| {
1358 try init_list.append(expr);
1359 while (p.eatToken(.Comma)) |_| {
1360 const next = (try p.parseExpr()) orelse break;
2192 while (true) {
2193 const next = try p.expectFieldInit();
13612194 try init_list.append(next);
2195
2196 switch (p.token_tags[p.nextToken()]) {
2197 .comma => {
2198 if (p.eatToken(.r_brace)) |_| break;
2199 continue;
2200 },
2201 .r_brace => break,
2202 .colon, .r_paren, .r_bracket => {
2203 p.tok_i -= 1;
2204 return p.failExpected(.r_brace);
2205 },
2206 else => {
2207 // This is likely just a missing comma;
2208 // give an error but continue parsing this list.
2209 p.tok_i -= 1;
2210 try p.warnExpected(.comma);
2211 },
2212 }
13622213 }
1363 const node = try Node.ArrayInitializer.alloc(&p.arena.allocator, init_list.items.len);
1364 node.* = .{
1365 .lhs = lhs,
1366 .rtoken = try p.expectToken(.RBrace),
1367 .list_len = init_list.items.len,
1368 };
1369 std.mem.copy(*Node, node.list(), init_list.items);
1370 return &node.base;
2214 const span = try p.listToSpan(init_list.items);
2215 return p.addNode(.{
2216 .tag = if (p.token_tags[p.tok_i - 2] == .comma) .struct_init_comma else .struct_init,
2217 .main_token = lbrace,
2218 .data = .{
2219 .lhs = lhs,
2220 .rhs = try p.addExtra(Node.SubRange{
2221 .start = span.start,
2222 .end = span.end,
2223 }),
2224 },
2225 });
13712226 }
13722227
1373 const node = try p.arena.allocator.create(Node.StructInitializer);
1374 node.* = .{
1375 .lhs = lhs,
1376 .rtoken = try p.expectToken(.RBrace),
1377 .list_len = 0,
1378 };
1379 return &node.base;
1380 }
2228 const elem_init = try p.expectExpr();
2229 const comma_one = p.eatToken(.comma);
2230 if (p.eatToken(.r_brace)) |_| {
2231 return p.addNode(.{
2232 .tag = if (comma_one != null) .array_init_one_comma else .array_init_one,
2233 .main_token = lbrace,
2234 .data = .{
2235 .lhs = lhs,
2236 .rhs = elem_init,
2237 },
2238 });
2239 }
2240 if (comma_one == null) {
2241 try p.warnExpected(.comma);
2242 }
13812243
1382 /// InitList
1383 /// <- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE
1384 /// / LBRACE Expr (COMMA Expr)* COMMA? RBRACE
1385 /// / LBRACE RBRACE
1386 fn parseAnonInitList(p: *Parser, dot: TokenIndex) !?*Node {
1387 const lbrace = p.eatToken(.LBrace) orelse return null;
1388 var init_list = std.ArrayList(*Node).init(p.gpa);
2244 var init_list = std.ArrayList(Node.Index).init(p.gpa);
13892245 defer init_list.deinit();
13902246
1391 if (try p.parseFieldInit()) |field_init| {
1392 try init_list.append(field_init);
1393 while (p.eatToken(.Comma)) |_| {
1394 const next = (try p.parseFieldInit()) orelse break;
1395 try init_list.append(next);
1396 }
1397 const node = try Node.StructInitializerDot.alloc(&p.arena.allocator, init_list.items.len);
1398 node.* = .{
1399 .dot = dot,
1400 .rtoken = try p.expectToken(.RBrace),
1401 .list_len = init_list.items.len,
1402 };
1403 std.mem.copy(*Node, node.list(), init_list.items);
1404 return &node.base;
1405 }
2247 try init_list.append(elem_init);
14062248
1407 if (try p.parseExpr()) |expr| {
1408 try init_list.append(expr);
1409 while (p.eatToken(.Comma)) |_| {
1410 const next = (try p.parseExpr()) orelse break;
1411 try init_list.append(next);
2249 var trailing_comma = true;
2250 var next = try p.parseExpr();
2251 while (next != 0) : (next = try p.parseExpr()) {
2252 try init_list.append(next);
2253 if (p.eatToken(.comma) == null) {
2254 trailing_comma = false;
2255 break;
14122256 }
1413 const node = try Node.ArrayInitializerDot.alloc(&p.arena.allocator, init_list.items.len);
1414 node.* = .{
1415 .dot = dot,
1416 .rtoken = try p.expectToken(.RBrace),
1417 .list_len = init_list.items.len,
1418 };
1419 std.mem.copy(*Node, node.list(), init_list.items);
1420 return &node.base;
14212257 }
1422
1423 const node = try p.arena.allocator.create(Node.StructInitializerDot);
1424 node.* = .{
1425 .dot = dot,
1426 .rtoken = try p.expectToken(.RBrace),
1427 .list_len = 0,
1428 };
1429 return &node.base;
1430 }
1431
1432 /// TypeExpr <- PrefixTypeOp* ErrorUnionExpr
1433 fn parseTypeExpr(p: *Parser) Error!?*Node {
1434 return p.parsePrefixOpExpr(parsePrefixTypeOp, parseErrorUnionExpr);
2258 _ = try p.expectToken(.r_brace);
2259 const span = try p.listToSpan(init_list.items);
2260 return p.addNode(.{
2261 .tag = if (trailing_comma) .array_init_comma else .array_init,
2262 .main_token = lbrace,
2263 .data = .{
2264 .lhs = lhs,
2265 .rhs = try p.addExtra(Node.SubRange{
2266 .start = span.start,
2267 .end = span.end,
2268 }),
2269 },
2270 });
14352271 }
14362272
14372273 /// ErrorUnionExpr <- SuffixExpr (EXCLAMATIONMARK TypeExpr)?
1438 fn parseErrorUnionExpr(p: *Parser) !?*Node {
1439 const suffix_expr = (try p.parseSuffixExpr()) orelse return null;
1440
1441 if (try SimpleBinOpParseFn(.Bang, .ErrorUnion)(p)) |node| {
1442 const error_union = node.castTag(.ErrorUnion).?;
1443 const type_expr = try p.expectNode(parseTypeExpr, .{
1444 .ExpectedTypeExpr = .{ .token = p.tok_i },
1445 });
1446 error_union.lhs = suffix_expr;
1447 error_union.rhs = type_expr;
1448 return node;
1449 }
1450
1451 return suffix_expr;
2274 fn parseErrorUnionExpr(p: *Parser) !Node.Index {
2275 const suffix_expr = try p.parseSuffixExpr();
2276 if (suffix_expr == 0) return null_node;
2277 const bang = p.eatToken(.bang) orelse return suffix_expr;
2278 return p.addNode(.{
2279 .tag = .error_union,
2280 .main_token = bang,
2281 .data = .{
2282 .lhs = suffix_expr,
2283 .rhs = try p.expectTypeExpr(),
2284 },
2285 });
14522286 }
14532287
14542288 /// SuffixExpr
14552289 /// <- KEYWORD_async PrimaryTypeExpr SuffixOp* FnCallArguments
14562290 /// / PrimaryTypeExpr (SuffixOp / FnCallArguments)*
1457 fn parseSuffixExpr(p: *Parser) !?*Node {
1458 const maybe_async = p.eatToken(.Keyword_async);
1459 if (maybe_async) |async_token| {
1460 const token_fn = p.eatToken(.Keyword_fn);
1461 if (token_fn != null) {
1462 // TODO: remove this hack when async fn rewriting is
1463 // HACK: If we see the keyword `fn`, then we assume that
1464 // we are parsing an async fn proto, and not a call.
1465 // We therefore put back all tokens consumed by the async
1466 // prefix...
1467 p.putBackToken(token_fn.?);
1468 p.putBackToken(async_token);
1469 return p.parsePrimaryTypeExpr();
1470 }
1471 var res = try p.expectNode(parsePrimaryTypeExpr, .{
1472 .ExpectedPrimaryTypeExpr = .{ .token = p.tok_i },
1473 });
2291 /// FnCallArguments <- LPAREN ExprList RPAREN
2292 /// ExprList <- (Expr COMMA)* Expr?
2293 fn parseSuffixExpr(p: *Parser) !Node.Index {
2294 if (p.eatToken(.keyword_async)) |async_token| {
2295 var res = try p.expectPrimaryTypeExpr();
14742296
1475 while (try p.parseSuffixOp(res)) |node| {
2297 while (true) {
2298 const node = try p.parseSuffixOp(res);
2299 if (node == 0) break;
14762300 res = node;
14772301 }
1478
1479 const params = (try p.parseFnCallArguments()) orelse {
1480 try p.errors.append(p.gpa, .{
1481 .ExpectedParamList = .{ .token = p.tok_i },
1482 });
1483 // ignore this, continue parsing
2302 const lparen = p.nextToken();
2303 if (p.token_tags[lparen] != .l_paren) {
2304 p.tok_i -= 1;
2305 try p.warn(.expected_param_list);
14842306 return res;
1485 };
1486 defer p.gpa.free(params.list);
1487 const node = try Node.Call.alloc(&p.arena.allocator, params.list.len);
1488 node.* = .{
1489 .lhs = res,
1490 .params_len = params.list.len,
1491 .async_token = async_token,
1492 .rtoken = params.rparen,
1493 };
1494 std.mem.copy(*Node, node.params(), params.list);
1495 return &node.base;
1496 }
1497 if (try p.parsePrimaryTypeExpr()) |expr| {
1498 var res = expr;
2307 }
2308 if (p.eatToken(.r_paren)) |_| {
2309 return p.addNode(.{
2310 .tag = .async_call_one,
2311 .main_token = lparen,
2312 .data = .{
2313 .lhs = res,
2314 .rhs = 0,
2315 },
2316 });
2317 }
2318 const param_one = try p.expectExpr();
2319 const comma_one = p.eatToken(.comma);
2320 if (p.eatToken(.r_paren)) |_| {
2321 return p.addNode(.{
2322 .tag = if (comma_one == null) .async_call_one else .async_call_one_comma,
2323 .main_token = lparen,
2324 .data = .{
2325 .lhs = res,
2326 .rhs = param_one,
2327 },
2328 });
2329 }
2330 if (comma_one == null) {
2331 try p.warnExpected(.comma);
2332 }
2333
2334 var param_list = std.ArrayList(Node.Index).init(p.gpa);
2335 defer param_list.deinit();
2336
2337 try param_list.append(param_one);
14992338
15002339 while (true) {
1501 if (try p.parseSuffixOp(res)) |node| {
1502 res = node;
1503 continue;
1504 }
1505 if (try p.parseFnCallArguments()) |params| {
1506 defer p.gpa.free(params.list);
1507 const call = try Node.Call.alloc(&p.arena.allocator, params.list.len);
1508 call.* = .{
1509 .lhs = res,
1510 .params_len = params.list.len,
1511 .async_token = null,
1512 .rtoken = params.rparen,
1513 };
1514 std.mem.copy(*Node, call.params(), params.list);
1515 res = &call.base;
1516 continue;
2340 const next = try p.expectExpr();
2341 try param_list.append(next);
2342 switch (p.token_tags[p.nextToken()]) {
2343 .comma => {
2344 if (p.eatToken(.r_paren)) |_| {
2345 const span = try p.listToSpan(param_list.items);
2346 return p.addNode(.{
2347 .tag = .async_call_comma,
2348 .main_token = lparen,
2349 .data = .{
2350 .lhs = res,
2351 .rhs = try p.addExtra(Node.SubRange{
2352 .start = span.start,
2353 .end = span.end,
2354 }),
2355 },
2356 });
2357 } else {
2358 continue;
2359 }
2360 },
2361 .r_paren => {
2362 const span = try p.listToSpan(param_list.items);
2363 return p.addNode(.{
2364 .tag = .async_call,
2365 .main_token = lparen,
2366 .data = .{
2367 .lhs = res,
2368 .rhs = try p.addExtra(Node.SubRange{
2369 .start = span.start,
2370 .end = span.end,
2371 }),
2372 },
2373 });
2374 },
2375 .colon, .r_brace, .r_bracket => {
2376 p.tok_i -= 1;
2377 return p.failExpected(.r_paren);
2378 },
2379 else => {
2380 p.tok_i -= 1;
2381 try p.warnExpected(.comma);
2382 },
15172383 }
1518 break;
15192384 }
1520 return res;
15212385 }
2386 var res = try p.parsePrimaryTypeExpr();
2387 if (res == 0) return res;
15222388
1523 return null;
2389 while (true) {
2390 const suffix_op = try p.parseSuffixOp(res);
2391 if (suffix_op != 0) {
2392 res = suffix_op;
2393 continue;
2394 }
2395 res = res: {
2396 const lparen = p.eatToken(.l_paren) orelse return res;
2397 if (p.eatToken(.r_paren)) |_| {
2398 break :res try p.addNode(.{
2399 .tag = .call_one,
2400 .main_token = lparen,
2401 .data = .{
2402 .lhs = res,
2403 .rhs = 0,
2404 },
2405 });
2406 }
2407 const param_one = try p.expectExpr();
2408 const comma_one = p.eatToken(.comma);
2409 if (p.eatToken(.r_paren)) |_| {
2410 break :res try p.addNode(.{
2411 .tag = if (comma_one == null) .call_one else .call_one_comma,
2412 .main_token = lparen,
2413 .data = .{
2414 .lhs = res,
2415 .rhs = param_one,
2416 },
2417 });
2418 }
2419 if (comma_one == null) {
2420 try p.warnExpected(.comma);
2421 }
2422
2423 var param_list = std.ArrayList(Node.Index).init(p.gpa);
2424 defer param_list.deinit();
2425
2426 try param_list.append(param_one);
2427
2428 while (true) {
2429 const next = try p.expectExpr();
2430 try param_list.append(next);
2431 switch (p.token_tags[p.nextToken()]) {
2432 .comma => {
2433 if (p.eatToken(.r_paren)) |_| {
2434 const span = try p.listToSpan(param_list.items);
2435 break :res try p.addNode(.{
2436 .tag = .call_comma,
2437 .main_token = lparen,
2438 .data = .{
2439 .lhs = res,
2440 .rhs = try p.addExtra(Node.SubRange{
2441 .start = span.start,
2442 .end = span.end,
2443 }),
2444 },
2445 });
2446 } else {
2447 continue;
2448 }
2449 },
2450 .r_paren => {
2451 const span = try p.listToSpan(param_list.items);
2452 break :res try p.addNode(.{
2453 .tag = .call,
2454 .main_token = lparen,
2455 .data = .{
2456 .lhs = res,
2457 .rhs = try p.addExtra(Node.SubRange{
2458 .start = span.start,
2459 .end = span.end,
2460 }),
2461 },
2462 });
2463 },
2464 .colon, .r_brace, .r_bracket => {
2465 p.tok_i -= 1;
2466 return p.failExpected(.r_paren);
2467 },
2468 else => {
2469 p.tok_i -= 1;
2470 try p.warnExpected(.comma);
2471 },
2472 }
2473 }
2474 };
2475 }
15242476 }
15252477
15262478 /// PrimaryTypeExpr
......@@ -1528,6 +2480,7 @@ const Parser = struct {
15282480 /// / CHAR_LITERAL
15292481 /// / ContainerDecl
15302482 /// / DOT IDENTIFIER
2483 /// / DOT InitList
15312484 /// / ErrorSetDecl
15322485 /// / FLOAT
15332486 /// / FnProto
......@@ -1546,260 +2499,546 @@ const Parser = struct {
15462499 /// / KEYWORD_unreachable
15472500 /// / STRINGLITERAL
15482501 /// / SwitchExpr
1549 fn parsePrimaryTypeExpr(p: *Parser) !?*Node {
1550 if (try p.parseBuiltinCall()) |node| return node;
1551 if (p.eatToken(.CharLiteral)) |token| {
1552 const node = try p.arena.allocator.create(Node.OneToken);
1553 node.* = .{
1554 .base = .{ .tag = .CharLiteral },
1555 .token = token,
1556 };
1557 return &node.base;
1558 }
1559 if (try p.parseContainerDecl()) |node| return node;
1560 if (try p.parseAnonLiteral()) |node| return node;
1561 if (try p.parseErrorSetDecl()) |node| return node;
1562 if (try p.parseFloatLiteral()) |node| return node;
1563 if (try p.parseFnProto(.as_type, .{})) |node| return node;
1564 if (try p.parseGroupedExpr()) |node| return node;
1565 if (try p.parseLabeledTypeExpr()) |node| return node;
1566 if (try p.parseIdentifier()) |node| return node;
1567 if (try p.parseIfTypeExpr()) |node| return node;
1568 if (try p.parseIntegerLiteral()) |node| return node;
1569 if (p.eatToken(.Keyword_comptime)) |token| {
1570 const expr = (try p.parseTypeExpr()) orelse return null;
1571 const node = try p.arena.allocator.create(Node.Comptime);
1572 node.* = .{
1573 .doc_comments = null,
1574 .comptime_token = token,
1575 .expr = expr,
1576 };
1577 return &node.base;
1578 }
1579 if (p.eatToken(.Keyword_error)) |token| {
1580 const period = try p.expectTokenRecoverable(.Period);
1581 const identifier = try p.expectNodeRecoverable(parseIdentifier, .{
1582 .ExpectedIdentifier = .{ .token = p.tok_i },
1583 });
1584 const global_error_set = try p.createLiteral(.ErrorType, token);
1585 if (period == null or identifier == null) return global_error_set;
1586
1587 const node = try p.arena.allocator.create(Node.SimpleInfixOp);
1588 node.* = .{
1589 .base = Node{ .tag = .Period },
1590 .op_token = period.?,
1591 .lhs = global_error_set,
1592 .rhs = identifier.?,
1593 };
1594 return &node.base;
1595 }
1596 if (p.eatToken(.Keyword_false)) |token| return p.createLiteral(.BoolLiteral, token);
1597 if (p.eatToken(.Keyword_null)) |token| return p.createLiteral(.NullLiteral, token);
1598 if (p.eatToken(.Keyword_anyframe)) |token| {
1599 const node = try p.arena.allocator.create(Node.AnyFrameType);
1600 node.* = .{
1601 .anyframe_token = token,
1602 .result = null,
1603 };
1604 return &node.base;
1605 }
1606 if (p.eatToken(.Keyword_true)) |token| return p.createLiteral(.BoolLiteral, token);
1607 if (p.eatToken(.Keyword_undefined)) |token| return p.createLiteral(.UndefinedLiteral, token);
1608 if (p.eatToken(.Keyword_unreachable)) |token| return p.createLiteral(.Unreachable, token);
1609 if (try p.parseStringLiteral()) |node| return node;
1610 if (try p.parseSwitchExpr()) |node| return node;
1611
1612 return null;
1613 }
1614
16152502 /// ContainerDecl <- (KEYWORD_extern / KEYWORD_packed)? ContainerDeclAuto
1616 fn parseContainerDecl(p: *Parser) !?*Node {
1617 const layout_token = p.eatToken(.Keyword_extern) orelse
1618 p.eatToken(.Keyword_packed);
1619
1620 const node = (try p.parseContainerDeclAuto()) orelse {
1621 if (layout_token) |token|
1622 p.putBackToken(token);
1623 return null;
1624 };
1625 node.cast(Node.ContainerDecl).?.*.layout_token = layout_token;
1626 return node;
1627 }
1628
2503 /// ContainerDeclAuto <- ContainerDeclType LBRACE ContainerMembers RBRACE
2504 /// InitList
2505 /// <- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE
2506 /// / LBRACE Expr (COMMA Expr)* COMMA? RBRACE
2507 /// / LBRACE RBRACE
16292508 /// ErrorSetDecl <- KEYWORD_error LBRACE IdentifierList RBRACE
1630 fn parseErrorSetDecl(p: *Parser) !?*Node {
1631 const error_token = p.eatToken(.Keyword_error) orelse return null;
1632 if (p.eatToken(.LBrace) == null) {
1633 // Might parse as `KEYWORD_error DOT IDENTIFIER` later in PrimaryTypeExpr, so don't error
1634 p.putBackToken(error_token);
1635 return null;
1636 }
1637 const decls = try p.parseErrorTagList();
1638 defer p.gpa.free(decls);
1639 const rbrace = try p.expectToken(.RBrace);
1640
1641 const node = try Node.ErrorSetDecl.alloc(&p.arena.allocator, decls.len);
1642 node.* = .{
1643 .error_token = error_token,
1644 .decls_len = decls.len,
1645 .rbrace_token = rbrace,
1646 };
1647 std.mem.copy(*Node, node.decls(), decls);
1648 return &node.base;
1649 }
1650
16512509 /// GroupedExpr <- LPAREN Expr RPAREN
1652 fn parseGroupedExpr(p: *Parser) !?*Node {
1653 const lparen = p.eatToken(.LParen) orelse return null;
1654 const expr = try p.expectNode(parseExpr, .{
1655 .ExpectedExpr = .{ .token = p.tok_i },
1656 });
1657 const rparen = try p.expectToken(.RParen);
1658
1659 const node = try p.arena.allocator.create(Node.GroupedExpression);
1660 node.* = .{
1661 .lparen = lparen,
1662 .expr = expr,
1663 .rparen = rparen,
1664 };
1665 return &node.base;
1666 }
1667
16682510 /// IfTypeExpr <- IfPrefix TypeExpr (KEYWORD_else Payload? TypeExpr)?
1669 fn parseIfTypeExpr(p: *Parser) !?*Node {
1670 return p.parseIf(parseTypeExpr);
1671 }
1672
16732511 /// LabeledTypeExpr
16742512 /// <- BlockLabel Block
16752513 /// / BlockLabel? LoopTypeExpr
1676 fn parseLabeledTypeExpr(p: *Parser) !?*Node {
1677 var colon: TokenIndex = undefined;
1678 const label = p.parseBlockLabel(&colon);
2514 /// LoopTypeExpr <- KEYWORD_inline? (ForTypeExpr / WhileTypeExpr)
2515 fn parsePrimaryTypeExpr(p: *Parser) !Node.Index {
2516 switch (p.token_tags[p.tok_i]) {
2517 .char_literal => return p.addNode(.{
2518 .tag = .char_literal,
2519 .main_token = p.nextToken(),
2520 .data = .{
2521 .lhs = undefined,
2522 .rhs = undefined,
2523 },
2524 }),
2525 .integer_literal => return p.addNode(.{
2526 .tag = .integer_literal,
2527 .main_token = p.nextToken(),
2528 .data = .{
2529 .lhs = undefined,
2530 .rhs = undefined,
2531 },
2532 }),
2533 .float_literal => return p.addNode(.{
2534 .tag = .float_literal,
2535 .main_token = p.nextToken(),
2536 .data = .{
2537 .lhs = undefined,
2538 .rhs = undefined,
2539 },
2540 }),
2541 .keyword_false => return p.addNode(.{
2542 .tag = .false_literal,
2543 .main_token = p.nextToken(),
2544 .data = .{
2545 .lhs = undefined,
2546 .rhs = undefined,
2547 },
2548 }),
2549 .keyword_true => return p.addNode(.{
2550 .tag = .true_literal,
2551 .main_token = p.nextToken(),
2552 .data = .{
2553 .lhs = undefined,
2554 .rhs = undefined,
2555 },
2556 }),
2557 .keyword_null => return p.addNode(.{
2558 .tag = .null_literal,
2559 .main_token = p.nextToken(),
2560 .data = .{
2561 .lhs = undefined,
2562 .rhs = undefined,
2563 },
2564 }),
2565 .keyword_undefined => return p.addNode(.{
2566 .tag = .undefined_literal,
2567 .main_token = p.nextToken(),
2568 .data = .{
2569 .lhs = undefined,
2570 .rhs = undefined,
2571 },
2572 }),
2573 .keyword_unreachable => return p.addNode(.{
2574 .tag = .unreachable_literal,
2575 .main_token = p.nextToken(),
2576 .data = .{
2577 .lhs = undefined,
2578 .rhs = undefined,
2579 },
2580 }),
2581 .keyword_anyframe => return p.addNode(.{
2582 .tag = .anyframe_literal,
2583 .main_token = p.nextToken(),
2584 .data = .{
2585 .lhs = undefined,
2586 .rhs = undefined,
2587 },
2588 }),
2589 .string_literal => {
2590 const main_token = p.nextToken();
2591 return p.addNode(.{
2592 .tag = .string_literal,
2593 .main_token = main_token,
2594 .data = .{
2595 .lhs = undefined,
2596 .rhs = undefined,
2597 },
2598 });
2599 },
16792600
1680 if (label) |label_token| {
1681 if (try p.parseBlock(label_token)) |node| return node;
1682 }
2601 .builtin => return p.parseBuiltinCall(),
2602 .keyword_fn => return p.parseFnProto(),
2603 .keyword_if => return p.parseIf(parseTypeExpr),
2604 .keyword_switch => return p.expectSwitchExpr(),
16832605
1684 if (try p.parseLoopTypeExpr()) |node| {
1685 switch (node.tag) {
1686 .For => node.cast(Node.For).?.label = label,
1687 .While => node.cast(Node.While).?.label = label,
1688 else => unreachable,
1689 }
1690 return node;
1691 }
2606 .keyword_extern,
2607 .keyword_packed,
2608 => {
2609 p.tok_i += 1;
2610 return p.parseContainerDeclAuto();
2611 },
16922612
1693 if (label) |token| {
1694 p.putBackToken(colon);
1695 p.putBackToken(token);
1696 }
1697 return null;
1698 }
2613 .keyword_struct,
2614 .keyword_opaque,
2615 .keyword_enum,
2616 .keyword_union,
2617 => return p.parseContainerDeclAuto(),
2618
2619 .keyword_comptime => return p.addNode(.{
2620 .tag = .@"comptime",
2621 .main_token = p.nextToken(),
2622 .data = .{
2623 .lhs = try p.expectTypeExpr(),
2624 .rhs = undefined,
2625 },
2626 }),
2627 .multiline_string_literal_line => {
2628 const first_line = p.nextToken();
2629 while (p.token_tags[p.tok_i] == .multiline_string_literal_line) {
2630 p.tok_i += 1;
2631 }
2632 return p.addNode(.{
2633 .tag = .multiline_string_literal,
2634 .main_token = first_line,
2635 .data = .{
2636 .lhs = first_line,
2637 .rhs = p.tok_i - 1,
2638 },
2639 });
2640 },
2641 .identifier => switch (p.token_tags[p.tok_i + 1]) {
2642 .colon => switch (p.token_tags[p.tok_i + 2]) {
2643 .keyword_inline => {
2644 p.tok_i += 3;
2645 switch (p.token_tags[p.tok_i]) {
2646 .keyword_for => return p.parseForTypeExpr(),
2647 .keyword_while => return p.parseWhileTypeExpr(),
2648 else => return p.fail(.expected_inlinable),
2649 }
2650 },
2651 .keyword_for => {
2652 p.tok_i += 2;
2653 return p.parseForTypeExpr();
2654 },
2655 .keyword_while => {
2656 p.tok_i += 2;
2657 return p.parseWhileTypeExpr();
2658 },
2659 .l_brace => {
2660 p.tok_i += 2;
2661 return p.parseBlock();
2662 },
2663 else => return p.addNode(.{
2664 .tag = .identifier,
2665 .main_token = p.nextToken(),
2666 .data = .{
2667 .lhs = undefined,
2668 .rhs = undefined,
2669 },
2670 }),
2671 },
2672 else => return p.addNode(.{
2673 .tag = .identifier,
2674 .main_token = p.nextToken(),
2675 .data = .{
2676 .lhs = undefined,
2677 .rhs = undefined,
2678 },
2679 }),
2680 },
2681 .keyword_inline => {
2682 p.tok_i += 1;
2683 switch (p.token_tags[p.tok_i]) {
2684 .keyword_for => return p.parseForTypeExpr(),
2685 .keyword_while => return p.parseWhileTypeExpr(),
2686 else => return p.fail(.expected_inlinable),
2687 }
2688 },
2689 .keyword_for => return p.parseForTypeExpr(),
2690 .keyword_while => return p.parseWhileTypeExpr(),
2691 .period => switch (p.token_tags[p.tok_i + 1]) {
2692 .identifier => return p.addNode(.{
2693 .tag = .enum_literal,
2694 .data = .{
2695 .lhs = p.nextToken(), // dot
2696 .rhs = undefined,
2697 },
2698 .main_token = p.nextToken(), // identifier
2699 }),
2700 .l_brace => {
2701 const lbrace = p.tok_i + 1;
2702 p.tok_i = lbrace + 1;
2703
2704 // If there are 0, 1, or 2 items, we can use ArrayInitDotTwo/StructInitDotTwo;
2705 // otherwise we use the full ArrayInitDot/StructInitDot.
2706
2707 if (p.eatToken(.r_brace)) |_| {
2708 return p.addNode(.{
2709 .tag = .struct_init_dot_two,
2710 .main_token = lbrace,
2711 .data = .{
2712 .lhs = 0,
2713 .rhs = 0,
2714 },
2715 });
2716 }
2717 const field_init_one = try p.parseFieldInit();
2718 if (field_init_one != 0) {
2719 const comma_one = p.eatToken(.comma);
2720 if (p.eatToken(.r_brace)) |_| {
2721 return p.addNode(.{
2722 .tag = if (comma_one != null) .struct_init_dot_two_comma else .struct_init_dot_two,
2723 .main_token = lbrace,
2724 .data = .{
2725 .lhs = field_init_one,
2726 .rhs = 0,
2727 },
2728 });
2729 }
2730 if (comma_one == null) {
2731 try p.warnExpected(.comma);
2732 }
2733 const field_init_two = try p.expectFieldInit();
2734 const comma_two = p.eatToken(.comma);
2735 if (p.eatToken(.r_brace)) |_| {
2736 return p.addNode(.{
2737 .tag = if (comma_two != null) .struct_init_dot_two_comma else .struct_init_dot_two,
2738 .main_token = lbrace,
2739 .data = .{
2740 .lhs = field_init_one,
2741 .rhs = field_init_two,
2742 },
2743 });
2744 }
2745 if (comma_two == null) {
2746 try p.warnExpected(.comma);
2747 }
2748 var init_list = std.ArrayList(Node.Index).init(p.gpa);
2749 defer init_list.deinit();
2750
2751 try init_list.appendSlice(&.{ field_init_one, field_init_two });
2752
2753 while (true) {
2754 const next = try p.expectFieldInit();
2755 assert(next != 0);
2756 try init_list.append(next);
2757 switch (p.token_tags[p.nextToken()]) {
2758 .comma => {
2759 if (p.eatToken(.r_brace)) |_| break;
2760 continue;
2761 },
2762 .r_brace => break,
2763 .colon, .r_paren, .r_bracket => {
2764 p.tok_i -= 1;
2765 return p.failExpected(.r_brace);
2766 },
2767 else => {
2768 p.tok_i -= 1;
2769 try p.warnExpected(.comma);
2770 },
2771 }
2772 }
2773 const span = try p.listToSpan(init_list.items);
2774 const trailing_comma = p.token_tags[p.tok_i - 2] == .comma;
2775 return p.addNode(.{
2776 .tag = if (trailing_comma) .struct_init_dot_comma else .struct_init_dot,
2777 .main_token = lbrace,
2778 .data = .{
2779 .lhs = span.start,
2780 .rhs = span.end,
2781 },
2782 });
2783 }
16992784
1700 /// LoopTypeExpr <- KEYWORD_inline? (ForTypeExpr / WhileTypeExpr)
1701 fn parseLoopTypeExpr(p: *Parser) !?*Node {
1702 const inline_token = p.eatToken(.Keyword_inline);
2785 const elem_init_one = try p.expectExpr();
2786 const comma_one = p.eatToken(.comma);
2787 if (p.eatToken(.r_brace)) |_| {
2788 return p.addNode(.{
2789 .tag = if (comma_one != null) .array_init_dot_two_comma else .array_init_dot_two,
2790 .main_token = lbrace,
2791 .data = .{
2792 .lhs = elem_init_one,
2793 .rhs = 0,
2794 },
2795 });
2796 }
2797 if (comma_one == null) {
2798 try p.warnExpected(.comma);
2799 }
2800 const elem_init_two = try p.expectExpr();
2801 const comma_two = p.eatToken(.comma);
2802 if (p.eatToken(.r_brace)) |_| {
2803 return p.addNode(.{
2804 .tag = if (comma_two != null) .array_init_dot_two_comma else .array_init_dot_two,
2805 .main_token = lbrace,
2806 .data = .{
2807 .lhs = elem_init_one,
2808 .rhs = elem_init_two,
2809 },
2810 });
2811 }
2812 if (comma_two == null) {
2813 try p.warnExpected(.comma);
2814 }
2815 var init_list = std.ArrayList(Node.Index).init(p.gpa);
2816 defer init_list.deinit();
2817
2818 try init_list.appendSlice(&.{ elem_init_one, elem_init_two });
2819
2820 while (true) {
2821 const next = try p.expectExpr();
2822 if (next == 0) break;
2823 try init_list.append(next);
2824 switch (p.token_tags[p.nextToken()]) {
2825 .comma => {
2826 if (p.eatToken(.r_brace)) |_| break;
2827 continue;
2828 },
2829 .r_brace => break,
2830 .colon, .r_paren, .r_bracket => {
2831 p.tok_i -= 1;
2832 return p.failExpected(.r_brace);
2833 },
2834 else => {
2835 p.tok_i -= 1;
2836 try p.warnExpected(.comma);
2837 },
2838 }
2839 }
2840 const span = try p.listToSpan(init_list.items);
2841 return p.addNode(.{
2842 .tag = if (p.token_tags[p.tok_i - 2] == .comma) .array_init_dot_comma else .array_init_dot,
2843 .main_token = lbrace,
2844 .data = .{
2845 .lhs = span.start,
2846 .rhs = span.end,
2847 },
2848 });
2849 },
2850 else => return null_node,
2851 },
2852 .keyword_error => switch (p.token_tags[p.tok_i + 1]) {
2853 .l_brace => {
2854 const error_token = p.tok_i;
2855 p.tok_i += 2;
2856
2857 if (p.eatToken(.r_brace)) |rbrace| {
2858 return p.addNode(.{
2859 .tag = .error_set_decl,
2860 .main_token = error_token,
2861 .data = .{
2862 .lhs = undefined,
2863 .rhs = rbrace,
2864 },
2865 });
2866 }
17032867
1704 if (try p.parseForTypeExpr()) |node| {
1705 node.cast(Node.For).?.inline_token = inline_token;
1706 return node;
2868 while (true) {
2869 const doc_comment = try p.eatDocComments();
2870 const identifier = try p.expectToken(.identifier);
2871 switch (p.token_tags[p.nextToken()]) {
2872 .comma => {
2873 if (p.eatToken(.r_brace)) |_| break;
2874 continue;
2875 },
2876 .r_brace => break,
2877 .colon, .r_paren, .r_bracket => {
2878 p.tok_i -= 1;
2879 return p.failExpected(.r_brace);
2880 },
2881 else => {
2882 // This is likely just a missing comma;
2883 // give an error but continue parsing this list.
2884 p.tok_i -= 1;
2885 try p.warnExpected(.comma);
2886 },
2887 }
2888 }
2889 return p.addNode(.{
2890 .tag = .error_set_decl,
2891 .main_token = error_token,
2892 .data = .{
2893 .lhs = undefined,
2894 .rhs = p.tok_i - 1, // rbrace
2895 },
2896 });
2897 },
2898 else => {
2899 const main_token = p.nextToken();
2900 const period = p.eatToken(.period);
2901 if (period == null) try p.warnExpected(.period);
2902 const identifier = p.eatToken(.identifier);
2903 if (identifier == null) try p.warnExpected(.identifier);
2904 return p.addNode(.{
2905 .tag = .error_value,
2906 .main_token = main_token,
2907 .data = .{
2908 .lhs = period orelse 0,
2909 .rhs = identifier orelse 0,
2910 },
2911 });
2912 },
2913 },
2914 .l_paren => return p.addNode(.{
2915 .tag = .grouped_expression,
2916 .main_token = p.nextToken(),
2917 .data = .{
2918 .lhs = try p.expectExpr(),
2919 .rhs = try p.expectToken(.r_paren),
2920 },
2921 }),
2922 else => return null_node,
17072923 }
2924 }
17082925
1709 if (try p.parseWhileTypeExpr()) |node| {
1710 node.cast(Node.While).?.inline_token = inline_token;
1711 return node;
2926 fn expectPrimaryTypeExpr(p: *Parser) !Node.Index {
2927 const node = try p.parsePrimaryTypeExpr();
2928 if (node == 0) {
2929 return p.fail(.expected_primary_type_expr);
17122930 }
1713
1714 if (inline_token == null) return null;
1715
1716 // If we've seen "inline", there should have been a "for" or "while"
1717 try p.errors.append(p.gpa, .{
1718 .ExpectedInlinable = .{ .token = p.tok_i },
1719 });
1720 return error.ParseError;
2931 return node;
17212932 }
17222933
2934 /// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload
17232935 /// ForTypeExpr <- ForPrefix TypeExpr (KEYWORD_else TypeExpr)?
1724 fn parseForTypeExpr(p: *Parser) !?*Node {
1725 const node = (try p.parseForPrefix()) orelse return null;
1726 const for_prefix = node.cast(Node.For).?;
1727
1728 const type_expr = try p.expectNode(parseTypeExpr, .{
1729 .ExpectedTypeExpr = .{ .token = p.tok_i },
1730 });
1731 for_prefix.body = type_expr;
1732
1733 if (p.eatToken(.Keyword_else)) |else_token| {
1734 const else_expr = try p.expectNode(parseTypeExpr, .{
1735 .ExpectedTypeExpr = .{ .token = p.tok_i },
2936 fn parseForTypeExpr(p: *Parser) !Node.Index {
2937 const for_token = p.eatToken(.keyword_for) orelse return null_node;
2938 _ = try p.expectToken(.l_paren);
2939 const array_expr = try p.expectExpr();
2940 _ = try p.expectToken(.r_paren);
2941 const found_payload = try p.parsePtrIndexPayload();
2942 if (found_payload == 0) try p.warn(.expected_loop_payload);
2943
2944 const then_expr = try p.expectExpr();
2945 const else_token = p.eatToken(.keyword_else) orelse {
2946 return p.addNode(.{
2947 .tag = .for_simple,
2948 .main_token = for_token,
2949 .data = .{
2950 .lhs = array_expr,
2951 .rhs = then_expr,
2952 },
17362953 });
1737
1738 const else_node = try p.arena.allocator.create(Node.Else);
1739 else_node.* = .{
1740 .else_token = else_token,
1741 .payload = null,
1742 .body = else_expr,
1743 };
1744
1745 for_prefix.@"else" = else_node;
1746 }
1747
1748 return node;
2954 };
2955 const else_expr = try p.expectTypeExpr();
2956 return p.addNode(.{
2957 .tag = .@"for",
2958 .main_token = for_token,
2959 .data = .{
2960 .lhs = array_expr,
2961 .rhs = try p.addExtra(Node.If{
2962 .then_expr = then_expr,
2963 .else_expr = else_expr,
2964 }),
2965 },
2966 });
17492967 }
17502968
2969 /// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr?
17512970 /// WhileTypeExpr <- WhilePrefix TypeExpr (KEYWORD_else Payload? TypeExpr)?
1752 fn parseWhileTypeExpr(p: *Parser) !?*Node {
1753 const node = (try p.parseWhilePrefix()) orelse return null;
1754 const while_prefix = node.cast(Node.While).?;
1755
1756 const type_expr = try p.expectNode(parseTypeExpr, .{
1757 .ExpectedTypeExpr = .{ .token = p.tok_i },
2971 fn parseWhileTypeExpr(p: *Parser) !Node.Index {
2972 const while_token = p.eatToken(.keyword_while) orelse return null_node;
2973 _ = try p.expectToken(.l_paren);
2974 const condition = try p.expectExpr();
2975 _ = try p.expectToken(.r_paren);
2976 const then_payload = try p.parsePtrPayload();
2977 const cont_expr = try p.parseWhileContinueExpr();
2978
2979 const then_expr = try p.expectTypeExpr();
2980 const else_token = p.eatToken(.keyword_else) orelse {
2981 if (cont_expr == 0) {
2982 return p.addNode(.{
2983 .tag = .while_simple,
2984 .main_token = while_token,
2985 .data = .{
2986 .lhs = condition,
2987 .rhs = then_expr,
2988 },
2989 });
2990 } else {
2991 return p.addNode(.{
2992 .tag = .while_cont,
2993 .main_token = while_token,
2994 .data = .{
2995 .lhs = condition,
2996 .rhs = try p.addExtra(Node.WhileCont{
2997 .cont_expr = cont_expr,
2998 .then_expr = then_expr,
2999 }),
3000 },
3001 });
3002 }
3003 };
3004 const else_payload = try p.parsePayload();
3005 const else_expr = try p.expectTypeExpr();
3006 return p.addNode(.{
3007 .tag = .@"while",
3008 .main_token = while_token,
3009 .data = .{
3010 .lhs = condition,
3011 .rhs = try p.addExtra(Node.While{
3012 .cont_expr = cont_expr,
3013 .then_expr = then_expr,
3014 .else_expr = else_expr,
3015 }),
3016 },
17583017 });
1759 while_prefix.body = type_expr;
1760
1761 if (p.eatToken(.Keyword_else)) |else_token| {
1762 const payload = try p.parsePayload();
1763
1764 const else_expr = try p.expectNode(parseTypeExpr, .{
1765 .ExpectedTypeExpr = .{ .token = p.tok_i },
1766 });
1767
1768 const else_node = try p.arena.allocator.create(Node.Else);
1769 else_node.* = .{
1770 .else_token = else_token,
1771 .payload = null,
1772 .body = else_expr,
1773 };
1774
1775 while_prefix.@"else" = else_node;
1776 }
1777
1778 return node;
17793018 }
17803019
17813020 /// SwitchExpr <- KEYWORD_switch LPAREN Expr RPAREN LBRACE SwitchProngList RBRACE
1782 fn parseSwitchExpr(p: *Parser) !?*Node {
1783 const switch_token = p.eatToken(.Keyword_switch) orelse return null;
1784 _ = try p.expectToken(.LParen);
1785 const expr_node = try p.expectNode(parseExpr, .{
1786 .ExpectedExpr = .{ .token = p.tok_i },
1787 });
1788 _ = try p.expectToken(.RParen);
1789 _ = try p.expectToken(.LBrace);
3021 fn expectSwitchExpr(p: *Parser) !Node.Index {
3022 const switch_token = p.assertToken(.keyword_switch);
3023 _ = try p.expectToken(.l_paren);
3024 const expr_node = try p.expectExpr();
3025 _ = try p.expectToken(.r_paren);
3026 _ = try p.expectToken(.l_brace);
17903027 const cases = try p.parseSwitchProngList();
1791 defer p.gpa.free(cases);
1792 const rbrace = try p.expectToken(.RBrace);
1793
1794 const node = try Node.Switch.alloc(&p.arena.allocator, cases.len);
1795 node.* = .{
1796 .switch_token = switch_token,
1797 .expr = expr_node,
1798 .cases_len = cases.len,
1799 .rbrace = rbrace,
1800 };
1801 std.mem.copy(*Node, node.cases(), cases);
1802 return &node.base;
3028 const trailing_comma = p.token_tags[p.tok_i - 1] == .comma;
3029 _ = try p.expectToken(.r_brace);
3030
3031 return p.addNode(.{
3032 .tag = if (trailing_comma) .switch_comma else .@"switch",
3033 .main_token = switch_token,
3034 .data = .{
3035 .lhs = expr_node,
3036 .rhs = try p.addExtra(Node.SubRange{
3037 .start = cases.start,
3038 .end = cases.end,
3039 }),
3040 },
3041 });
18033042 }
18043043
18053044 /// AsmExpr <- KEYWORD_asm KEYWORD_volatile? LPAREN Expr AsmOutput? RPAREN
......@@ -1807,1696 +3046,1050 @@ const Parser = struct {
18073046 /// AsmInput <- COLON AsmInputList AsmClobbers?
18083047 /// AsmClobbers <- COLON StringList
18093048 /// StringList <- (STRINGLITERAL COMMA)* STRINGLITERAL?
1810 fn parseAsmExpr(p: *Parser) !?*Node {
1811 const asm_token = p.eatToken(.Keyword_asm) orelse return null;
1812 const volatile_token = p.eatToken(.Keyword_volatile);
1813 _ = try p.expectToken(.LParen);
1814 const template = try p.expectNode(parseExpr, .{
1815 .ExpectedExpr = .{ .token = p.tok_i },
1816 });
1817
1818 var arena_outputs: []Node.Asm.Output = &[0]Node.Asm.Output{};
1819 var arena_inputs: []Node.Asm.Input = &[0]Node.Asm.Input{};
1820 var arena_clobbers: []*Node = &[0]*Node{};
3049 /// AsmOutputList <- (AsmOutputItem COMMA)* AsmOutputItem?
3050 /// AsmInputList <- (AsmInputItem COMMA)* AsmInputItem?
3051 fn expectAsmExpr(p: *Parser) !Node.Index {
3052 const asm_token = p.assertToken(.keyword_asm);
3053 _ = p.eatToken(.keyword_volatile);
3054 _ = try p.expectToken(.l_paren);
3055 const template = try p.expectExpr();
3056
3057 if (p.eatToken(.r_paren)) |rparen| {
3058 return p.addNode(.{
3059 .tag = .asm_simple,
3060 .main_token = asm_token,
3061 .data = .{
3062 .lhs = template,
3063 .rhs = rparen,
3064 },
3065 });
3066 }
18213067
1822 if (p.eatToken(.Colon) != null) {
1823 const outputs = try p.parseAsmOutputList();
1824 defer p.gpa.free(outputs);
1825 arena_outputs = try p.arena.allocator.dupe(Node.Asm.Output, outputs);
3068 _ = try p.expectToken(.colon);
18263069
1827 if (p.eatToken(.Colon) != null) {
1828 const inputs = try p.parseAsmInputList();
1829 defer p.gpa.free(inputs);
1830 arena_inputs = try p.arena.allocator.dupe(Node.Asm.Input, inputs);
3070 var list = std.ArrayList(Node.Index).init(p.gpa);
3071 defer list.deinit();
18313072
1832 if (p.eatToken(.Colon) != null) {
1833 const clobbers = try ListParseFn(*Node, parseStringLiteral)(p);
1834 defer p.gpa.free(clobbers);
1835 arena_clobbers = try p.arena.allocator.dupe(*Node, clobbers);
3073 while (true) {
3074 const output_item = try p.parseAsmOutputItem();
3075 if (output_item == 0) break;
3076 try list.append(output_item);
3077 switch (p.token_tags[p.tok_i]) {
3078 .comma => p.tok_i += 1,
3079 .colon, .r_paren, .r_brace, .r_bracket => break, // All possible delimiters.
3080 else => {
3081 // This is likely just a missing comma;
3082 // give an error but continue parsing this list.
3083 try p.warnExpected(.comma);
3084 },
3085 }
3086 }
3087 if (p.eatToken(.colon)) |_| {
3088 while (true) {
3089 const input_item = try p.parseAsmInputItem();
3090 if (input_item == 0) break;
3091 try list.append(input_item);
3092 switch (p.token_tags[p.tok_i]) {
3093 .comma => p.tok_i += 1,
3094 .colon, .r_paren, .r_brace, .r_bracket => break, // All possible delimiters.
3095 else => {
3096 // This is likely just a missing comma;
3097 // give an error but continue parsing this list.
3098 try p.warnExpected(.comma);
3099 },
3100 }
3101 }
3102 if (p.eatToken(.colon)) |_| {
3103 while (p.eatToken(.string_literal)) |_| {
3104 switch (p.token_tags[p.tok_i]) {
3105 .comma => p.tok_i += 1,
3106 .colon, .r_paren, .r_brace, .r_bracket => break,
3107 else => {
3108 // This is likely just a missing comma;
3109 // give an error but continue parsing this list.
3110 try p.warnExpected(.comma);
3111 },
3112 }
18363113 }
18373114 }
18383115 }
1839
1840 const node = try p.arena.allocator.create(Node.Asm);
1841 node.* = .{
1842 .asm_token = asm_token,
1843 .volatile_token = volatile_token,
1844 .template = template,
1845 .outputs = arena_outputs,
1846 .inputs = arena_inputs,
1847 .clobbers = arena_clobbers,
1848 .rparen = try p.expectToken(.RParen),
1849 };
1850
1851 return &node.base;
1852 }
1853
1854 /// DOT IDENTIFIER
1855 fn parseAnonLiteral(p: *Parser) !?*Node {
1856 const dot = p.eatToken(.Period) orelse return null;
1857
1858 // anon enum literal
1859 if (p.eatToken(.Identifier)) |name| {
1860 const node = try p.arena.allocator.create(Node.EnumLiteral);
1861 node.* = .{
1862 .dot = dot,
1863 .name = name,
1864 };
1865 return &node.base;
1866 }
1867
1868 if (try p.parseAnonInitList(dot)) |node| {
1869 return node;
1870 }
1871
1872 p.putBackToken(dot);
1873 return null;
3116 const rparen = try p.expectToken(.r_paren);
3117 const span = try p.listToSpan(list.items);
3118 return p.addNode(.{
3119 .tag = .@"asm",
3120 .main_token = asm_token,
3121 .data = .{
3122 .lhs = template,
3123 .rhs = try p.addExtra(Node.Asm{
3124 .items_start = span.start,
3125 .items_end = span.end,
3126 .rparen = rparen,
3127 }),
3128 },
3129 });
18743130 }
18753131
18763132 /// AsmOutputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN (MINUSRARROW TypeExpr / IDENTIFIER) RPAREN
1877 fn parseAsmOutputItem(p: *Parser) !?Node.Asm.Output {
1878 const lbracket = p.eatToken(.LBracket) orelse return null;
1879 const name = try p.expectNode(parseIdentifier, .{
1880 .ExpectedIdentifier = .{ .token = p.tok_i },
1881 });
1882 _ = try p.expectToken(.RBracket);
1883
1884 const constraint = try p.expectNode(parseStringLiteral, .{
1885 .ExpectedStringLiteral = .{ .token = p.tok_i },
1886 });
1887
1888 _ = try p.expectToken(.LParen);
1889 const kind: Node.Asm.Output.Kind = blk: {
1890 if (p.eatToken(.Arrow) != null) {
1891 const return_ident = try p.expectNode(parseTypeExpr, .{
1892 .ExpectedTypeExpr = .{ .token = p.tok_i },
1893 });
1894 break :blk .{ .Return = return_ident };
3133 fn parseAsmOutputItem(p: *Parser) !Node.Index {
3134 _ = p.eatToken(.l_bracket) orelse return null_node;
3135 const identifier = try p.expectToken(.identifier);
3136 _ = try p.expectToken(.r_bracket);
3137 _ = try p.expectToken(.string_literal);
3138 _ = try p.expectToken(.l_paren);
3139 const type_expr: Node.Index = blk: {
3140 if (p.eatToken(.arrow)) |_| {
3141 break :blk try p.expectTypeExpr();
3142 } else {
3143 _ = try p.expectToken(.identifier);
3144 break :blk null_node;
18953145 }
1896 const variable = try p.expectNode(parseIdentifier, .{
1897 .ExpectedIdentifier = .{ .token = p.tok_i },
1898 });
1899 break :blk .{ .Variable = variable.castTag(.Identifier).? };
1900 };
1901 const rparen = try p.expectToken(.RParen);
1902
1903 return Node.Asm.Output{
1904 .lbracket = lbracket,
1905 .symbolic_name = name,
1906 .constraint = constraint,
1907 .kind = kind,
1908 .rparen = rparen,
19093146 };
3147 const rparen = try p.expectToken(.r_paren);
3148 return p.addNode(.{
3149 .tag = .asm_output,
3150 .main_token = identifier,
3151 .data = .{
3152 .lhs = type_expr,
3153 .rhs = rparen,
3154 },
3155 });
19103156 }
19113157
19123158 /// AsmInputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN Expr RPAREN
1913 fn parseAsmInputItem(p: *Parser) !?Node.Asm.Input {
1914 const lbracket = p.eatToken(.LBracket) orelse return null;
1915 const name = try p.expectNode(parseIdentifier, .{
1916 .ExpectedIdentifier = .{ .token = p.tok_i },
1917 });
1918 _ = try p.expectToken(.RBracket);
1919
1920 const constraint = try p.expectNode(parseStringLiteral, .{
1921 .ExpectedStringLiteral = .{ .token = p.tok_i },
1922 });
1923
1924 _ = try p.expectToken(.LParen);
1925 const expr = try p.expectNode(parseExpr, .{
1926 .ExpectedExpr = .{ .token = p.tok_i },
3159 fn parseAsmInputItem(p: *Parser) !Node.Index {
3160 _ = p.eatToken(.l_bracket) orelse return null_node;
3161 const identifier = try p.expectToken(.identifier);
3162 _ = try p.expectToken(.r_bracket);
3163 _ = try p.expectToken(.string_literal);
3164 _ = try p.expectToken(.l_paren);
3165 const expr = try p.expectExpr();
3166 const rparen = try p.expectToken(.r_paren);
3167 return p.addNode(.{
3168 .tag = .asm_input,
3169 .main_token = identifier,
3170 .data = .{
3171 .lhs = expr,
3172 .rhs = rparen,
3173 },
19273174 });
1928 const rparen = try p.expectToken(.RParen);
1929
1930 return Node.Asm.Input{
1931 .lbracket = lbracket,
1932 .symbolic_name = name,
1933 .constraint = constraint,
1934 .expr = expr,
1935 .rparen = rparen,
1936 };
19373175 }
19383176
19393177 /// BreakLabel <- COLON IDENTIFIER
1940 fn parseBreakLabel(p: *Parser) !?TokenIndex {
1941 _ = p.eatToken(.Colon) orelse return null;
1942 const ident = try p.expectToken(.Identifier);
1943 return ident;
3178 fn parseBreakLabel(p: *Parser) !TokenIndex {
3179 _ = p.eatToken(.colon) orelse return @as(TokenIndex, 0);
3180 return p.expectToken(.identifier);
19443181 }
19453182
19463183 /// BlockLabel <- IDENTIFIER COLON
1947 fn parseBlockLabel(p: *Parser, colon_token: *TokenIndex) ?TokenIndex {
1948 const identifier = p.eatToken(.Identifier) orelse return null;
1949 if (p.eatToken(.Colon)) |colon| {
1950 colon_token.* = colon;
3184 fn parseBlockLabel(p: *Parser) TokenIndex {
3185 if (p.token_tags[p.tok_i] == .identifier and
3186 p.token_tags[p.tok_i + 1] == .colon)
3187 {
3188 const identifier = p.tok_i;
3189 p.tok_i += 2;
19513190 return identifier;
19523191 }
1953 p.putBackToken(identifier);
1954 return null;
3192 return 0;
19553193 }
19563194
19573195 /// FieldInit <- DOT IDENTIFIER EQUAL Expr
1958 fn parseFieldInit(p: *Parser) !?*Node {
1959 const period_token = p.eatToken(.Period) orelse return null;
1960 const name_token = p.eatToken(.Identifier) orelse {
1961 // Because of anon literals `.{` is also valid.
1962 p.putBackToken(period_token);
1963 return null;
1964 };
1965 const eq_token = p.eatToken(.Equal) orelse {
1966 // `.Name` may also be an enum literal, which is a later rule.
1967 p.putBackToken(name_token);
1968 p.putBackToken(period_token);
1969 return null;
1970 };
1971 const expr_node = try p.expectNode(parseExpr, .{
1972 .ExpectedExpr = .{ .token = p.tok_i },
1973 });
3196 fn parseFieldInit(p: *Parser) !Node.Index {
3197 if (p.token_tags[p.tok_i + 0] == .period and
3198 p.token_tags[p.tok_i + 1] == .identifier and
3199 p.token_tags[p.tok_i + 2] == .equal)
3200 {
3201 p.tok_i += 3;
3202 return p.expectExpr();
3203 } else {
3204 return null_node;
3205 }
3206 }
19743207
1975 const node = try p.arena.allocator.create(Node.FieldInitializer);
1976 node.* = .{
1977 .period_token = period_token,
1978 .name_token = name_token,
1979 .expr = expr_node,
1980 };
1981 return &node.base;
3208 fn expectFieldInit(p: *Parser) !Node.Index {
3209 _ = try p.expectToken(.period);
3210 _ = try p.expectToken(.identifier);
3211 _ = try p.expectToken(.equal);
3212 return p.expectExpr();
19823213 }
19833214
19843215 /// WhileContinueExpr <- COLON LPAREN AssignExpr RPAREN
1985 fn parseWhileContinueExpr(p: *Parser) !?*Node {
1986 _ = p.eatToken(.Colon) orelse return null;
1987 _ = try p.expectToken(.LParen);
1988 const node = try p.expectNode(parseAssignExpr, .{
1989 .ExpectedExprOrAssignment = .{ .token = p.tok_i },
1990 });
1991 _ = try p.expectToken(.RParen);
3216 fn parseWhileContinueExpr(p: *Parser) !Node.Index {
3217 _ = p.eatToken(.colon) orelse return null_node;
3218 _ = try p.expectToken(.l_paren);
3219 const node = try p.parseAssignExpr();
3220 if (node == 0) return p.fail(.expected_expr_or_assignment);
3221 _ = try p.expectToken(.r_paren);
19923222 return node;
19933223 }
19943224
19953225 /// LinkSection <- KEYWORD_linksection LPAREN Expr RPAREN
1996 fn parseLinkSection(p: *Parser) !?*Node {
1997 _ = p.eatToken(.Keyword_linksection) orelse return null;
1998 _ = try p.expectToken(.LParen);
1999 const expr_node = try p.expectNode(parseExpr, .{
2000 .ExpectedExpr = .{ .token = p.tok_i },
2001 });
2002 _ = try p.expectToken(.RParen);
3226 fn parseLinkSection(p: *Parser) !Node.Index {
3227 _ = p.eatToken(.keyword_linksection) orelse return null_node;
3228 _ = try p.expectToken(.l_paren);
3229 const expr_node = try p.expectExpr();
3230 _ = try p.expectToken(.r_paren);
20033231 return expr_node;
20043232 }
20053233
20063234 /// CallConv <- KEYWORD_callconv LPAREN Expr RPAREN
2007 fn parseCallconv(p: *Parser) !?*Node {
2008 _ = p.eatToken(.Keyword_callconv) orelse return null;
2009 _ = try p.expectToken(.LParen);
2010 const expr_node = try p.expectNode(parseExpr, .{
2011 .ExpectedExpr = .{ .token = p.tok_i },
2012 });
2013 _ = try p.expectToken(.RParen);
3235 fn parseCallconv(p: *Parser) !Node.Index {
3236 _ = p.eatToken(.keyword_callconv) orelse return null_node;
3237 _ = try p.expectToken(.l_paren);
3238 const expr_node = try p.expectExpr();
3239 _ = try p.expectToken(.r_paren);
20143240 return expr_node;
20153241 }
20163242
2017 /// ParamDecl <- (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType
2018 fn parseParamDecl(p: *Parser) !?Node.FnProto.ParamDecl {
2019 const doc_comments = try p.parseDocComment();
2020 const noalias_token = p.eatToken(.Keyword_noalias);
2021 const comptime_token = if (noalias_token == null) p.eatToken(.Keyword_comptime) else null;
2022 const name_token = blk: {
2023 const identifier = p.eatToken(.Identifier) orelse break :blk null;
2024 if (p.eatToken(.Colon) != null) break :blk identifier;
2025 p.putBackToken(identifier); // ParamType may also be an identifier
2026 break :blk null;
2027 };
2028 const param_type = (try p.parseParamType()) orelse {
2029 // Only return cleanly if no keyword, identifier, or doc comment was found
2030 if (noalias_token == null and
2031 comptime_token == null and
2032 name_token == null and
2033 doc_comments == null)
2034 {
2035 return null;
2036 }
2037 try p.errors.append(p.gpa, .{
2038 .ExpectedParamType = .{ .token = p.tok_i },
2039 });
2040 return error.ParseError;
2041 };
2042
2043 return Node.FnProto.ParamDecl{
2044 .doc_comments = doc_comments,
2045 .comptime_token = comptime_token,
2046 .noalias_token = noalias_token,
2047 .name_token = name_token,
2048 .param_type = param_type,
2049 };
2050 }
2051
3243 /// ParamDecl
3244 /// <- (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType
3245 /// / DOT3
20523246 /// ParamType
20533247 /// <- Keyword_anytype
2054 /// / DOT3
20553248 /// / TypeExpr
2056 fn parseParamType(p: *Parser) !?Node.FnProto.ParamDecl.ParamType {
2057 // TODO cast from tuple to error union is broken
2058 const P = Node.FnProto.ParamDecl.ParamType;
2059 if (try p.parseAnyType()) |node| return P{ .any_type = node };
2060 if (try p.parseTypeExpr()) |node| return P{ .type_expr = node };
2061 return null;
2062 }
2063
2064 /// IfPrefix <- KEYWORD_if LPAREN Expr RPAREN PtrPayload?
2065 fn parseIfPrefix(p: *Parser) !?*Node {
2066 const if_token = p.eatToken(.Keyword_if) orelse return null;
2067 _ = try p.expectToken(.LParen);
2068 const condition = try p.expectNode(parseExpr, .{
2069 .ExpectedExpr = .{ .token = p.tok_i },
2070 });
2071 _ = try p.expectToken(.RParen);
2072 const payload = try p.parsePtrPayload();
2073
2074 const node = try p.arena.allocator.create(Node.If);
2075 node.* = .{
2076 .if_token = if_token,
2077 .condition = condition,
2078 .payload = payload,
2079 .body = undefined, // set by caller
2080 .@"else" = null,
2081 };
2082 return &node.base;
2083 }
2084
2085 /// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr?
2086 fn parseWhilePrefix(p: *Parser) !?*Node {
2087 const while_token = p.eatToken(.Keyword_while) orelse return null;
2088
2089 _ = try p.expectToken(.LParen);
2090 const condition = try p.expectNode(parseExpr, .{
2091 .ExpectedExpr = .{ .token = p.tok_i },
2092 });
2093 _ = try p.expectToken(.RParen);
2094
2095 const payload = try p.parsePtrPayload();
2096 const continue_expr = try p.parseWhileContinueExpr();
2097
2098 const node = try p.arena.allocator.create(Node.While);
2099 node.* = .{
2100 .label = null,
2101 .inline_token = null,
2102 .while_token = while_token,
2103 .condition = condition,
2104 .payload = payload,
2105 .continue_expr = continue_expr,
2106 .body = undefined, // set by caller
2107 .@"else" = null,
2108 };
2109 return &node.base;
2110 }
2111
2112 /// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload
2113 fn parseForPrefix(p: *Parser) !?*Node {
2114 const for_token = p.eatToken(.Keyword_for) orelse return null;
2115
2116 _ = try p.expectToken(.LParen);
2117 const array_expr = try p.expectNode(parseExpr, .{
2118 .ExpectedExpr = .{ .token = p.tok_i },
2119 });
2120 _ = try p.expectToken(.RParen);
2121
2122 const payload = try p.expectNode(parsePtrIndexPayload, .{
2123 .ExpectedPayload = .{ .token = p.tok_i },
2124 });
2125
2126 const node = try p.arena.allocator.create(Node.For);
2127 node.* = .{
2128 .label = null,
2129 .inline_token = null,
2130 .for_token = for_token,
2131 .array_expr = array_expr,
2132 .payload = payload,
2133 .body = undefined, // set by caller
2134 .@"else" = null,
2135 };
2136 return &node.base;
3249 /// This function can return null nodes and then still return nodes afterwards,
3250 /// such as in the case of anytype and `...`. Caller must look for rparen to find
3251 /// out when there are no more param decls left.
3252 fn expectParamDecl(p: *Parser) !Node.Index {
3253 _ = try p.eatDocComments();
3254 switch (p.token_tags[p.tok_i]) {
3255 .keyword_noalias, .keyword_comptime => p.tok_i += 1,
3256 .ellipsis3 => {
3257 p.tok_i += 1;
3258 return null_node;
3259 },
3260 else => {},
3261 }
3262 if (p.token_tags[p.tok_i] == .identifier and
3263 p.token_tags[p.tok_i + 1] == .colon)
3264 {
3265 p.tok_i += 2;
3266 }
3267 switch (p.token_tags[p.tok_i]) {
3268 .keyword_anytype => {
3269 p.tok_i += 1;
3270 return null_node;
3271 },
3272 else => return p.expectTypeExpr(),
3273 }
21373274 }
21383275
21393276 /// Payload <- PIPE IDENTIFIER PIPE
2140 fn parsePayload(p: *Parser) !?*Node {
2141 const lpipe = p.eatToken(.Pipe) orelse return null;
2142 const identifier = try p.expectNode(parseIdentifier, .{
2143 .ExpectedIdentifier = .{ .token = p.tok_i },
2144 });
2145 const rpipe = try p.expectToken(.Pipe);
2146
2147 const node = try p.arena.allocator.create(Node.Payload);
2148 node.* = .{
2149 .lpipe = lpipe,
2150 .error_symbol = identifier,
2151 .rpipe = rpipe,
2152 };
2153 return &node.base;
3277 fn parsePayload(p: *Parser) !TokenIndex {
3278 _ = p.eatToken(.pipe) orelse return @as(TokenIndex, 0);
3279 const identifier = try p.expectToken(.identifier);
3280 _ = try p.expectToken(.pipe);
3281 return identifier;
21543282 }
21553283
21563284 /// PtrPayload <- PIPE ASTERISK? IDENTIFIER PIPE
2157 fn parsePtrPayload(p: *Parser) !?*Node {
2158 const lpipe = p.eatToken(.Pipe) orelse return null;
2159 const asterisk = p.eatToken(.Asterisk);
2160 const identifier = try p.expectNode(parseIdentifier, .{
2161 .ExpectedIdentifier = .{ .token = p.tok_i },
2162 });
2163 const rpipe = try p.expectToken(.Pipe);
2164
2165 const node = try p.arena.allocator.create(Node.PointerPayload);
2166 node.* = .{
2167 .lpipe = lpipe,
2168 .ptr_token = asterisk,
2169 .value_symbol = identifier,
2170 .rpipe = rpipe,
2171 };
2172 return &node.base;
3285 fn parsePtrPayload(p: *Parser) !TokenIndex {
3286 _ = p.eatToken(.pipe) orelse return @as(TokenIndex, 0);
3287 _ = p.eatToken(.asterisk);
3288 const identifier = try p.expectToken(.identifier);
3289 _ = try p.expectToken(.pipe);
3290 return identifier;
21733291 }
21743292
21753293 /// PtrIndexPayload <- PIPE ASTERISK? IDENTIFIER (COMMA IDENTIFIER)? PIPE
2176 fn parsePtrIndexPayload(p: *Parser) !?*Node {
2177 const lpipe = p.eatToken(.Pipe) orelse return null;
2178 const asterisk = p.eatToken(.Asterisk);
2179 const identifier = try p.expectNode(parseIdentifier, .{
2180 .ExpectedIdentifier = .{ .token = p.tok_i },
2181 });
2182
2183 const index = if (p.eatToken(.Comma) == null)
2184 null
2185 else
2186 try p.expectNode(parseIdentifier, .{
2187 .ExpectedIdentifier = .{ .token = p.tok_i },
2188 });
2189
2190 const rpipe = try p.expectToken(.Pipe);
2191
2192 const node = try p.arena.allocator.create(Node.PointerIndexPayload);
2193 node.* = .{
2194 .lpipe = lpipe,
2195 .ptr_token = asterisk,
2196 .value_symbol = identifier,
2197 .index_symbol = index,
2198 .rpipe = rpipe,
2199 };
2200 return &node.base;
3294 /// Returns the first identifier token, if any.
3295 fn parsePtrIndexPayload(p: *Parser) !TokenIndex {
3296 _ = p.eatToken(.pipe) orelse return @as(TokenIndex, 0);
3297 _ = p.eatToken(.asterisk);
3298 const identifier = try p.expectToken(.identifier);
3299 if (p.eatToken(.comma) != null) {
3300 _ = try p.expectToken(.identifier);
3301 }
3302 _ = try p.expectToken(.pipe);
3303 return identifier;
22013304 }
22023305
22033306 /// SwitchProng <- SwitchCase EQUALRARROW PtrPayload? AssignExpr
2204 fn parseSwitchProng(p: *Parser) !?*Node {
2205 const node = (try p.parseSwitchCase()) orelse return null;
2206 const arrow = try p.expectToken(.EqualAngleBracketRight);
2207 const payload = try p.parsePtrPayload();
2208 const expr = try p.expectNode(parseAssignExpr, .{
2209 .ExpectedExprOrAssignment = .{ .token = p.tok_i },
2210 });
2211
2212 const switch_case = node.cast(Node.SwitchCase).?;
2213 switch_case.arrow_token = arrow;
2214 switch_case.payload = payload;
2215 switch_case.expr = expr;
2216
2217 return node;
2218 }
2219
22203307 /// SwitchCase
22213308 /// <- SwitchItem (COMMA SwitchItem)* COMMA?
22223309 /// / KEYWORD_else
2223 fn parseSwitchCase(p: *Parser) !?*Node {
2224 var list = std.ArrayList(*Node).init(p.gpa);
2225 defer list.deinit();
2226
2227 if (try p.parseSwitchItem()) |first_item| {
2228 try list.append(first_item);
2229 while (p.eatToken(.Comma) != null) {
2230 const next_item = (try p.parseSwitchItem()) orelse break;
2231 try list.append(next_item);
2232 }
2233 } else if (p.eatToken(.Keyword_else)) |else_token| {
2234 const else_node = try p.arena.allocator.create(Node.SwitchElse);
2235 else_node.* = .{
2236 .token = else_token,
2237 };
2238 try list.append(&else_node.base);
2239 } else return null;
2240
2241 const node = try Node.SwitchCase.alloc(&p.arena.allocator, list.items.len);
2242 node.* = .{
2243 .items_len = list.items.len,
2244 .arrow_token = undefined, // set by caller
2245 .payload = null,
2246 .expr = undefined, // set by caller
2247 };
2248 std.mem.copy(*Node, node.items(), list.items);
2249 return &node.base;
2250 }
2251
2252 /// SwitchItem <- Expr (DOT3 Expr)?
2253 fn parseSwitchItem(p: *Parser) !?*Node {
2254 const expr = (try p.parseExpr()) orelse return null;
2255 if (p.eatToken(.Ellipsis3)) |token| {
2256 const range_end = try p.expectNode(parseExpr, .{
2257 .ExpectedExpr = .{ .token = p.tok_i },
3310 fn parseSwitchProng(p: *Parser) !Node.Index {
3311 if (p.eatToken(.keyword_else)) |_| {
3312 const arrow_token = try p.expectToken(.equal_angle_bracket_right);
3313 _ = try p.parsePtrPayload();
3314 return p.addNode(.{
3315 .tag = .switch_case_one,
3316 .main_token = arrow_token,
3317 .data = .{
3318 .lhs = 0,
3319 .rhs = try p.expectAssignExpr(),
3320 },
22583321 });
2259
2260 const node = try p.arena.allocator.create(Node.SimpleInfixOp);
2261 node.* = .{
2262 .base = Node{ .tag = .Range },
2263 .op_token = token,
2264 .lhs = expr,
2265 .rhs = range_end,
2266 };
2267 return &node.base;
22683322 }
2269 return expr;
2270 }
2271
2272 /// AssignOp
2273 /// <- ASTERISKEQUAL
2274 /// / SLASHEQUAL
2275 /// / PERCENTEQUAL
2276 /// / PLUSEQUAL
2277 /// / MINUSEQUAL
2278 /// / LARROW2EQUAL
2279 /// / RARROW2EQUAL
2280 /// / AMPERSANDEQUAL
2281 /// / CARETEQUAL
2282 /// / PIPEEQUAL
2283 /// / ASTERISKPERCENTEQUAL
2284 /// / PLUSPERCENTEQUAL
2285 /// / MINUSPERCENTEQUAL
2286 /// / EQUAL
2287 fn parseAssignOp(p: *Parser) !?*Node {
2288 const token = p.nextToken();
2289 const op: Node.Tag = switch (p.token_ids[token]) {
2290 .AsteriskEqual => .AssignMul,
2291 .SlashEqual => .AssignDiv,
2292 .PercentEqual => .AssignMod,
2293 .PlusEqual => .AssignAdd,
2294 .MinusEqual => .AssignSub,
2295 .AngleBracketAngleBracketLeftEqual => .AssignBitShiftLeft,
2296 .AngleBracketAngleBracketRightEqual => .AssignBitShiftRight,
2297 .AmpersandEqual => .AssignBitAnd,
2298 .CaretEqual => .AssignBitXor,
2299 .PipeEqual => .AssignBitOr,
2300 .AsteriskPercentEqual => .AssignMulWrap,
2301 .PlusPercentEqual => .AssignAddWrap,
2302 .MinusPercentEqual => .AssignSubWrap,
2303 .Equal => .Assign,
2304 else => {
2305 p.putBackToken(token);
2306 return null;
2307 },
2308 };
2309
2310 const node = try p.arena.allocator.create(Node.SimpleInfixOp);
2311 node.* = .{
2312 .base = .{ .tag = op },
2313 .op_token = token,
2314 .lhs = undefined, // set by caller
2315 .rhs = undefined, // set by caller
2316 };
2317 return &node.base;
2318 }
2319
2320 /// CompareOp
2321 /// <- EQUALEQUAL
2322 /// / EXCLAMATIONMARKEQUAL
2323 /// / LARROW
2324 /// / RARROW
2325 /// / LARROWEQUAL
2326 /// / RARROWEQUAL
2327 fn parseCompareOp(p: *Parser) !?*Node {
2328 const token = p.nextToken();
2329 const op: Node.Tag = switch (p.token_ids[token]) {
2330 .EqualEqual => .EqualEqual,
2331 .BangEqual => .BangEqual,
2332 .AngleBracketLeft => .LessThan,
2333 .AngleBracketRight => .GreaterThan,
2334 .AngleBracketLeftEqual => .LessOrEqual,
2335 .AngleBracketRightEqual => .GreaterOrEqual,
2336 else => {
2337 p.putBackToken(token);
2338 return null;
2339 },
2340 };
2341
2342 return p.createInfixOp(token, op);
2343 }
2344
2345 /// BitwiseOp
2346 /// <- AMPERSAND
2347 /// / CARET
2348 /// / PIPE
2349 /// / KEYWORD_orelse
2350 /// / KEYWORD_catch Payload?
2351 fn parseBitwiseOp(p: *Parser) !?*Node {
2352 const token = p.nextToken();
2353 const op: Node.Tag = switch (p.token_ids[token]) {
2354 .Ampersand => .BitAnd,
2355 .Caret => .BitXor,
2356 .Pipe => .BitOr,
2357 .Keyword_orelse => .OrElse,
2358 .Keyword_catch => {
2359 const payload = try p.parsePayload();
2360 const node = try p.arena.allocator.create(Node.Catch);
2361 node.* = .{
2362 .op_token = token,
2363 .lhs = undefined, // set by caller
2364 .rhs = undefined, // set by caller
2365 .payload = payload,
2366 };
2367 return &node.base;
2368 },
2369 else => {
2370 p.putBackToken(token);
2371 return null;
2372 },
2373 };
2374
2375 return p.createInfixOp(token, op);
2376 }
2377
2378 /// BitShiftOp
2379 /// <- LARROW2
2380 /// / RARROW2
2381 fn parseBitShiftOp(p: *Parser) !?*Node {
2382 const token = p.nextToken();
2383 const op: Node.Tag = switch (p.token_ids[token]) {
2384 .AngleBracketAngleBracketLeft => .BitShiftLeft,
2385 .AngleBracketAngleBracketRight => .BitShiftRight,
2386 else => {
2387 p.putBackToken(token);
2388 return null;
2389 },
2390 };
2391
2392 return p.createInfixOp(token, op);
2393 }
2394
2395 /// AdditionOp
2396 /// <- PLUS
2397 /// / MINUS
2398 /// / PLUS2
2399 /// / PLUSPERCENT
2400 /// / MINUSPERCENT
2401 fn parseAdditionOp(p: *Parser) !?*Node {
2402 const token = p.nextToken();
2403 const op: Node.Tag = switch (p.token_ids[token]) {
2404 .Plus => .Add,
2405 .Minus => .Sub,
2406 .PlusPlus => .ArrayCat,
2407 .PlusPercent => .AddWrap,
2408 .MinusPercent => .SubWrap,
2409 else => {
2410 p.putBackToken(token);
2411 return null;
2412 },
2413 };
2414
2415 return p.createInfixOp(token, op);
2416 }
2417
2418 /// MultiplyOp
2419 /// <- PIPE2
2420 /// / ASTERISK
2421 /// / SLASH
2422 /// / PERCENT
2423 /// / ASTERISK2
2424 /// / ASTERISKPERCENT
2425 fn parseMultiplyOp(p: *Parser) !?*Node {
2426 const token = p.nextToken();
2427 const op: Node.Tag = switch (p.token_ids[token]) {
2428 .PipePipe => .MergeErrorSets,
2429 .Asterisk => .Mul,
2430 .Slash => .Div,
2431 .Percent => .Mod,
2432 .AsteriskAsterisk => .ArrayMult,
2433 .AsteriskPercent => .MulWrap,
2434 else => {
2435 p.putBackToken(token);
2436 return null;
2437 },
2438 };
2439
2440 return p.createInfixOp(token, op);
2441 }
2442
2443 /// PrefixOp
2444 /// <- EXCLAMATIONMARK
2445 /// / MINUS
2446 /// / TILDE
2447 /// / MINUSPERCENT
2448 /// / AMPERSAND
2449 /// / KEYWORD_try
2450 /// / KEYWORD_await
2451 fn parsePrefixOp(p: *Parser) !?*Node {
2452 const token = p.nextToken();
2453 switch (p.token_ids[token]) {
2454 .Bang => return p.allocSimplePrefixOp(.BoolNot, token),
2455 .Minus => return p.allocSimplePrefixOp(.Negation, token),
2456 .Tilde => return p.allocSimplePrefixOp(.BitNot, token),
2457 .MinusPercent => return p.allocSimplePrefixOp(.NegationWrap, token),
2458 .Ampersand => return p.allocSimplePrefixOp(.AddressOf, token),
2459 .Keyword_try => return p.allocSimplePrefixOp(.Try, token),
2460 .Keyword_await => return p.allocSimplePrefixOp(.Await, token),
2461 else => {
2462 p.putBackToken(token);
2463 return null;
2464 },
2465 }
2466 }
2467
2468 fn allocSimplePrefixOp(p: *Parser, comptime tag: Node.Tag, token: TokenIndex) !?*Node {
2469 const node = try p.arena.allocator.create(Node.SimplePrefixOp);
2470 node.* = .{
2471 .base = .{ .tag = tag },
2472 .op_token = token,
2473 .rhs = undefined, // set by caller
2474 };
2475 return &node.base;
2476 }
2477
2478 // TODO: ArrayTypeStart is either an array or a slice, but const/allowzero only work on
2479 // pointers. Consider updating this rule:
2480 // ...
2481 // / ArrayTypeStart
2482 // / SliceTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*
2483 // / PtrTypeStart ...
2484
2485 /// PrefixTypeOp
2486 /// <- QUESTIONMARK
2487 /// / KEYWORD_anyframe MINUSRARROW
2488 /// / ArrayTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*
2489 /// / PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*
2490 fn parsePrefixTypeOp(p: *Parser) !?*Node {
2491 if (p.eatToken(.QuestionMark)) |token| {
2492 const node = try p.arena.allocator.create(Node.SimplePrefixOp);
2493 node.* = .{
2494 .base = .{ .tag = .OptionalType },
2495 .op_token = token,
2496 .rhs = undefined, // set by caller
2497 };
2498 return &node.base;
2499 }
2500
2501 if (p.eatToken(.Keyword_anyframe)) |token| {
2502 const arrow = p.eatToken(.Arrow) orelse {
2503 p.putBackToken(token);
2504 return null;
2505 };
2506 const node = try p.arena.allocator.create(Node.AnyFrameType);
2507 node.* = .{
2508 .anyframe_token = token,
2509 .result = .{
2510 .arrow_token = arrow,
2511 .return_type = undefined, // set by caller
3323 const first_item = try p.parseSwitchItem();
3324 if (first_item == 0) return null_node;
3325
3326 if (p.eatToken(.equal_angle_bracket_right)) |arrow_token| {
3327 _ = try p.parsePtrPayload();
3328 return p.addNode(.{
3329 .tag = .switch_case_one,
3330 .main_token = arrow_token,
3331 .data = .{
3332 .lhs = first_item,
3333 .rhs = try p.expectAssignExpr(),
25123334 },
2513 };
2514 return &node.base;
2515 }
2516
2517 if (try p.parsePtrTypeStart()) |node| {
2518 // If the token encountered was **, there will be two nodes instead of one.
2519 // The attributes should be applied to the rightmost operator.
2520 var ptr_info = if (node.cast(Node.PtrType)) |ptr_type|
2521 if (p.token_ids[ptr_type.op_token] == .AsteriskAsterisk)
2522 &ptr_type.rhs.cast(Node.PtrType).?.ptr_info
2523 else
2524 &ptr_type.ptr_info
2525 else if (node.cast(Node.SliceType)) |slice_type|
2526 &slice_type.ptr_info
2527 else
2528 unreachable;
2529
2530 while (true) {
2531 if (p.eatToken(.Keyword_align)) |align_token| {
2532 const lparen = try p.expectToken(.LParen);
2533 const expr_node = try p.expectNode(parseExpr, .{
2534 .ExpectedExpr = .{ .token = p.tok_i },
2535 });
2536
2537 // Optional bit range
2538 const bit_range = if (p.eatToken(.Colon)) |_| bit_range_value: {
2539 const range_start = try p.expectNode(parseIntegerLiteral, .{
2540 .ExpectedIntegerLiteral = .{ .token = p.tok_i },
2541 });
2542 _ = try p.expectToken(.Colon);
2543 const range_end = try p.expectNode(parseIntegerLiteral, .{
2544 .ExpectedIntegerLiteral = .{ .token = p.tok_i },
2545 });
2546
2547 break :bit_range_value ast.PtrInfo.Align.BitRange{
2548 .start = range_start,
2549 .end = range_end,
2550 };
2551 } else null;
2552 _ = try p.expectToken(.RParen);
2553
2554 if (ptr_info.align_info != null) {
2555 try p.errors.append(p.gpa, .{
2556 .ExtraAlignQualifier = .{ .token = p.tok_i - 1 },
2557 });
2558 continue;
2559 }
2560
2561 ptr_info.align_info = ast.PtrInfo.Align{
2562 .node = expr_node,
2563 .bit_range = bit_range,
2564 };
2565
2566 continue;
2567 }
2568 if (p.eatToken(.Keyword_const)) |const_token| {
2569 if (ptr_info.const_token != null) {
2570 try p.errors.append(p.gpa, .{
2571 .ExtraConstQualifier = .{ .token = p.tok_i - 1 },
2572 });
2573 continue;
2574 }
2575 ptr_info.const_token = const_token;
2576 continue;
2577 }
2578 if (p.eatToken(.Keyword_volatile)) |volatile_token| {
2579 if (ptr_info.volatile_token != null) {
2580 try p.errors.append(p.gpa, .{
2581 .ExtraVolatileQualifier = .{ .token = p.tok_i - 1 },
2582 });
2583 continue;
2584 }
2585 ptr_info.volatile_token = volatile_token;
2586 continue;
2587 }
2588 if (p.eatToken(.Keyword_allowzero)) |allowzero_token| {
2589 if (ptr_info.allowzero_token != null) {
2590 try p.errors.append(p.gpa, .{
2591 .ExtraAllowZeroQualifier = .{ .token = p.tok_i - 1 },
2592 });
2593 continue;
2594 }
2595 ptr_info.allowzero_token = allowzero_token;
2596 continue;
2597 }
2598 break;
2599 }
2600
2601 return node;
2602 }
2603
2604 if (try p.parseArrayTypeStart()) |node| {
2605 if (node.cast(Node.SliceType)) |slice_type| {
2606 // Collect pointer qualifiers in any order, but disallow duplicates
2607 while (true) {
2608 if (try p.parseByteAlign()) |align_expr| {
2609 if (slice_type.ptr_info.align_info != null) {
2610 try p.errors.append(p.gpa, .{
2611 .ExtraAlignQualifier = .{ .token = p.tok_i - 1 },
2612 });
2613 continue;
2614 }
2615 slice_type.ptr_info.align_info = ast.PtrInfo.Align{
2616 .node = align_expr,
2617 .bit_range = null,
2618 };
2619 continue;
2620 }
2621 if (p.eatToken(.Keyword_const)) |const_token| {
2622 if (slice_type.ptr_info.const_token != null) {
2623 try p.errors.append(p.gpa, .{
2624 .ExtraConstQualifier = .{ .token = p.tok_i - 1 },
2625 });
2626 continue;
2627 }
2628 slice_type.ptr_info.const_token = const_token;
2629 continue;
2630 }
2631 if (p.eatToken(.Keyword_volatile)) |volatile_token| {
2632 if (slice_type.ptr_info.volatile_token != null) {
2633 try p.errors.append(p.gpa, .{
2634 .ExtraVolatileQualifier = .{ .token = p.tok_i - 1 },
2635 });
2636 continue;
2637 }
2638 slice_type.ptr_info.volatile_token = volatile_token;
2639 continue;
2640 }
2641 if (p.eatToken(.Keyword_allowzero)) |allowzero_token| {
2642 if (slice_type.ptr_info.allowzero_token != null) {
2643 try p.errors.append(p.gpa, .{
2644 .ExtraAllowZeroQualifier = .{ .token = p.tok_i - 1 },
2645 });
2646 continue;
2647 }
2648 slice_type.ptr_info.allowzero_token = allowzero_token;
2649 continue;
2650 }
2651 break;
2652 }
2653 }
2654 return node;
2655 }
2656
2657 return null;
2658 }
2659
2660 /// SuffixOp
2661 /// <- LBRACKET Expr (DOT2 (Expr (COLON Expr)?)?)? RBRACKET
2662 /// / DOT IDENTIFIER
2663 /// / DOTASTERISK
2664 /// / DOTQUESTIONMARK
2665 fn parseSuffixOp(p: *Parser, lhs: *Node) !?*Node {
2666 if (p.eatToken(.LBracket)) |_| {
2667 const index_expr = try p.expectNode(parseExpr, .{
2668 .ExpectedExpr = .{ .token = p.tok_i },
26693335 });
2670
2671 if (p.eatToken(.Ellipsis2) != null) {
2672 const end_expr = try p.parseExpr();
2673 const sentinel: ?*Node = if (p.eatToken(.Colon) != null)
2674 try p.parseExpr()
2675 else
2676 null;
2677 const rtoken = try p.expectToken(.RBracket);
2678 const node = try p.arena.allocator.create(Node.Slice);
2679 node.* = .{
2680 .lhs = lhs,
2681 .rtoken = rtoken,
2682 .start = index_expr,
2683 .end = end_expr,
2684 .sentinel = sentinel,
2685 };
2686 return &node.base;
2687 }
2688
2689 const rtoken = try p.expectToken(.RBracket);
2690 const node = try p.arena.allocator.create(Node.ArrayAccess);
2691 node.* = .{
2692 .lhs = lhs,
2693 .rtoken = rtoken,
2694 .index_expr = index_expr,
2695 };
2696 return &node.base;
26973336 }
26983337
2699 if (p.eatToken(.PeriodAsterisk)) |period_asterisk| {
2700 const node = try p.arena.allocator.create(Node.SimpleSuffixOp);
2701 node.* = .{
2702 .base = .{ .tag = .Deref },
2703 .lhs = lhs,
2704 .rtoken = period_asterisk,
2705 };
2706 return &node.base;
2707 }
3338 var list = std.ArrayList(Node.Index).init(p.gpa);
3339 defer list.deinit();
27083340
2709 if (p.eatToken(.Invalid_periodasterisks)) |period_asterisk| {
2710 try p.errors.append(p.gpa, .{
2711 .AsteriskAfterPointerDereference = .{ .token = period_asterisk },
2712 });
2713 const node = try p.arena.allocator.create(Node.SimpleSuffixOp);
2714 node.* = .{
2715 .base = .{ .tag = .Deref },
2716 .lhs = lhs,
2717 .rtoken = period_asterisk,
2718 };
2719 return &node.base;
2720 }
3341 try list.append(first_item);
3342 while (p.eatToken(.comma)) |_| {
3343 const next_item = try p.parseSwitchItem();
3344 if (next_item == 0) break;
3345 try list.append(next_item);
3346 }
3347 const span = try p.listToSpan(list.items);
3348 const arrow_token = try p.expectToken(.equal_angle_bracket_right);
3349 _ = try p.parsePtrPayload();
3350 return p.addNode(.{
3351 .tag = .switch_case,
3352 .main_token = arrow_token,
3353 .data = .{
3354 .lhs = try p.addExtra(Node.SubRange{
3355 .start = span.start,
3356 .end = span.end,
3357 }),
3358 .rhs = try p.expectAssignExpr(),
3359 },
3360 });
3361 }
27213362
2722 if (p.eatToken(.Period)) |period| {
2723 if (try p.parseIdentifier()) |identifier| {
2724 const node = try p.arena.allocator.create(Node.SimpleInfixOp);
2725 node.* = .{
2726 .base = Node{ .tag = .Period },
2727 .op_token = period,
2728 .lhs = lhs,
2729 .rhs = identifier,
2730 };
2731 return &node.base;
2732 }
2733 if (p.eatToken(.QuestionMark)) |question_mark| {
2734 const node = try p.arena.allocator.create(Node.SimpleSuffixOp);
2735 node.* = .{
2736 .base = .{ .tag = .UnwrapOptional },
2737 .lhs = lhs,
2738 .rtoken = question_mark,
2739 };
2740 return &node.base;
2741 }
2742 try p.errors.append(p.gpa, .{
2743 .ExpectedSuffixOp = .{ .token = p.tok_i },
3363 /// SwitchItem <- Expr (DOT3 Expr)?
3364 fn parseSwitchItem(p: *Parser) !Node.Index {
3365 const expr = try p.parseExpr();
3366 if (expr == 0) return null_node;
3367
3368 if (p.eatToken(.ellipsis3)) |token| {
3369 return p.addNode(.{
3370 .tag = .switch_range,
3371 .main_token = token,
3372 .data = .{
3373 .lhs = expr,
3374 .rhs = try p.expectExpr(),
3375 },
27443376 });
2745 return null;
27463377 }
2747
2748 return null;
2749 }
2750
2751 /// FnCallArguments <- LPAREN ExprList RPAREN
2752 /// ExprList <- (Expr COMMA)* Expr?
2753 fn parseFnCallArguments(p: *Parser) !?AnnotatedParamList {
2754 if (p.eatToken(.LParen) == null) return null;
2755 const list = try ListParseFn(*Node, parseExpr)(p);
2756 errdefer p.gpa.free(list);
2757 const rparen = try p.expectToken(.RParen);
2758 return AnnotatedParamList{ .list = list, .rparen = rparen };
3378 return expr;
27593379 }
27603380
2761 const AnnotatedParamList = struct {
2762 list: []*Node,
2763 rparen: TokenIndex,
3381 const PtrModifiers = struct {
3382 align_node: Node.Index,
3383 bit_range_start: Node.Index,
3384 bit_range_end: Node.Index,
27643385 };
27653386
2766 /// ArrayTypeStart <- LBRACKET Expr? (COLON Expr)? RBRACKET
2767 fn parseArrayTypeStart(p: *Parser) !?*Node {
2768 const lbracket = p.eatToken(.LBracket) orelse return null;
2769 const expr = try p.parseExpr();
2770 const sentinel = if (p.eatToken(.Colon)) |_|
2771 try p.expectNode(parseExpr, .{
2772 .ExpectedExpr = .{ .token = p.tok_i },
2773 })
2774 else
2775 null;
2776 const rbracket = try p.expectToken(.RBracket);
2777
2778 if (expr) |len_expr| {
2779 if (sentinel) |s| {
2780 const node = try p.arena.allocator.create(Node.ArrayTypeSentinel);
2781 node.* = .{
2782 .op_token = lbracket,
2783 .rhs = undefined, // set by caller
2784 .len_expr = len_expr,
2785 .sentinel = s,
2786 };
2787 return &node.base;
2788 } else {
2789 const node = try p.arena.allocator.create(Node.ArrayType);
2790 node.* = .{
2791 .op_token = lbracket,
2792 .rhs = undefined, // set by caller
2793 .len_expr = len_expr,
2794 };
2795 return &node.base;
2796 }
2797 }
2798
2799 const node = try p.arena.allocator.create(Node.SliceType);
2800 node.* = .{
2801 .op_token = lbracket,
2802 .rhs = undefined, // set by caller
2803 .ptr_info = .{ .sentinel = sentinel },
3387 fn parsePtrModifiers(p: *Parser) !PtrModifiers {
3388 var result: PtrModifiers = .{
3389 .align_node = 0,
3390 .bit_range_start = 0,
3391 .bit_range_end = 0,
28043392 };
2805 return &node.base;
2806 }
2807
2808 /// PtrTypeStart
2809 /// <- ASTERISK
2810 /// / ASTERISK2
2811 /// / LBRACKET ASTERISK (LETTERC / COLON Expr)? RBRACKET
2812 fn parsePtrTypeStart(p: *Parser) !?*Node {
2813 if (p.eatToken(.Asterisk)) |asterisk| {
2814 const sentinel = if (p.eatToken(.Colon)) |_|
2815 try p.expectNode(parseExpr, .{
2816 .ExpectedExpr = .{ .token = p.tok_i },
2817 })
2818 else
2819 null;
2820 const node = try p.arena.allocator.create(Node.PtrType);
2821 node.* = .{
2822 .op_token = asterisk,
2823 .rhs = undefined, // set by caller
2824 .ptr_info = .{ .sentinel = sentinel },
2825 };
2826 return &node.base;
2827 }
2828
2829 if (p.eatToken(.AsteriskAsterisk)) |double_asterisk| {
2830 const node = try p.arena.allocator.create(Node.PtrType);
2831 node.* = .{
2832 .op_token = double_asterisk,
2833 .rhs = undefined, // set by caller
2834 };
2835
2836 // Special case for **, which is its own token
2837 const child = try p.arena.allocator.create(Node.PtrType);
2838 child.* = .{
2839 .op_token = double_asterisk,
2840 .rhs = undefined, // set by caller
2841 };
2842 node.rhs = &child.base;
3393 var saw_const = false;
3394 var saw_volatile = false;
3395 var saw_allowzero = false;
3396 while (true) {
3397 switch (p.token_tags[p.tok_i]) {
3398 .keyword_align => {
3399 if (result.align_node != 0) {
3400 try p.warn(.extra_align_qualifier);
3401 }
3402 p.tok_i += 1;
3403 _ = try p.expectToken(.l_paren);
3404 result.align_node = try p.expectExpr();
3405
3406 if (p.eatToken(.colon)) |_| {
3407 result.bit_range_start = try p.expectExpr();
3408 _ = try p.expectToken(.colon);
3409 result.bit_range_end = try p.expectExpr();
3410 }
28433411
2844 return &node.base;
2845 }
2846 if (p.eatToken(.LBracket)) |lbracket| {
2847 const asterisk = p.eatToken(.Asterisk) orelse {
2848 p.putBackToken(lbracket);
2849 return null;
2850 };
2851 if (p.eatToken(.Identifier)) |ident| {
2852 const token_loc = p.token_locs[ident];
2853 const token_slice = p.source[token_loc.start..token_loc.end];
2854 if (!std.mem.eql(u8, token_slice, "c")) {
2855 p.putBackToken(ident);
2856 } else {
2857 _ = try p.expectToken(.RBracket);
2858 const node = try p.arena.allocator.create(Node.PtrType);
2859 node.* = .{
2860 .op_token = lbracket,
2861 .rhs = undefined, // set by caller
2862 };
2863 return &node.base;
2864 }
3412 _ = try p.expectToken(.r_paren);
3413 },
3414 .keyword_const => {
3415 if (saw_const) {
3416 try p.warn(.extra_const_qualifier);
3417 }
3418 p.tok_i += 1;
3419 saw_const = true;
3420 },
3421 .keyword_volatile => {
3422 if (saw_volatile) {
3423 try p.warn(.extra_volatile_qualifier);
3424 }
3425 p.tok_i += 1;
3426 saw_volatile = true;
3427 },
3428 .keyword_allowzero => {
3429 if (saw_allowzero) {
3430 try p.warn(.extra_allowzero_qualifier);
3431 }
3432 p.tok_i += 1;
3433 saw_allowzero = true;
3434 },
3435 else => return result,
28653436 }
2866 const sentinel = if (p.eatToken(.Colon)) |_|
2867 try p.expectNode(parseExpr, .{
2868 .ExpectedExpr = .{ .token = p.tok_i },
2869 })
2870 else
2871 null;
2872 _ = try p.expectToken(.RBracket);
2873 const node = try p.arena.allocator.create(Node.PtrType);
2874 node.* = .{
2875 .op_token = lbracket,
2876 .rhs = undefined, // set by caller
2877 .ptr_info = .{ .sentinel = sentinel },
2878 };
2879 return &node.base;
28803437 }
2881 return null;
28823438 }
28833439
2884 /// ContainerDeclAuto <- ContainerDeclType LBRACE ContainerMembers RBRACE
2885 fn parseContainerDeclAuto(p: *Parser) !?*Node {
2886 const container_decl_type = (try p.parseContainerDeclType()) orelse return null;
2887 const lbrace = try p.expectToken(.LBrace);
2888 const members = try p.parseContainerMembers(false);
2889 defer p.gpa.free(members);
2890 const rbrace = try p.expectToken(.RBrace);
2891
2892 const members_len = @intCast(NodeIndex, members.len);
2893 const node = try Node.ContainerDecl.alloc(&p.arena.allocator, members_len);
2894 node.* = .{
2895 .layout_token = null,
2896 .kind_token = container_decl_type.kind_token,
2897 .init_arg_expr = container_decl_type.init_arg_expr,
2898 .fields_and_decls_len = members_len,
2899 .lbrace_token = lbrace,
2900 .rbrace_token = rbrace,
2901 };
2902 std.mem.copy(*Node, node.fieldsAndDecls(), members);
2903 return &node.base;
3440 /// SuffixOp
3441 /// <- LBRACKET Expr (DOT2 (Expr (COLON Expr)?)?)? RBRACKET
3442 /// / DOT IDENTIFIER
3443 /// / DOTASTERISK
3444 /// / DOTQUESTIONMARK
3445 fn parseSuffixOp(p: *Parser, lhs: Node.Index) !Node.Index {
3446 switch (p.token_tags[p.tok_i]) {
3447 .l_bracket => {
3448 const lbracket = p.nextToken();
3449 const index_expr = try p.expectExpr();
3450
3451 if (p.eatToken(.ellipsis2)) |_| {
3452 const end_expr = try p.parseExpr();
3453 if (end_expr == 0) {
3454 _ = try p.expectToken(.r_bracket);
3455 return p.addNode(.{
3456 .tag = .slice_open,
3457 .main_token = lbracket,
3458 .data = .{
3459 .lhs = lhs,
3460 .rhs = index_expr,
3461 },
3462 });
3463 }
3464 if (p.eatToken(.colon)) |_| {
3465 const sentinel = try p.parseExpr();
3466 _ = try p.expectToken(.r_bracket);
3467 return p.addNode(.{
3468 .tag = .slice_sentinel,
3469 .main_token = lbracket,
3470 .data = .{
3471 .lhs = lhs,
3472 .rhs = try p.addExtra(Node.SliceSentinel{
3473 .start = index_expr,
3474 .end = end_expr,
3475 .sentinel = sentinel,
3476 }),
3477 },
3478 });
3479 } else {
3480 _ = try p.expectToken(.r_bracket);
3481 return p.addNode(.{
3482 .tag = .slice,
3483 .main_token = lbracket,
3484 .data = .{
3485 .lhs = lhs,
3486 .rhs = try p.addExtra(Node.Slice{
3487 .start = index_expr,
3488 .end = end_expr,
3489 }),
3490 },
3491 });
3492 }
3493 }
3494 _ = try p.expectToken(.r_bracket);
3495 return p.addNode(.{
3496 .tag = .array_access,
3497 .main_token = lbracket,
3498 .data = .{
3499 .lhs = lhs,
3500 .rhs = index_expr,
3501 },
3502 });
3503 },
3504 .period_asterisk => return p.addNode(.{
3505 .tag = .deref,
3506 .main_token = p.nextToken(),
3507 .data = .{
3508 .lhs = lhs,
3509 .rhs = undefined,
3510 },
3511 }),
3512 .invalid_periodasterisks => {
3513 try p.warn(.asterisk_after_ptr_deref);
3514 return p.addNode(.{
3515 .tag = .deref,
3516 .main_token = p.nextToken(),
3517 .data = .{
3518 .lhs = lhs,
3519 .rhs = undefined,
3520 },
3521 });
3522 },
3523 .period => switch (p.token_tags[p.tok_i + 1]) {
3524 .identifier => return p.addNode(.{
3525 .tag = .field_access,
3526 .main_token = p.nextToken(),
3527 .data = .{
3528 .lhs = lhs,
3529 .rhs = p.nextToken(),
3530 },
3531 }),
3532 .question_mark => return p.addNode(.{
3533 .tag = .unwrap_optional,
3534 .main_token = p.nextToken(),
3535 .data = .{
3536 .lhs = lhs,
3537 .rhs = p.nextToken(),
3538 },
3539 }),
3540 else => {
3541 p.tok_i += 1;
3542 try p.warn(.expected_suffix_op);
3543 return null_node;
3544 },
3545 },
3546 else => return null_node,
3547 }
29043548 }
29053549
2906 /// Holds temporary data until we are ready to construct the full ContainerDecl AST node.
2907 const ContainerDeclType = struct {
2908 kind_token: TokenIndex,
2909 init_arg_expr: Node.ContainerDecl.InitArg,
2910 };
2911
3550 /// Caller must have already verified the first token.
29123551 /// ContainerDeclType
29133552 /// <- KEYWORD_struct
29143553 /// / KEYWORD_enum (LPAREN Expr RPAREN)?
29153554 /// / KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? / Expr) RPAREN)?
29163555 /// / KEYWORD_opaque
2917 fn parseContainerDeclType(p: *Parser) !?ContainerDeclType {
2918 const kind_token = p.nextToken();
2919
2920 const init_arg_expr = switch (p.token_ids[kind_token]) {
2921 .Keyword_struct, .Keyword_opaque => Node.ContainerDecl.InitArg{ .None = {} },
2922 .Keyword_enum => blk: {
2923 if (p.eatToken(.LParen) != null) {
2924 const expr = try p.expectNode(parseExpr, .{
2925 .ExpectedExpr = .{ .token = p.tok_i },
2926 });
2927 _ = try p.expectToken(.RParen);
2928 break :blk Node.ContainerDecl.InitArg{ .Type = expr };
3556 fn parseContainerDeclAuto(p: *Parser) !Node.Index {
3557 const main_token = p.nextToken();
3558 const arg_expr = switch (p.token_tags[main_token]) {
3559 .keyword_struct, .keyword_opaque => null_node,
3560 .keyword_enum => blk: {
3561 if (p.eatToken(.l_paren)) |_| {
3562 const expr = try p.expectExpr();
3563 _ = try p.expectToken(.r_paren);
3564 break :blk expr;
3565 } else {
3566 break :blk null_node;
29293567 }
2930 break :blk Node.ContainerDecl.InitArg{ .None = {} };
2931 },
2932 .Keyword_union => blk: {
2933 if (p.eatToken(.LParen) != null) {
2934 if (p.eatToken(.Keyword_enum) != null) {
2935 if (p.eatToken(.LParen) != null) {
2936 const expr = try p.expectNode(parseExpr, .{
2937 .ExpectedExpr = .{ .token = p.tok_i },
3568 },
3569 .keyword_union => blk: {
3570 if (p.eatToken(.l_paren)) |_| {
3571 if (p.eatToken(.keyword_enum)) |_| {
3572 if (p.eatToken(.l_paren)) |_| {
3573 const enum_tag_expr = try p.expectExpr();
3574 _ = try p.expectToken(.r_paren);
3575 _ = try p.expectToken(.r_paren);
3576
3577 _ = try p.expectToken(.l_brace);
3578 const members = try p.parseContainerMembers();
3579 const members_span = try members.toSpan(p);
3580 _ = try p.expectToken(.r_brace);
3581 return p.addNode(.{
3582 .tag = switch (members.trailing) {
3583 true => .tagged_union_enum_tag_trailing,
3584 false => .tagged_union_enum_tag,
3585 },
3586 .main_token = main_token,
3587 .data = .{
3588 .lhs = enum_tag_expr,
3589 .rhs = try p.addExtra(members_span),
3590 },
29383591 });
2939 _ = try p.expectToken(.RParen);
2940 _ = try p.expectToken(.RParen);
2941 break :blk Node.ContainerDecl.InitArg{ .Enum = expr };
3592 } else {
3593 _ = try p.expectToken(.r_paren);
3594
3595 _ = try p.expectToken(.l_brace);
3596 const members = try p.parseContainerMembers();
3597 _ = try p.expectToken(.r_brace);
3598 if (members.len <= 2) {
3599 return p.addNode(.{
3600 .tag = switch (members.trailing) {
3601 true => .tagged_union_two_trailing,
3602 false => .tagged_union_two,
3603 },
3604 .main_token = main_token,
3605 .data = .{
3606 .lhs = members.lhs,
3607 .rhs = members.rhs,
3608 },
3609 });
3610 } else {
3611 const span = try members.toSpan(p);
3612 return p.addNode(.{
3613 .tag = switch (members.trailing) {
3614 true => .tagged_union_trailing,
3615 false => .tagged_union,
3616 },
3617 .main_token = main_token,
3618 .data = .{
3619 .lhs = span.start,
3620 .rhs = span.end,
3621 },
3622 });
3623 }
29423624 }
2943 _ = try p.expectToken(.RParen);
2944 break :blk Node.ContainerDecl.InitArg{ .Enum = null };
3625 } else {
3626 const expr = try p.expectExpr();
3627 _ = try p.expectToken(.r_paren);
3628 break :blk expr;
29453629 }
2946 const expr = try p.expectNode(parseExpr, .{
2947 .ExpectedExpr = .{ .token = p.tok_i },
2948 });
2949 _ = try p.expectToken(.RParen);
2950 break :blk Node.ContainerDecl.InitArg{ .Type = expr };
3630 } else {
3631 break :blk null_node;
29513632 }
2952 break :blk Node.ContainerDecl.InitArg{ .None = {} };
29533633 },
29543634 else => {
2955 p.putBackToken(kind_token);
2956 return null;
3635 p.tok_i -= 1;
3636 return p.fail(.expected_container);
29573637 },
29583638 };
2959
2960 return ContainerDeclType{
2961 .kind_token = kind_token,
2962 .init_arg_expr = init_arg_expr,
2963 };
3639 _ = try p.expectToken(.l_brace);
3640 const members = try p.parseContainerMembers();
3641 _ = try p.expectToken(.r_brace);
3642 if (arg_expr == 0) {
3643 if (members.len <= 2) {
3644 return p.addNode(.{
3645 .tag = switch (members.trailing) {
3646 true => .container_decl_two_trailing,
3647 false => .container_decl_two,
3648 },
3649 .main_token = main_token,
3650 .data = .{
3651 .lhs = members.lhs,
3652 .rhs = members.rhs,
3653 },
3654 });
3655 } else {
3656 const span = try members.toSpan(p);
3657 return p.addNode(.{
3658 .tag = switch (members.trailing) {
3659 true => .container_decl_trailing,
3660 false => .container_decl,
3661 },
3662 .main_token = main_token,
3663 .data = .{
3664 .lhs = span.start,
3665 .rhs = span.end,
3666 },
3667 });
3668 }
3669 } else {
3670 const span = try members.toSpan(p);
3671 return p.addNode(.{
3672 .tag = switch (members.trailing) {
3673 true => .container_decl_arg_trailing,
3674 false => .container_decl_arg,
3675 },
3676 .main_token = main_token,
3677 .data = .{
3678 .lhs = arg_expr,
3679 .rhs = try p.addExtra(Node.SubRange{
3680 .start = span.start,
3681 .end = span.end,
3682 }),
3683 },
3684 });
3685 }
29643686 }
29653687
3688 /// Holds temporary data until we are ready to construct the full ContainerDecl AST node.
29663689 /// ByteAlign <- KEYWORD_align LPAREN Expr RPAREN
2967 fn parseByteAlign(p: *Parser) !?*Node {
2968 _ = p.eatToken(.Keyword_align) orelse return null;
2969 _ = try p.expectToken(.LParen);
2970 const expr = try p.expectNode(parseExpr, .{
2971 .ExpectedExpr = .{ .token = p.tok_i },
2972 });
2973 _ = try p.expectToken(.RParen);
3690 fn parseByteAlign(p: *Parser) !Node.Index {
3691 _ = p.eatToken(.keyword_align) orelse return null_node;
3692 _ = try p.expectToken(.l_paren);
3693 const expr = try p.expectExpr();
3694 _ = try p.expectToken(.r_paren);
29743695 return expr;
29753696 }
29763697
2977 /// IdentifierList <- (IDENTIFIER COMMA)* IDENTIFIER?
2978 /// Only ErrorSetDecl parses an IdentifierList
2979 fn parseErrorTagList(p: *Parser) ![]*Node {
2980 return ListParseFn(*Node, parseErrorTag)(p);
2981 }
2982
29833698 /// SwitchProngList <- (SwitchProng COMMA)* SwitchProng?
2984 fn parseSwitchProngList(p: *Parser) ![]*Node {
2985 return ListParseFn(*Node, parseSwitchProng)(p);
3699 fn parseSwitchProngList(p: *Parser) !Node.SubRange {
3700 return ListParseFn(parseSwitchProng)(p);
29863701 }
29873702
2988 /// AsmOutputList <- (AsmOutputItem COMMA)* AsmOutputItem?
2989 fn parseAsmOutputList(p: *Parser) Error![]Node.Asm.Output {
2990 return ListParseFn(Node.Asm.Output, parseAsmOutputItem)(p);
2991 }
3703 /// ParamDeclList <- (ParamDecl COMMA)* ParamDecl?
3704 fn parseParamDeclList(p: *Parser) !SmallSpan {
3705 _ = try p.expectToken(.l_paren);
3706 if (p.eatToken(.r_paren)) |_| {
3707 return SmallSpan{ .zero_or_one = 0 };
3708 }
3709 const param_one = while (true) {
3710 const param = try p.expectParamDecl();
3711 if (param != 0) break param;
3712 switch (p.token_tags[p.nextToken()]) {
3713 .comma => {
3714 if (p.eatToken(.r_paren)) |_| {
3715 return SmallSpan{ .zero_or_one = 0 };
3716 }
3717 continue;
3718 },
3719 .r_paren => return SmallSpan{ .zero_or_one = 0 },
3720 else => {
3721 // This is likely just a missing comma;
3722 // give an error but continue parsing this list.
3723 p.tok_i -= 1;
3724 try p.warnExpected(.comma);
3725 },
3726 }
3727 } else unreachable;
29923728
2993 /// AsmInputList <- (AsmInputItem COMMA)* AsmInputItem?
2994 fn parseAsmInputList(p: *Parser) Error![]Node.Asm.Input {
2995 return ListParseFn(Node.Asm.Input, parseAsmInputItem)(p);
2996 }
3729 const param_two = while (true) {
3730 switch (p.token_tags[p.nextToken()]) {
3731 .comma => {
3732 if (p.eatToken(.r_paren)) |_| {
3733 return SmallSpan{ .zero_or_one = param_one };
3734 }
3735 const param = try p.expectParamDecl();
3736 if (param != 0) break param;
3737 continue;
3738 },
3739 .r_paren => return SmallSpan{ .zero_or_one = param_one },
3740 .colon, .r_brace, .r_bracket => {
3741 p.tok_i -= 1;
3742 return p.failExpected(.r_paren);
3743 },
3744 else => {
3745 // This is likely just a missing comma;
3746 // give an error but continue parsing this list.
3747 p.tok_i -= 1;
3748 try p.warnExpected(.comma);
3749 },
3750 }
3751 } else unreachable;
29973752
2998 /// ParamDeclList <- (ParamDecl COMMA)* ParamDecl?
2999 fn parseParamDeclList(p: *Parser) ![]Node.FnProto.ParamDecl {
3000 return ListParseFn(Node.FnProto.ParamDecl, parseParamDecl)(p);
3753 var list = std.ArrayList(Node.Index).init(p.gpa);
3754 defer list.deinit();
3755
3756 try list.appendSlice(&.{ param_one, param_two });
3757
3758 while (true) {
3759 switch (p.token_tags[p.nextToken()]) {
3760 .comma => {
3761 if (p.token_tags[p.tok_i] == .r_paren) {
3762 p.tok_i += 1;
3763 return SmallSpan{ .multi = list.toOwnedSlice() };
3764 }
3765 const param = try p.expectParamDecl();
3766 if (param != 0) {
3767 try list.append(param);
3768 }
3769 continue;
3770 },
3771 .r_paren => return SmallSpan{ .multi = list.toOwnedSlice() },
3772 .colon, .r_brace, .r_bracket => {
3773 p.tok_i -= 1;
3774 return p.failExpected(.r_paren);
3775 },
3776 else => {
3777 // This is likely just a missing comma;
3778 // give an error but continue parsing this list.
3779 p.tok_i -= 1;
3780 try p.warnExpected(.comma);
3781 },
3782 }
3783 }
30013784 }
30023785
3003 const NodeParseFn = fn (p: *Parser) Error!?*Node;
3786 const NodeParseFn = fn (p: *Parser) Error!Node.Index;
30043787
3005 fn ListParseFn(comptime E: type, comptime nodeParseFn: anytype) ParseFn([]E) {
3788 fn ListParseFn(comptime nodeParseFn: anytype) (fn (p: *Parser) Error!Node.SubRange) {
30063789 return struct {
3007 pub fn parse(p: *Parser) ![]E {
3008 var list = std.ArrayList(E).init(p.gpa);
3790 pub fn parse(p: *Parser) Error!Node.SubRange {
3791 var list = std.ArrayList(Node.Index).init(p.gpa);
30093792 defer list.deinit();
30103793
3011 while (try nodeParseFn(p)) |item| {
3794 while (true) {
3795 const item = try nodeParseFn(p);
3796 if (item == 0) break;
3797
30123798 try list.append(item);
30133799
3014 switch (p.token_ids[p.tok_i]) {
3015 .Comma => _ = p.nextToken(),
3800 switch (p.token_tags[p.tok_i]) {
3801 .comma => p.tok_i += 1,
30163802 // all possible delimiters
3017 .Colon, .RParen, .RBrace, .RBracket => break,
3803 .colon, .r_paren, .r_brace, .r_bracket => break,
30183804 else => {
3019 // this is likely just a missing comma,
3020 // continue parsing this list and give an error
3021 try p.errors.append(p.gpa, .{
3022 .ExpectedToken = .{ .token = p.tok_i, .expected_id = .Comma },
3023 });
3805 // This is likely just a missing comma;
3806 // give an error but continue parsing this list.
3807 try p.warnExpected(.comma);
30243808 },
30253809 }
30263810 }
3027 return list.toOwnedSlice();
3028 }
3029 }.parse;
3030 }
3031
3032 fn SimpleBinOpParseFn(comptime token: Token.Id, comptime op: Node.Tag) NodeParseFn {
3033 return struct {
3034 pub fn parse(p: *Parser) Error!?*Node {
3035 const op_token = if (token == .Keyword_and) switch (p.token_ids[p.tok_i]) {
3036 .Keyword_and => p.nextToken(),
3037 .Invalid_ampersands => blk: {
3038 try p.errors.append(p.gpa, .{
3039 .InvalidAnd = .{ .token = p.tok_i },
3040 });
3041 break :blk p.nextToken();
3042 },
3043 else => return null,
3044 } else p.eatToken(token) orelse return null;
3045
3046 const node = try p.arena.allocator.create(Node.SimpleInfixOp);
3047 node.* = .{
3048 .base = .{ .tag = op },
3049 .op_token = op_token,
3050 .lhs = undefined, // set by caller
3051 .rhs = undefined, // set by caller
3052 };
3053 return &node.base;
3811 return p.listToSpan(list.items);
30543812 }
30553813 }.parse;
30563814 }
30573815
3058 // Helper parsers not included in the grammar
3059
3060 fn parseBuiltinCall(p: *Parser) !?*Node {
3061 const token = p.eatToken(.Builtin) orelse return null;
3062 const params = (try p.parseFnCallArguments()) orelse {
3063 try p.errors.append(p.gpa, .{
3064 .ExpectedParamList = .{ .token = p.tok_i },
3816 /// FnCallArguments <- LPAREN ExprList RPAREN
3817 /// ExprList <- (Expr COMMA)* Expr?
3818 fn parseBuiltinCall(p: *Parser) !Node.Index {
3819 const builtin_token = p.assertToken(.builtin);
3820 if (p.token_tags[p.nextToken()] != .l_paren) {
3821 p.tok_i -= 1;
3822 try p.warn(.expected_param_list);
3823 // Pretend this was an identifier so we can continue parsing.
3824 return p.addNode(.{
3825 .tag = .identifier,
3826 .main_token = builtin_token,
3827 .data = .{
3828 .lhs = undefined,
3829 .rhs = undefined,
3830 },
30653831 });
3832 }
3833 if (p.eatToken(.r_paren)) |_| {
3834 return p.addNode(.{
3835 .tag = .builtin_call_two,
3836 .main_token = builtin_token,
3837 .data = .{
3838 .lhs = 0,
3839 .rhs = 0,
3840 },
3841 });
3842 }
3843 const param_one = try p.expectExpr();
3844 switch (p.token_tags[p.nextToken()]) {
3845 .comma => {
3846 if (p.eatToken(.r_paren)) |_| {
3847 return p.addNode(.{
3848 .tag = .builtin_call_two_comma,
3849 .main_token = builtin_token,
3850 .data = .{
3851 .lhs = param_one,
3852 .rhs = 0,
3853 },
3854 });
3855 }
3856 },
3857 .r_paren => return p.addNode(.{
3858 .tag = .builtin_call_two,
3859 .main_token = builtin_token,
3860 .data = .{
3861 .lhs = param_one,
3862 .rhs = 0,
3863 },
3864 }),
3865 else => {
3866 // This is likely just a missing comma;
3867 // give an error but continue parsing this list.
3868 p.tok_i -= 1;
3869 try p.warnExpected(.comma);
3870 },
3871 }
3872 const param_two = try p.expectExpr();
3873 switch (p.token_tags[p.nextToken()]) {
3874 .comma => {
3875 if (p.eatToken(.r_paren)) |_| {
3876 return p.addNode(.{
3877 .tag = .builtin_call_two_comma,
3878 .main_token = builtin_token,
3879 .data = .{
3880 .lhs = param_one,
3881 .rhs = param_two,
3882 },
3883 });
3884 }
3885 },
3886 .r_paren => return p.addNode(.{
3887 .tag = .builtin_call_two,
3888 .main_token = builtin_token,
3889 .data = .{
3890 .lhs = param_one,
3891 .rhs = param_two,
3892 },
3893 }),
3894 else => {
3895 // This is likely just a missing comma;
3896 // give an error but continue parsing this list.
3897 p.tok_i -= 1;
3898 try p.warnExpected(.comma);
3899 },
3900 }
30663901
3067 // lets pretend this was an identifier so we can continue parsing
3068 const node = try p.arena.allocator.create(Node.OneToken);
3069 node.* = .{
3070 .base = .{ .tag = .Identifier },
3071 .token = token,
3072 };
3073 return &node.base;
3074 };
3075 defer p.gpa.free(params.list);
3076
3077 const node = try Node.BuiltinCall.alloc(&p.arena.allocator, params.list.len);
3078 node.* = .{
3079 .builtin_token = token,
3080 .params_len = params.list.len,
3081 .rparen_token = params.rparen,
3082 };
3083 std.mem.copy(*Node, node.params(), params.list);
3084 return &node.base;
3085 }
3086
3087 fn parseErrorTag(p: *Parser) !?*Node {
3088 const doc_comments = try p.parseDocComment(); // no need to rewind on failure
3089 const token = p.eatToken(.Identifier) orelse return null;
3090
3091 const node = try p.arena.allocator.create(Node.ErrorTag);
3092 node.* = .{
3093 .doc_comments = doc_comments,
3094 .name_token = token,
3095 };
3096 return &node.base;
3097 }
3098
3099 fn parseIdentifier(p: *Parser) !?*Node {
3100 const token = p.eatToken(.Identifier) orelse return null;
3101 const node = try p.arena.allocator.create(Node.OneToken);
3102 node.* = .{
3103 .base = .{ .tag = .Identifier },
3104 .token = token,
3105 };
3106 return &node.base;
3107 }
3108
3109 fn parseAnyType(p: *Parser) !?*Node {
3110 const token = p.eatToken(.Keyword_anytype) orelse
3111 p.eatToken(.Keyword_var) orelse return null; // TODO remove in next release cycle
3112 const node = try p.arena.allocator.create(Node.OneToken);
3113 node.* = .{
3114 .base = .{ .tag = .AnyType },
3115 .token = token,
3116 };
3117 return &node.base;
3118 }
3902 var list = std.ArrayList(Node.Index).init(p.gpa);
3903 defer list.deinit();
31193904
3120 fn createLiteral(p: *Parser, tag: ast.Node.Tag, token: TokenIndex) !*Node {
3121 const result = try p.arena.allocator.create(Node.OneToken);
3122 result.* = .{
3123 .base = .{ .tag = tag },
3124 .token = token,
3125 };
3126 return &result.base;
3127 }
3905 try list.appendSlice(&.{ param_one, param_two });
31283906
3129 fn parseStringLiteralSingle(p: *Parser) !?*Node {
3130 if (p.eatToken(.StringLiteral)) |token| {
3131 const node = try p.arena.allocator.create(Node.OneToken);
3132 node.* = .{
3133 .base = .{ .tag = .StringLiteral },
3134 .token = token,
3135 };
3136 return &node.base;
3907 while (true) {
3908 const param = try p.expectExpr();
3909 try list.append(param);
3910 switch (p.token_tags[p.nextToken()]) {
3911 .comma => {
3912 if (p.eatToken(.r_paren)) |_| {
3913 const params = try p.listToSpan(list.items);
3914 return p.addNode(.{
3915 .tag = .builtin_call_comma,
3916 .main_token = builtin_token,
3917 .data = .{
3918 .lhs = params.start,
3919 .rhs = params.end,
3920 },
3921 });
3922 }
3923 continue;
3924 },
3925 .r_paren => {
3926 const params = try p.listToSpan(list.items);
3927 return p.addNode(.{
3928 .tag = .builtin_call,
3929 .main_token = builtin_token,
3930 .data = .{
3931 .lhs = params.start,
3932 .rhs = params.end,
3933 },
3934 });
3935 },
3936 else => {
3937 // This is likely just a missing comma;
3938 // give an error but continue parsing this list.
3939 p.tok_i -= 1;
3940 try p.warnExpected(.comma);
3941 },
3942 }
31373943 }
3138 return null;
31393944 }
31403945
31413946 // string literal or multiline string literal
3142 fn parseStringLiteral(p: *Parser) !?*Node {
3143 if (try p.parseStringLiteralSingle()) |node| return node;
3144
3145 if (p.eatToken(.MultilineStringLiteralLine)) |first_line| {
3146 const start_tok_i = p.tok_i;
3147 var tok_i = start_tok_i;
3148 var count: usize = 1; // including first_line
3149 while (true) : (tok_i += 1) {
3150 switch (p.token_ids[tok_i]) {
3151 .LineComment => continue,
3152 .MultilineStringLiteralLine => count += 1,
3153 else => break,
3154 }
3155 }
3156
3157 const node = try Node.MultilineStringLiteral.alloc(&p.arena.allocator, count);
3158 node.* = .{ .lines_len = count };
3159 const lines = node.lines();
3160 tok_i = start_tok_i;
3161 lines[0] = first_line;
3162 count = 1;
3163 while (true) : (tok_i += 1) {
3164 switch (p.token_ids[tok_i]) {
3165 .LineComment => continue,
3166 .MultilineStringLiteralLine => {
3167 lines[count] = tok_i;
3168 count += 1;
3947 fn parseStringLiteral(p: *Parser) !Node.Index {
3948 switch (p.token_tags[p.tok_i]) {
3949 .string_literal => {
3950 const main_token = p.nextToken();
3951 return p.addNode(.{
3952 .tag = .string_literal,
3953 .main_token = main_token,
3954 .data = .{
3955 .lhs = undefined,
3956 .rhs = undefined,
31693957 },
3170 else => break,
3958 });
3959 },
3960 .multiline_string_literal_line => {
3961 const first_line = p.nextToken();
3962 while (p.token_tags[p.tok_i] == .multiline_string_literal_line) {
3963 p.tok_i += 1;
31713964 }
3172 }
3173 p.tok_i = tok_i;
3174 return &node.base;
3965 return p.addNode(.{
3966 .tag = .multiline_string_literal,
3967 .main_token = first_line,
3968 .data = .{
3969 .lhs = first_line,
3970 .rhs = p.tok_i - 1,
3971 },
3972 });
3973 },
3974 else => return null_node,
31753975 }
3176
3177 return null;
31783976 }
31793977
3180 fn parseIntegerLiteral(p: *Parser) !?*Node {
3181 const token = p.eatToken(.IntegerLiteral) orelse return null;
3182 const node = try p.arena.allocator.create(Node.OneToken);
3183 node.* = .{
3184 .base = .{ .tag = .IntegerLiteral },
3185 .token = token,
3186 };
3187 return &node.base;
3978 fn expectStringLiteral(p: *Parser) !Node.Index {
3979 const node = try p.parseStringLiteral();
3980 if (node == 0) {
3981 return p.fail(.expected_string_literal);
3982 }
3983 return node;
31883984 }
31893985
3190 fn parseFloatLiteral(p: *Parser) !?*Node {
3191 const token = p.eatToken(.FloatLiteral) orelse return null;
3192 const node = try p.arena.allocator.create(Node.OneToken);
3193 node.* = .{
3194 .base = .{ .tag = .FloatLiteral },
3195 .token = token,
3196 };
3197 return &node.base;
3986 fn expectIntegerLiteral(p: *Parser) !Node.Index {
3987 return p.addNode(.{
3988 .tag = .integer_literal,
3989 .main_token = try p.expectToken(.integer_literal),
3990 .data = .{
3991 .lhs = undefined,
3992 .rhs = undefined,
3993 },
3994 });
31983995 }
31993996
3200 fn parseTry(p: *Parser) !?*Node {
3201 const token = p.eatToken(.Keyword_try) orelse return null;
3202 const node = try p.arena.allocator.create(Node.SimplePrefixOp);
3203 node.* = .{
3204 .base = .{ .tag = .Try },
3205 .op_token = token,
3206 .rhs = undefined, // set by caller
3207 };
3208 return &node.base;
3209 }
3997 /// KEYWORD_if LPAREN Expr RPAREN PtrPayload? Body (KEYWORD_else Payload? Body)?
3998 fn parseIf(p: *Parser, bodyParseFn: NodeParseFn) !Node.Index {
3999 const if_token = p.eatToken(.keyword_if) orelse return null_node;
4000 _ = try p.expectToken(.l_paren);
4001 const condition = try p.expectExpr();
4002 _ = try p.expectToken(.r_paren);
4003 const then_payload = try p.parsePtrPayload();
32104004
3211 /// IfPrefix Body (KEYWORD_else Payload? Body)?
3212 fn parseIf(p: *Parser, bodyParseFn: NodeParseFn) !?*Node {
3213 const node = (try p.parseIfPrefix()) orelse return null;
3214 const if_prefix = node.cast(Node.If).?;
4005 const then_expr = try bodyParseFn(p);
4006 if (then_expr == 0) return p.fail(.invalid_token);
32154007
3216 if_prefix.body = try p.expectNode(bodyParseFn, .{
3217 .InvalidToken = .{ .token = p.tok_i },
4008 const else_token = p.eatToken(.keyword_else) orelse return p.addNode(.{
4009 .tag = .if_simple,
4010 .main_token = if_token,
4011 .data = .{
4012 .lhs = condition,
4013 .rhs = then_expr,
4014 },
32184015 });
3219
3220 const else_token = p.eatToken(.Keyword_else) orelse return node;
3221 const payload = try p.parsePayload();
3222 const else_expr = try p.expectNode(bodyParseFn, .{
3223 .InvalidToken = .{ .token = p.tok_i },
4016 const else_payload = try p.parsePayload();
4017 const else_expr = try bodyParseFn(p);
4018 if (else_expr == 0) return p.fail(.invalid_token);
4019
4020 return p.addNode(.{
4021 .tag = .@"if",
4022 .main_token = if_token,
4023 .data = .{
4024 .lhs = condition,
4025 .rhs = try p.addExtra(Node.If{
4026 .then_expr = then_expr,
4027 .else_expr = else_expr,
4028 }),
4029 },
32244030 });
3225 const else_node = try p.arena.allocator.create(Node.Else);
3226 else_node.* = .{
3227 .else_token = else_token,
3228 .payload = payload,
3229 .body = else_expr,
3230 };
3231 if_prefix.@"else" = else_node;
3232
3233 return node;
32344031 }
32354032
3236 /// Eat a multiline doc comment
3237 fn parseDocComment(p: *Parser) !?*Node.DocComment {
3238 if (p.eatToken(.DocComment)) |first_line| {
3239 while (p.eatToken(.DocComment)) |_| {}
3240 const node = try p.arena.allocator.create(Node.DocComment);
3241 node.* = .{ .first_line = first_line };
3242 return node;
4033 /// Skips over doc comment tokens. Returns the first one, if any.
4034 fn eatDocComments(p: *Parser) !?TokenIndex {
4035 if (p.eatToken(.doc_comment)) |tok| {
4036 var first_line = tok;
4037 if (tok > 0 and tokensOnSameLine(p, tok - 1, tok)) {
4038 try p.warnMsg(.{
4039 .tag = .same_line_doc_comment,
4040 .token = tok,
4041 });
4042 first_line = p.eatToken(.doc_comment) orelse return null;
4043 }
4044 while (p.eatToken(.doc_comment)) |_| {}
4045 return first_line;
32434046 }
32444047 return null;
32454048 }
32464049
32474050 fn tokensOnSameLine(p: *Parser, token1: TokenIndex, token2: TokenIndex) bool {
3248 return std.mem.indexOfScalar(u8, p.source[p.token_locs[token1].end..p.token_locs[token2].start], '\n') == null;
4051 return std.mem.indexOfScalar(u8, p.source[p.token_starts[token1]..p.token_starts[token2]], '\n') == null;
32494052 }
32504053
3251 /// Eat a single-line doc comment on the same line as another node
3252 fn parseAppendedDocComment(p: *Parser, after_token: TokenIndex) !?*Node.DocComment {
3253 const comment_token = p.eatToken(.DocComment) orelse return null;
3254 if (p.tokensOnSameLine(after_token, comment_token)) {
3255 const node = try p.arena.allocator.create(Node.DocComment);
3256 node.* = .{ .first_line = comment_token };
3257 return node;
3258 }
3259 p.putBackToken(comment_token);
3260 return null;
4054 fn eatToken(p: *Parser, tag: Token.Tag) ?TokenIndex {
4055 return if (p.token_tags[p.tok_i] == tag) p.nextToken() else null;
32614056 }
32624057
3263 /// Op* Child
3264 fn parsePrefixOpExpr(p: *Parser, comptime opParseFn: NodeParseFn, comptime childParseFn: NodeParseFn) Error!?*Node {
3265 if (try opParseFn(p)) |first_op| {
3266 var rightmost_op = first_op;
3267 while (true) {
3268 switch (rightmost_op.tag) {
3269 .AddressOf,
3270 .Await,
3271 .BitNot,
3272 .BoolNot,
3273 .OptionalType,
3274 .Negation,
3275 .NegationWrap,
3276 .Resume,
3277 .Try,
3278 => {
3279 if (try opParseFn(p)) |rhs| {
3280 rightmost_op.cast(Node.SimplePrefixOp).?.rhs = rhs;
3281 rightmost_op = rhs;
3282 } else break;
3283 },
3284 .ArrayType => {
3285 if (try opParseFn(p)) |rhs| {
3286 rightmost_op.cast(Node.ArrayType).?.rhs = rhs;
3287 rightmost_op = rhs;
3288 } else break;
3289 },
3290 .ArrayTypeSentinel => {
3291 if (try opParseFn(p)) |rhs| {
3292 rightmost_op.cast(Node.ArrayTypeSentinel).?.rhs = rhs;
3293 rightmost_op = rhs;
3294 } else break;
3295 },
3296 .SliceType => {
3297 if (try opParseFn(p)) |rhs| {
3298 rightmost_op.cast(Node.SliceType).?.rhs = rhs;
3299 rightmost_op = rhs;
3300 } else break;
3301 },
3302 .PtrType => {
3303 var ptr_type = rightmost_op.cast(Node.PtrType).?;
3304 // If the token encountered was **, there will be two nodes
3305 if (p.token_ids[ptr_type.op_token] == .AsteriskAsterisk) {
3306 rightmost_op = ptr_type.rhs;
3307 ptr_type = rightmost_op.cast(Node.PtrType).?;
3308 }
3309 if (try opParseFn(p)) |rhs| {
3310 ptr_type.rhs = rhs;
3311 rightmost_op = rhs;
3312 } else break;
3313 },
3314 .AnyFrameType => {
3315 const prom = rightmost_op.cast(Node.AnyFrameType).?;
3316 if (try opParseFn(p)) |rhs| {
3317 prom.result.?.return_type = rhs;
3318 rightmost_op = rhs;
3319 } else break;
3320 },
3321 else => unreachable,
3322 }
3323 }
3324
3325 // If any prefix op existed, a child node on the RHS is required
3326 switch (rightmost_op.tag) {
3327 .AddressOf,
3328 .Await,
3329 .BitNot,
3330 .BoolNot,
3331 .OptionalType,
3332 .Negation,
3333 .NegationWrap,
3334 .Resume,
3335 .Try,
3336 => {
3337 const prefix_op = rightmost_op.cast(Node.SimplePrefixOp).?;
3338 prefix_op.rhs = try p.expectNode(childParseFn, .{
3339 .InvalidToken = .{ .token = p.tok_i },
3340 });
3341 },
3342 .ArrayType => {
3343 const prefix_op = rightmost_op.cast(Node.ArrayType).?;
3344 prefix_op.rhs = try p.expectNode(childParseFn, .{
3345 .InvalidToken = .{ .token = p.tok_i },
3346 });
3347 },
3348 .ArrayTypeSentinel => {
3349 const prefix_op = rightmost_op.cast(Node.ArrayTypeSentinel).?;
3350 prefix_op.rhs = try p.expectNode(childParseFn, .{
3351 .InvalidToken = .{ .token = p.tok_i },
3352 });
3353 },
3354 .PtrType => {
3355 const prefix_op = rightmost_op.cast(Node.PtrType).?;
3356 prefix_op.rhs = try p.expectNode(childParseFn, .{
3357 .InvalidToken = .{ .token = p.tok_i },
3358 });
3359 },
3360 .SliceType => {
3361 const prefix_op = rightmost_op.cast(Node.SliceType).?;
3362 prefix_op.rhs = try p.expectNode(childParseFn, .{
3363 .InvalidToken = .{ .token = p.tok_i },
3364 });
3365 },
3366 .AnyFrameType => {
3367 const prom = rightmost_op.cast(Node.AnyFrameType).?;
3368 prom.result.?.return_type = try p.expectNode(childParseFn, .{
3369 .InvalidToken = .{ .token = p.tok_i },
3370 });
3371 },
3372 else => unreachable,
3373 }
3374
3375 return first_op;
3376 }
3377
3378 // Otherwise, the child node is optional
3379 return childParseFn(p);
4058 fn assertToken(p: *Parser, tag: Token.Tag) TokenIndex {
4059 const token = p.nextToken();
4060 assert(p.token_tags[token] == tag);
4061 return token;
33804062 }
33814063
3382 /// Child (Op Child)*
3383 /// Child (Op Child)?
3384 fn parseBinOpExpr(
3385 p: *Parser,
3386 opParseFn: NodeParseFn,
3387 childParseFn: NodeParseFn,
3388 chain: enum {
3389 Once,
3390 Infinitely,
3391 },
3392 ) Error!?*Node {
3393 var res = (try childParseFn(p)) orelse return null;
3394
3395 while (try opParseFn(p)) |node| {
3396 const right = try p.expectNode(childParseFn, .{
3397 .InvalidToken = .{ .token = p.tok_i },
4064 fn expectToken(p: *Parser, tag: Token.Tag) Error!TokenIndex {
4065 const token = p.nextToken();
4066 if (p.token_tags[token] != tag) {
4067 p.tok_i -= 1; // Go back so that we can recover properly.
4068 return p.failMsg(.{
4069 .tag = .expected_token,
4070 .token = token,
4071 .extra = .{ .expected_tag = tag },
33984072 });
3399 const left = res;
3400 res = node;
3401
3402 if (node.castTag(.Catch)) |op| {
3403 op.lhs = left;
3404 op.rhs = right;
3405 } else if (node.cast(Node.SimpleInfixOp)) |op| {
3406 op.lhs = left;
3407 op.rhs = right;
3408 }
3409
3410 switch (chain) {
3411 .Once => break,
3412 .Infinitely => continue,
3413 }
34144073 }
3415
3416 return res;
3417 }
3418
3419 fn createInfixOp(p: *Parser, op_token: TokenIndex, tag: Node.Tag) !*Node {
3420 const node = try p.arena.allocator.create(Node.SimpleInfixOp);
3421 node.* = .{
3422 .base = Node{ .tag = tag },
3423 .op_token = op_token,
3424 .lhs = undefined, // set by caller
3425 .rhs = undefined, // set by caller
3426 };
3427 return &node.base;
3428 }
3429
3430 fn eatToken(p: *Parser, id: Token.Id) ?TokenIndex {
3431 return if (p.token_ids[p.tok_i] == id) p.nextToken() else null;
3432 }
3433
3434 fn expectToken(p: *Parser, id: Token.Id) Error!TokenIndex {
3435 return (try p.expectTokenRecoverable(id)) orelse error.ParseError;
4074 return token;
34364075 }
34374076
3438 fn expectTokenRecoverable(p: *Parser, id: Token.Id) !?TokenIndex {
3439 const token = p.nextToken();
3440 if (p.token_ids[token] != id) {
3441 try p.errors.append(p.gpa, .{
3442 .ExpectedToken = .{ .token = token, .expected_id = id },
3443 });
3444 // go back so that we can recover properly
3445 p.putBackToken(token);
4077 fn expectTokenRecoverable(p: *Parser, tag: Token.Tag) !?TokenIndex {
4078 if (p.token_tags[p.tok_i] != tag) {
4079 try p.warnExpected(tag);
34464080 return null;
4081 } else {
4082 return p.nextToken();
34474083 }
3448 return token;
34494084 }
34504085
34514086 fn nextToken(p: *Parser) TokenIndex {
34524087 const result = p.tok_i;
34534088 p.tok_i += 1;
3454 assert(p.token_ids[result] != .LineComment);
3455 if (p.tok_i >= p.token_ids.len) return result;
3456
3457 while (true) {
3458 if (p.token_ids[p.tok_i] != .LineComment) return result;
3459 p.tok_i += 1;
3460 }
3461 }
3462
3463 fn putBackToken(p: *Parser, putting_back: TokenIndex) void {
3464 while (p.tok_i > 0) {
3465 p.tok_i -= 1;
3466 if (p.token_ids[p.tok_i] == .LineComment) continue;
3467 assert(putting_back == p.tok_i);
3468 return;
3469 }
3470 }
3471
3472 /// TODO Delete this function. I don't like the inversion of control.
3473 fn expectNode(
3474 p: *Parser,
3475 parseFn: NodeParseFn,
3476 /// if parsing fails
3477 err: AstError,
3478 ) Error!*Node {
3479 return (try p.expectNodeRecoverable(parseFn, err)) orelse return error.ParseError;
3480 }
3481
3482 /// TODO Delete this function. I don't like the inversion of control.
3483 fn expectNodeRecoverable(
3484 p: *Parser,
3485 parseFn: NodeParseFn,
3486 /// if parsing fails
3487 err: AstError,
3488 ) !?*Node {
3489 return (try parseFn(p)) orelse {
3490 try p.errors.append(p.gpa, err);
3491 return null;
3492 };
4089 return result;
34934090 }
34944091};
34954092
3496fn ParseFn(comptime T: type) type {
3497 return fn (p: *Parser) Error!T;
3498}
3499
3500test "std.zig.parser" {
4093test {
35014094 _ = @import("parser_test.zig");
35024095}
lib/std/zig/parser_test.zig+1753-949
......@@ -3,275 +3,34 @@
33// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
44// The MIT license requires this copyright notice to be included in all copies
55// and substantial portions of the software.
6test "zig fmt: convert var to anytype" {
7 // TODO remove in next release cycle
6
7// TODO Remove this after zig 0.9.0 is released.
8test "zig fmt: rewrite inline functions as callconv(.Inline)" {
89 try testTransform(
9 \\pub fn main(
10 \\ a: var,
11 \\ bar: var,
12 \\) void {}
10 \\inline fn foo() void {}
11 \\
1312 ,
14 \\pub fn main(
15 \\ a: anytype,
16 \\ bar: anytype,
17 \\) void {}
13 \\fn foo() callconv(.Inline) void {}
1814 \\
1915 );
2016}
2117
22test "zig fmt: noasync to nosuspend" {
23 // TODO: remove this
24 try testTransform(
25 \\pub fn main() void {
26 \\ noasync call();
27 \\}
28 ,
29 \\pub fn main() void {
30 \\ nosuspend call();
31 \\}
18test "zig fmt: simple top level comptime block" {
19 try testCanonical(
20 \\// line comment
21 \\comptime {}
3222 \\
3323 );
3424}
3525
36test "recovery: top level" {
37 try testError(
38 \\test "" {inline}
39 \\test "" {inline}
40 , &[_]Error{
41 .ExpectedInlinable,
42 .ExpectedInlinable,
43 });
44}
45
46test "recovery: block statements" {
47 try testError(
48 \\test "" {
49 \\ foo + +;
50 \\ inline;
51 \\}
52 , &[_]Error{
53 .InvalidToken,
54 .ExpectedInlinable,
55 });
56}
57
58test "recovery: missing comma" {
59 try testError(
60 \\test "" {
61 \\ switch (foo) {
62 \\ 2 => {}
63 \\ 3 => {}
64 \\ else => {
65 \\ foo && bar +;
66 \\ }
67 \\ }
68 \\}
69 , &[_]Error{
70 .ExpectedToken,
71 .ExpectedToken,
72 .InvalidAnd,
73 .InvalidToken,
74 });
75}
76
77test "recovery: extra qualifier" {
78 try testError(
79 \\const a: *const const u8;
80 \\test ""
81 , &[_]Error{
82 .ExtraConstQualifier,
83 .ExpectedLBrace,
84 });
85}
86
87test "recovery: missing return type" {
88 try testError(
89 \\fn foo() {
90 \\ a && b;
91 \\}
92 \\test ""
93 , &[_]Error{
94 .ExpectedReturnType,
95 .InvalidAnd,
96 .ExpectedLBrace,
97 });
98}
99
100test "recovery: continue after invalid decl" {
101 try testError(
102 \\fn foo {
103 \\ inline;
104 \\}
105 \\pub test "" {
106 \\ async a && b;
107 \\}
108 , &[_]Error{
109 .ExpectedToken,
110 .ExpectedPubItem,
111 .ExpectedParamList,
112 .InvalidAnd,
113 });
114 try testError(
115 \\threadlocal test "" {
116 \\ @a && b;
117 \\}
118 , &[_]Error{
119 .ExpectedVarDecl,
120 .ExpectedParamList,
121 .InvalidAnd,
122 });
123}
124
125test "recovery: invalid extern/inline" {
126 try testError(
127 \\inline test "" { a && b; }
128 , &[_]Error{
129 .ExpectedFn,
130 .InvalidAnd,
131 });
132 try testError(
133 \\extern "" test "" { a && b; }
134 , &[_]Error{
135 .ExpectedVarDeclOrFn,
136 .InvalidAnd,
137 });
138}
139
140test "recovery: missing semicolon" {
141 try testError(
142 \\test "" {
143 \\ comptime a && b
144 \\ c && d
145 \\ @foo
146 \\}
147 , &[_]Error{
148 .InvalidAnd,
149 .ExpectedToken,
150 .InvalidAnd,
151 .ExpectedToken,
152 .ExpectedParamList,
153 .ExpectedToken,
154 });
155}
156
157test "recovery: invalid container members" {
158 try testError(
159 \\usingnamespace;
160 \\foo+
161 \\bar@,
162 \\while (a == 2) { test "" {}}
163 \\test "" {
164 \\ a && b
165 \\}
166 , &[_]Error{
167 .ExpectedExpr,
168 .ExpectedToken,
169 .ExpectedToken,
170 .ExpectedContainerMembers,
171 .InvalidAnd,
172 .ExpectedToken,
173 });
174}
175
176test "recovery: invalid parameter" {
177 try testError(
178 \\fn main() void {
179 \\ a(comptime T: type)
180 \\}
181 , &[_]Error{
182 .ExpectedToken,
183 });
184}
185
186test "recovery: extra '}' at top level" {
187 try testError(
188 \\}}}
189 \\test "" {
190 \\ a && b;
191 \\}
192 , &[_]Error{
193 .ExpectedContainerMembers,
194 .ExpectedContainerMembers,
195 .ExpectedContainerMembers,
196 .InvalidAnd,
197 });
198}
199
200test "recovery: mismatched bracket at top level" {
201 try testError(
202 \\const S = struct {
203 \\ arr: 128]?G
204 \\};
205 , &[_]Error{
206 .ExpectedToken,
207 });
208}
209
210test "recovery: invalid global error set access" {
211 try testError(
212 \\test "" {
213 \\ error && foo;
214 \\}
215 , &[_]Error{
216 .ExpectedToken,
217 .ExpectedIdentifier,
218 .InvalidAnd,
219 });
220}
221
222test "recovery: invalid asterisk after pointer dereference" {
223 try testError(
224 \\test "" {
225 \\ var sequence = "repeat".*** 10;
226 \\}
227 , &[_]Error{
228 .AsteriskAfterPointerDereference,
229 });
230 try testError(
231 \\test "" {
232 \\ var sequence = "repeat".** 10&&a;
233 \\}
234 , &[_]Error{
235 .AsteriskAfterPointerDereference,
236 .InvalidAnd,
237 });
238}
239
240test "recovery: missing semicolon after if, for, while stmt" {
241 try testError(
242 \\test "" {
243 \\ if (foo) bar
244 \\ for (foo) |a| bar
245 \\ while (foo) bar
246 \\ a && b;
247 \\}
248 , &[_]Error{
249 .ExpectedSemiOrElse,
250 .ExpectedSemiOrElse,
251 .ExpectedSemiOrElse,
252 .InvalidAnd,
253 });
254}
255
256test "recovery: invalid comptime" {
257 try testError(
258 \\comptime
259 , &[_]Error{
260 .ExpectedBlockOrField,
261 });
262}
263
264test "recovery: missing block after for/while loops" {
265 try testError(
266 \\test "" { while (foo) }
267 , &[_]Error{
268 .ExpectedBlockOrAssignment,
269 });
270 try testError(
271 \\test "" { for (foo) |bar| }
272 , &[_]Error{
273 .ExpectedBlockOrAssignment,
274 });
26test "zig fmt: two spaced line comments before decl" {
27 try testCanonical(
28 \\// line comment
29 \\
30 \\// another
31 \\comptime {}
32 \\
33 );
27534}
27635
27736test "zig fmt: respect line breaks after var declarations" {
......@@ -325,6 +84,35 @@ test "zig fmt: empty file" {
32584 );
32685}
32786
87test "zig fmt: file ends in comment" {
88 try testTransform(
89 \\ //foobar
90 ,
91 \\//foobar
92 \\
93 );
94}
95
96test "zig fmt: file ends in comment after var decl" {
97 try testTransform(
98 \\const x = 42;
99 \\ //foobar
100 ,
101 \\const x = 42;
102 \\//foobar
103 \\
104 );
105}
106
107test "zig fmt: doc comments on test" {
108 try testCanonical(
109 \\/// hello
110 \\/// world
111 \\test "" {}
112 \\
113 );
114}
115
328116test "zig fmt: if statment" {
329117 try testCanonical(
330118 \\test "" {
......@@ -357,7 +145,7 @@ test "zig fmt: decl between fields" {
357145 \\ b: usize,
358146 \\};
359147 , &[_]Error{
360 .DeclBetweenFields,
148 .decl_between_fields,
361149 });
362150}
363151
......@@ -365,7 +153,7 @@ test "zig fmt: eof after missing comma" {
365153 try testError(
366154 \\foo()
367155 , &[_]Error{
368 .ExpectedToken,
156 .expected_token,
369157 });
370158}
371159
......@@ -402,7 +190,7 @@ test "zig fmt: nosuspend await" {
402190 );
403191}
404192
405test "zig fmt: trailing comma in container declaration" {
193test "zig fmt: container declaration, single line" {
406194 try testCanonical(
407195 \\const X = struct { foo: i32 };
408196 \\const X = struct { foo: i32, bar: i32 };
......@@ -411,7 +199,23 @@ test "zig fmt: trailing comma in container declaration" {
411199 \\const X = struct { foo: i32 align(4) = 1, bar: i32 align(4) = 2 };
412200 \\
413201 );
202}
203
204test "zig fmt: container declaration, one item, multi line trailing comma" {
414205 try testCanonical(
206 \\test "" {
207 \\ comptime {
208 \\ const X = struct {
209 \\ x: i32,
210 \\ };
211 \\ }
212 \\}
213 \\
214 );
215}
216
217test "zig fmt: container declaration, no trailing comma on separate line" {
218 try testTransform(
415219 \\test "" {
416220 \\ comptime {
417221 \\ const X = struct {
......@@ -420,30 +224,113 @@ test "zig fmt: trailing comma in container declaration" {
420224 \\ }
421225 \\}
422226 \\
227 ,
228 \\test "" {
229 \\ comptime {
230 \\ const X = struct { x: i32 };
231 \\ }
232 \\}
233 \\
423234 );
235}
236
237test "zig fmt: container declaration, line break, no trailing comma" {
424238 try testTransform(
425239 \\const X = struct {
426240 \\ foo: i32, bar: i8 };
241 ,
242 \\const X = struct { foo: i32, bar: i8 };
243 \\
244 );
245}
246
247test "zig fmt: container declaration, transform trailing comma" {
248 try testTransform(
249 \\const X = struct {
250 \\ foo: i32, bar: i8, };
427251 ,
428252 \\const X = struct {
429 \\ foo: i32, bar: i8
253 \\ foo: i32,
254 \\ bar: i8,
430255 \\};
431256 \\
432257 );
433258}
434259
435test "zig fmt: trailing comma in fn parameter list" {
436 try testCanonical(
437 \\pub fn f(
438 \\ a: i32,
439 \\ b: i32,
440 \\) i32 {}
441 \\pub fn f(
442 \\ a: i32,
443 \\ b: i32,
444 \\) align(8) i32 {}
445 \\pub fn f(
446 \\ a: i32,
260test "zig fmt: remove empty lines at start/end of container decl" {
261 try testTransform(
262 \\const X = struct {
263 \\
264 \\ foo: i32,
265 \\
266 \\ bar: i8,
267 \\
268 \\};
269 \\
270 ,
271 \\const X = struct {
272 \\ foo: i32,
273 \\
274 \\ bar: i8,
275 \\};
276 \\
277 );
278}
279
280test "zig fmt: remove empty lines at start/end of block" {
281 try testTransform(
282 \\test {
283 \\
284 \\ if (foo) {
285 \\ foo();
286 \\ }
287 \\
288 \\}
289 \\
290 ,
291 \\test {
292 \\ if (foo) {
293 \\ foo();
294 \\ }
295 \\}
296 \\
297 );
298}
299
300test "zig fmt: allow empty line before commment at start of block" {
301 try testCanonical(
302 \\test {
303 \\
304 \\ // foo
305 \\ const x = 42;
306 \\}
307 \\
308 );
309}
310
311test "zig fmt: allow empty line before commment at start of block" {
312 try testCanonical(
313 \\test {
314 \\
315 \\ // foo
316 \\ const x = 42;
317 \\}
318 \\
319 );
320}
321
322test "zig fmt: trailing comma in fn parameter list" {
323 try testCanonical(
324 \\pub fn f(
325 \\ a: i32,
326 \\ b: i32,
327 \\) i32 {}
328 \\pub fn f(
329 \\ a: i32,
330 \\ b: i32,
331 \\) align(8) i32 {}
332 \\pub fn f(
333 \\ a: i32,
447334 \\ b: i32,
448335 \\) linksection(".text") i32 {}
449336 \\pub fn f(
......@@ -480,6 +367,31 @@ test "zig fmt: comptime struct field" {
480367 );
481368}
482369
370test "zig fmt: break from block" {
371 try testCanonical(
372 \\const a = blk: {
373 \\ break :blk 42;
374 \\};
375 \\const b = blk: {
376 \\ break :blk;
377 \\};
378 \\const c = {
379 \\ break 42;
380 \\};
381 \\const d = {
382 \\ break;
383 \\};
384 \\
385 );
386}
387
388test "zig fmt: grouped expressions (parentheses)" {
389 try testCanonical(
390 \\const r = (x + y) * (a + b);
391 \\
392 );
393}
394
483395test "zig fmt: c pointer type" {
484396 try testCanonical(
485397 \\pub extern fn repro() [*c]const u8;
......@@ -535,6 +447,19 @@ test "zig fmt: anytype struct field" {
535447 );
536448}
537449
450test "zig fmt: array types last token" {
451 try testCanonical(
452 \\test {
453 \\ const x = [40]u32;
454 \\}
455 \\
456 \\test {
457 \\ const x = [40:0]u32;
458 \\}
459 \\
460 );
461}
462
538463test "zig fmt: sentinel-terminated array type" {
539464 try testCanonical(
540465 \\pub fn cStrToPrefixedFileW(s: [*:0]const u8) ![PATH_MAX_WIDE:0]u16 {
......@@ -553,6 +478,58 @@ test "zig fmt: sentinel-terminated slice type" {
553478 );
554479}
555480
481test "zig fmt: pointer-to-one with modifiers" {
482 try testCanonical(
483 \\const x: *u32 = undefined;
484 \\const y: *allowzero align(8) const volatile u32 = undefined;
485 \\const z: *allowzero align(8:4:2) const volatile u32 = undefined;
486 \\
487 );
488}
489
490test "zig fmt: pointer-to-many with modifiers" {
491 try testCanonical(
492 \\const x: [*]u32 = undefined;
493 \\const y: [*]allowzero align(8) const volatile u32 = undefined;
494 \\const z: [*]allowzero align(8:4:2) const volatile u32 = undefined;
495 \\
496 );
497}
498
499test "zig fmt: sentinel pointer with modifiers" {
500 try testCanonical(
501 \\const x: [*:42]u32 = undefined;
502 \\const y: [*:42]allowzero align(8) const volatile u32 = undefined;
503 \\const y: [*:42]allowzero align(8:4:2) const volatile u32 = undefined;
504 \\
505 );
506}
507
508test "zig fmt: c pointer with modifiers" {
509 try testCanonical(
510 \\const x: [*c]u32 = undefined;
511 \\const y: [*c]allowzero align(8) const volatile u32 = undefined;
512 \\const z: [*c]allowzero align(8:4:2) const volatile u32 = undefined;
513 \\
514 );
515}
516
517test "zig fmt: slice with modifiers" {
518 try testCanonical(
519 \\const x: []u32 = undefined;
520 \\const y: []allowzero align(8) const volatile u32 = undefined;
521 \\
522 );
523}
524
525test "zig fmt: sentinel slice with modifiers" {
526 try testCanonical(
527 \\const x: [:42]u32 = undefined;
528 \\const y: [:42]allowzero align(8) const volatile u32 = undefined;
529 \\
530 );
531}
532
556533test "zig fmt: anon literal in array" {
557534 try testCanonical(
558535 \\var arr: [2]Foo = .{
......@@ -581,100 +558,91 @@ test "zig fmt: alignment in anonymous literal" {
581558 );
582559}
583560
584test "zig fmt: anon struct literal syntax" {
561test "zig fmt: anon struct literal 0 element" {
585562 try testCanonical(
586 \\const x = .{
587 \\ .a = b,
588 \\ .c = d,
589 \\};
563 \\test {
564 \\ const x = .{};
565 \\}
590566 \\
591567 );
592568}
593569
594test "zig fmt: anon list literal syntax" {
570test "zig fmt: anon struct literal 1 element" {
595571 try testCanonical(
596 \\const x = .{ a, b, c };
572 \\test {
573 \\ const x = .{ .a = b };
574 \\}
597575 \\
598576 );
599577}
600578
601test "zig fmt: async function" {
579test "zig fmt: anon struct literal 1 element comma" {
602580 try testCanonical(
603 \\pub const Server = struct {
604 \\ handleRequestFn: fn (*Server, *const std.net.Address, File) callconv(.Async) void,
605 \\};
606 \\test "hi" {
607 \\ var ptr = @ptrCast(fn (i32) callconv(.Async) void, other);
581 \\test {
582 \\ const x = .{
583 \\ .a = b,
584 \\ };
608585 \\}
609586 \\
610587 );
611588}
612589
613test "zig fmt: whitespace fixes" {
614 try testTransform("test \"\" {\r\n\tconst hi = x;\r\n}\n// zig fmt: off\ntest \"\"{\r\n\tconst a = b;}\r\n",
615 \\test "" {
616 \\ const hi = x;
590test "zig fmt: anon struct literal 2 element" {
591 try testCanonical(
592 \\test {
593 \\ const x = .{ .a = b, .c = d };
617594 \\}
618 \\// zig fmt: off
619 \\test ""{
620 \\ const a = b;}
621595 \\
622596 );
623597}
624598
625test "zig fmt: while else err prong with no block" {
599test "zig fmt: anon struct literal 2 element comma" {
626600 try testCanonical(
627 \\test "" {
628 \\ const result = while (returnError()) |value| {
629 \\ break value;
630 \\ } else |err| @as(i32, 2);
631 \\ expect(result == 2);
601 \\test {
602 \\ const x = .{
603 \\ .a = b,
604 \\ .c = d,
605 \\ };
632606 \\}
633607 \\
634608 );
635609}
636610
637test "zig fmt: tagged union with enum values" {
611test "zig fmt: anon struct literal 3 element" {
638612 try testCanonical(
639 \\const MultipleChoice2 = union(enum(u32)) {
640 \\ Unspecified1: i32,
641 \\ A: f32 = 20,
642 \\ Unspecified2: void,
643 \\ B: bool = 40,
644 \\ Unspecified3: i32,
645 \\ C: i8 = 60,
646 \\ Unspecified4: void,
647 \\ D: void = 1000,
648 \\ Unspecified5: i32,
649 \\};
613 \\test {
614 \\ const x = .{ .a = b, .c = d, .e = f };
615 \\}
650616 \\
651617 );
652618}
653619
654test "zig fmt: allowzero pointer" {
620test "zig fmt: anon struct literal 3 element comma" {
655621 try testCanonical(
656 \\const T = [*]allowzero const u8;
622 \\test {
623 \\ const x = .{
624 \\ .a = b,
625 \\ .c = d,
626 \\ .e = f,
627 \\ };
628 \\}
657629 \\
658630 );
659631}
660632
661test "zig fmt: enum literal" {
633test "zig fmt: struct literal 0 element" {
662634 try testCanonical(
663 \\const x = .hi;
635 \\test {
636 \\ const x = X{};
637 \\}
664638 \\
665639 );
666640}
667641
668test "zig fmt: enum literal inside array literal" {
642test "zig fmt: struct literal 1 element" {
669643 try testCanonical(
670 \\test "enums in arrays" {
671 \\ var colors = []Color{.Green};
672 \\ colors = []Colors{ .Green, .Cyan };
673 \\ colors = []Colors{
674 \\ .Grey,
675 \\ .Green,
676 \\ .Cyan,
677 \\ };
644 \\test {
645 \\ const x = X{ .a = b };
678646 \\}
679647 \\
680648 );
......@@ -682,656 +650,1016 @@ test "zig fmt: enum literal inside array literal" {
682650
683651test "zig fmt: Unicode code point literal larger than u8" {
684652 try testCanonical(
685 \\const x = '\u{01f4a9}';
653 \\test {
654 \\ const x = X{
655 \\ .a = b,
656 \\ };
657 \\}
686658 \\
687659 );
688660}
689661
690test "zig fmt: infix operator and then multiline string literal" {
662test "zig fmt: struct literal 2 element" {
691663 try testCanonical(
692 \\const x = "" ++
693 \\ \\ hi
694 \\;
664 \\test {
665 \\ const x = X{ .a = b, .c = d };
666 \\}
695667 \\
696668 );
697669}
698670
699test "zig fmt: infix operator and then multiline string literal" {
671test "zig fmt: struct literal 2 element comma" {
700672 try testCanonical(
701 \\const x = "" ++
702 \\ \\ hi0
703 \\ \\ hi1
704 \\ \\ hi2
705 \\;
673 \\test {
674 \\ const x = X{
675 \\ .a = b,
676 \\ .c = d,
677 \\ };
678 \\}
706679 \\
707680 );
708681}
709682
710test "zig fmt: C pointers" {
683test "zig fmt: struct literal 3 element" {
711684 try testCanonical(
712 \\const Ptr = [*c]i32;
685 \\test {
686 \\ const x = X{ .a = b, .c = d, .e = f };
687 \\}
713688 \\
714689 );
715690}
716691
717test "zig fmt: threadlocal" {
692test "zig fmt: struct literal 3 element comma" {
718693 try testCanonical(
719 \\threadlocal var x: i32 = 1234;
694 \\test {
695 \\ const x = X{
696 \\ .a = b,
697 \\ .c = d,
698 \\ .e = f,
699 \\ };
700 \\}
720701 \\
721702 );
722703}
723704
724test "zig fmt: linksection" {
705test "zig fmt: anon list literal 1 element" {
725706 try testCanonical(
726 \\export var aoeu: u64 linksection(".text.derp") = 1234;
727 \\export fn _start() linksection(".text.boot") callconv(.Naked) noreturn {}
707 \\test {
708 \\ const x = .{a};
709 \\}
728710 \\
729711 );
730712}
731713
732test "zig fmt: correctly move doc comments on struct fields" {
733 try testTransform(
734 \\pub const section_64 = extern struct {
735 \\ sectname: [16]u8, /// name of this section
736 \\ segname: [16]u8, /// segment this section goes in
737 \\};
738 ,
739 \\pub const section_64 = extern struct {
740 \\ /// name of this section
741 \\ sectname: [16]u8,
742 \\ /// segment this section goes in
743 \\ segname: [16]u8,
744 \\};
714test "zig fmt: anon list literal 1 element comma" {
715 try testCanonical(
716 \\test {
717 \\ const x = .{
718 \\ a,
719 \\ };
720 \\}
745721 \\
746722 );
747723}
748724
749test "zig fmt: correctly space struct fields with doc comments" {
750 try testTransform(
751 \\pub const S = struct {
752 \\ /// A
753 \\ a: u8,
754 \\ /// B
755 \\ /// B (cont)
756 \\ b: u8,
757 \\
758 \\
759 \\ /// C
760 \\ c: u8,
761 \\};
762 \\
763 ,
764 \\pub const S = struct {
765 \\ /// A
766 \\ a: u8,
767 \\ /// B
768 \\ /// B (cont)
769 \\ b: u8,
770 \\
771 \\ /// C
772 \\ c: u8,
773 \\};
725test "zig fmt: anon list literal 2 element" {
726 try testCanonical(
727 \\test {
728 \\ const x = .{ a, b };
729 \\}
774730 \\
775731 );
776732}
777733
778test "zig fmt: doc comments on param decl" {
734test "zig fmt: anon list literal 2 element comma" {
779735 try testCanonical(
780 \\pub const Allocator = struct {
781 \\ shrinkFn: fn (
782 \\ self: *Allocator,
783 \\ /// Guaranteed to be the same as what was returned from most recent call to
784 \\ /// `allocFn`, `reallocFn`, or `shrinkFn`.
785 \\ old_mem: []u8,
786 \\ /// Guaranteed to be the same as what was returned from most recent call to
787 \\ /// `allocFn`, `reallocFn`, or `shrinkFn`.
788 \\ old_alignment: u29,
789 \\ /// Guaranteed to be less than or equal to `old_mem.len`.
790 \\ new_byte_count: usize,
791 \\ /// Guaranteed to be less than or equal to `old_alignment`.
792 \\ new_alignment: u29,
793 \\ ) []u8,
794 \\};
736 \\test {
737 \\ const x = .{
738 \\ a,
739 \\ b,
740 \\ };
741 \\}
795742 \\
796743 );
797744}
798745
799test "zig fmt: aligned struct field" {
746test "zig fmt: anon list literal 3 element" {
800747 try testCanonical(
801 \\pub const S = struct {
802 \\ f: i32 align(32),
803 \\};
804 \\
805 );
806 try testCanonical(
807 \\pub const S = struct {
808 \\ f: i32 align(32) = 1,
809 \\};
748 \\test {
749 \\ const x = .{ a, b, c };
750 \\}
810751 \\
811752 );
812753}
813754
814test "zig fmt: comment to disable/enable zig fmt first" {
755test "zig fmt: anon list literal 3 element comma" {
815756 try testCanonical(
816 \\// Test trailing comma syntax
817 \\// zig fmt: off
757 \\test {
758 \\ const x = .{
759 \\ a,
760 \\ // foo
761 \\ b,
818762 \\
819 \\const struct_trailing_comma = struct { x: i32, y: i32, };
820 );
821}
822
823test "zig fmt: comment to disable/enable zig fmt" {
824 try testTransform(
825 \\const a = b;
826 \\// zig fmt: off
827 \\const c = d;
828 \\// zig fmt: on
829 \\const e = f;
830 ,
831 \\const a = b;
832 \\// zig fmt: off
833 \\const c = d;
834 \\// zig fmt: on
835 \\const e = f;
763 \\ c,
764 \\ };
765 \\}
836766 \\
837767 );
838768}
839769
840test "zig fmt: line comment following 'zig fmt: off'" {
770test "zig fmt: array literal 0 element" {
841771 try testCanonical(
842 \\// zig fmt: off
843 \\// Test
844 \\const e = f;
772 \\test {
773 \\ const x = [_]u32{};
774 \\}
775 \\
845776 );
846777}
847778
848test "zig fmt: doc comment following 'zig fmt: off'" {
779test "zig fmt: array literal 1 element" {
849780 try testCanonical(
850 \\// zig fmt: off
851 \\/// test
852 \\const e = f;
781 \\test {
782 \\ const x = [_]u32{a};
783 \\}
784 \\
853785 );
854786}
855787
856test "zig fmt: line and doc comment following 'zig fmt: off'" {
788test "zig fmt: array literal 1 element comma" {
857789 try testCanonical(
858 \\// zig fmt: off
859 \\// test 1
860 \\/// test 2
861 \\const e = f;
790 \\test {
791 \\ const x = [1]u32{
792 \\ a,
793 \\ };
794 \\}
795 \\
862796 );
863797}
864798
865test "zig fmt: doc and line comment following 'zig fmt: off'" {
799test "zig fmt: array literal 2 element" {
866800 try testCanonical(
867 \\// zig fmt: off
868 \\/// test 1
869 \\// test 2
870 \\const e = f;
801 \\test {
802 \\ const x = [_]u32{ a, b };
803 \\}
804 \\
871805 );
872806}
873807
874test "zig fmt: alternating 'zig fmt: off' and 'zig fmt: on'" {
808test "zig fmt: array literal 2 element comma" {
875809 try testCanonical(
876 \\// zig fmt: off
877 \\// zig fmt: on
878 \\// zig fmt: off
879 \\const e = f;
880 \\// zig fmt: off
881 \\// zig fmt: on
882 \\// zig fmt: off
883 \\const a = b;
884 \\// zig fmt: on
885 \\const c = d;
886 \\// zig fmt: on
810 \\test {
811 \\ const x = [2]u32{
812 \\ a,
813 \\ b,
814 \\ };
815 \\}
887816 \\
888817 );
889818}
890819
891test "zig fmt: line comment following 'zig fmt: on'" {
820test "zig fmt: array literal 3 element" {
892821 try testCanonical(
893 \\// zig fmt: off
894 \\const e = f;
895 \\// zig fmt: on
896 \\// test
897 \\const e = f;
822 \\test {
823 \\ const x = [_]u32{ a, b, c };
824 \\}
898825 \\
899826 );
900827}
901828
902test "zig fmt: doc comment following 'zig fmt: on'" {
829test "zig fmt: array literal 3 element comma" {
903830 try testCanonical(
904 \\// zig fmt: off
905 \\const e = f;
906 \\// zig fmt: on
907 \\/// test
908 \\const e = f;
831 \\test {
832 \\ const x = [3]u32{
833 \\ a,
834 \\ b,
835 \\ c,
836 \\ };
837 \\}
909838 \\
910839 );
911840}
912841
913test "zig fmt: line and doc comment following 'zig fmt: on'" {
842test "zig fmt: sentinel array literal 1 element" {
914843 try testCanonical(
915 \\// zig fmt: off
916 \\const e = f;
917 \\// zig fmt: on
918 \\// test1
919 \\/// test2
920 \\const e = f;
844 \\test {
845 \\ const x = [_:9000]u32{a};
846 \\}
921847 \\
922848 );
923849}
924850
925test "zig fmt: doc and line comment following 'zig fmt: on'" {
851test "zig fmt: slices" {
926852 try testCanonical(
927 \\// zig fmt: off
928 \\const e = f;
929 \\// zig fmt: on
930 \\/// test1
931 \\// test2
932 \\const e = f;
853 \\const a = b[0..];
854 \\const c = d[0..1];
855 \\const e = f[0..1 :0];
933856 \\
934857 );
935858}
936859
937test "zig fmt: pointer of unknown length" {
860test "zig fmt: slices with spaces in bounds" {
938861 try testCanonical(
939 \\fn foo(ptr: [*]u8) void {}
862 \\const a = b[0 + 0 ..];
863 \\const c = d[0 + 0 .. 1];
864 \\const e = f[0 .. 1 + 1 :0];
940865 \\
941866 );
942867}
943868
944test "zig fmt: spaces around slice operator" {
869test "zig fmt: block in slice expression" {
945870 try testCanonical(
946 \\var a = b[c..d];
947 \\var a = b[c..d :0];
948 \\var a = b[c + 1 .. d];
949 \\var a = b[c + 1 ..];
950 \\var a = b[c .. d + 1];
951 \\var a = b[c .. d + 1 :0];
952 \\var a = b[c.a..d.e];
953 \\var a = b[c.a..d.e :0];
871 \\const a = b[{
872 \\ _ = x;
873 \\}..];
874 \\const c = d[0..{
875 \\ _ = x;
876 \\ _ = y;
877 \\}];
878 \\const e = f[0..1 :{
879 \\ _ = x;
880 \\ _ = y;
881 \\ _ = z;
882 \\}];
954883 \\
955884 );
956885}
957886
958test "zig fmt: async call in if condition" {
887test "zig fmt: async function" {
959888 try testCanonical(
960 \\comptime {
961 \\ if (async b()) {
962 \\ a();
963 \\ }
889 \\pub const Server = struct {
890 \\ handleRequestFn: fn (*Server, *const std.net.Address, File) callconv(.Async) void,
891 \\};
892 \\test "hi" {
893 \\ var ptr = @ptrCast(fn (i32) callconv(.Async) void, other);
964894 \\}
965895 \\
966896 );
967897}
968898
969test "zig fmt: 2nd arg multiline string" {
970 try testCanonical(
971 \\comptime {
972 \\ cases.addAsm("hello world linux x86_64",
973 \\ \\.text
974 \\ , "Hello, world!\n");
899test "zig fmt: whitespace fixes" {
900 try testTransform("test \"\" {\r\n\tconst hi = x;\r\n}\n// zig fmt: off\ntest \"\"{\r\n\tconst a = b;}\r\n",
901 \\test "" {
902 \\ const hi = x;
975903 \\}
904 \\// zig fmt: off
905 \\test ""{
906 \\ const a = b;}
976907 \\
977908 );
978909}
979910
980test "zig fmt: 2nd arg multiline string many args" {
911test "zig fmt: while else err prong with no block" {
981912 try testCanonical(
982 \\comptime {
983 \\ cases.addAsm("hello world linux x86_64",
984 \\ \\.text
985 \\ , "Hello, world!\n", "Hello, world!\n");
913 \\test "" {
914 \\ const result = while (returnError()) |value| {
915 \\ break value;
916 \\ } else |err| @as(i32, 2);
917 \\ expect(result == 2);
986918 \\}
987919 \\
988920 );
989921}
990922
991test "zig fmt: final arg multiline string" {
923test "zig fmt: tagged union with enum values" {
992924 try testCanonical(
993 \\comptime {
994 \\ cases.addAsm("hello world linux x86_64", "Hello, world!\n",
995 \\ \\.text
996 \\ );
997 \\}
925 \\const MultipleChoice2 = union(enum(u32)) {
926 \\ Unspecified1: i32,
927 \\ A: f32 = 20,
928 \\ Unspecified2: void,
929 \\ B: bool = 40,
930 \\ Unspecified3: i32,
931 \\ C: i8 = 60,
932 \\ Unspecified4: void,
933 \\ D: void = 1000,
934 \\ Unspecified5: i32,
935 \\};
998936 \\
999937 );
1000938}
1001939
1002test "zig fmt: if condition wraps" {
1003 try testTransform(
1004 \\comptime {
1005 \\ if (cond and
1006 \\ cond) {
1007 \\ return x;
1008 \\ }
1009 \\ while (cond and
1010 \\ cond) {
1011 \\ return x;
1012 \\ }
1013 \\ if (a == b and
1014 \\ c) {
1015 \\ a = b;
1016 \\ }
1017 \\ while (a == b and
1018 \\ c) {
1019 \\ a = b;
1020 \\ }
1021 \\ if ((cond and
1022 \\ cond)) {
1023 \\ return x;
1024 \\ }
1025 \\ while ((cond and
1026 \\ cond)) {
1027 \\ return x;
1028 \\ }
1029 \\ var a = if (a) |*f| x: {
1030 \\ break :x &a.b;
1031 \\ } else |err| err;
1032 \\ var a = if (cond and
1033 \\ cond) |*f|
1034 \\ x: {
1035 \\ break :x &a.b;
1036 \\ } else |err| err;
940test "zig fmt: tagged union enum tag last token" {
941 try testCanonical(
942 \\test {
943 \\ const U = union(enum(u32)) {};
1037944 \\}
1038 ,
1039 \\comptime {
1040 \\ if (cond and
1041 \\ cond)
1042 \\ {
1043 \\ return x;
1044 \\ }
1045 \\ while (cond and
1046 \\ cond)
1047 \\ {
1048 \\ return x;
1049 \\ }
1050 \\ if (a == b and
1051 \\ c)
1052 \\ {
1053 \\ a = b;
1054 \\ }
1055 \\ while (a == b and
1056 \\ c)
1057 \\ {
1058 \\ a = b;
1059 \\ }
1060 \\ if ((cond and
1061 \\ cond))
1062 \\ {
1063 \\ return x;
1064 \\ }
1065 \\ while ((cond and
1066 \\ cond))
1067 \\ {
1068 \\ return x;
1069 \\ }
1070 \\ var a = if (a) |*f| x: {
1071 \\ break :x &a.b;
1072 \\ } else |err| err;
1073 \\ var a = if (cond and
1074 \\ cond) |*f|
1075 \\ x: {
1076 \\ break :x &a.b;
1077 \\ } else |err| err;
945 \\
946 \\test {
947 \\ const U = union(enum(u32)) { foo };
948 \\}
949 \\
950 \\test {
951 \\ const U = union(enum(u32)) {
952 \\ foo,
953 \\ };
1078954 \\}
1079955 \\
1080956 );
1081957}
1082958
1083test "zig fmt: if condition has line break but must not wrap" {
959test "zig fmt: allowzero pointer" {
1084960 try testCanonical(
1085 \\comptime {
1086 \\ if (self.user_input_options.put(
1087 \\ name,
1088 \\ UserInputOption{
1089 \\ .name = name,
1090 \\ .used = false,
1091 \\ },
1092 \\ ) catch unreachable) |*prev_value| {
1093 \\ foo();
1094 \\ bar();
1095 \\ }
1096 \\ if (put(
1097 \\ a,
1098 \\ b,
1099 \\ )) {
1100 \\ foo();
1101 \\ }
1102 \\}
961 \\const T = [*]allowzero const u8;
1103962 \\
1104963 );
1105964}
1106965
1107test "zig fmt: if condition has line break but must not wrap" {
966test "zig fmt: enum literal" {
1108967 try testCanonical(
1109 \\comptime {
1110 \\ if (self.user_input_options.put(name, UserInputOption{
1111 \\ .name = name,
1112 \\ .used = false,
1113 \\ }) catch unreachable) |*prev_value| {
1114 \\ foo();
1115 \\ bar();
1116 \\ }
1117 \\ if (put(
1118 \\ a,
1119 \\ b,
1120 \\ )) {
1121 \\ foo();
1122 \\ }
1123 \\}
968 \\const x = .hi;
1124969 \\
1125970 );
1126971}
1127972
1128test "zig fmt: function call with multiline argument" {
973test "zig fmt: enum literal inside array literal" {
1129974 try testCanonical(
1130 \\comptime {
1131 \\ self.user_input_options.put(name, UserInputOption{
1132 \\ .name = name,
1133 \\ .used = false,
1134 \\ });
975 \\test "enums in arrays" {
976 \\ var colors = []Color{.Green};
977 \\ colors = []Colors{ .Green, .Cyan };
978 \\ colors = []Colors{
979 \\ .Grey,
980 \\ .Green,
981 \\ .Cyan,
982 \\ };
1135983 \\}
1136984 \\
1137985 );
1138986}
1139987
1140test "zig fmt: same-line doc comment on variable declaration" {
1141 try testTransform(
1142 \\pub const MAP_ANONYMOUS = 0x1000; /// allocated from memory, swap space
1143 \\pub const MAP_FILE = 0x0000; /// map from file (default)
1144 \\
1145 \\pub const EMEDIUMTYPE = 124; /// Wrong medium type
1146 \\
1147 \\// nameserver query return codes
1148 \\pub const ENSROK = 0; /// DNS server returned answer with no data
1149 ,
1150 \\/// allocated from memory, swap space
1151 \\pub const MAP_ANONYMOUS = 0x1000;
1152 \\/// map from file (default)
1153 \\pub const MAP_FILE = 0x0000;
1154 \\
1155 \\/// Wrong medium type
1156 \\pub const EMEDIUMTYPE = 124;
1157 \\
1158 \\// nameserver query return codes
1159 \\/// DNS server returned answer with no data
1160 \\pub const ENSROK = 0;
988test "zig fmt: character literal larger than u8" {
989 try testCanonical(
990 \\const x = '\u{01f4a9}';
1161991 \\
1162992 );
1163993}
1164994
1165test "zig fmt: if-else with comment before else" {
995test "zig fmt: infix operator and then multiline string literal" {
1166996 try testCanonical(
1167 \\comptime {
1168 \\ // cexp(finite|nan +- i inf|nan) = nan + i nan
1169 \\ if ((hx & 0x7fffffff) != 0x7f800000) {
1170 \\ return Complex(f32).new(y - y, y - y);
1171 \\ } // cexp(-inf +- i inf|nan) = 0 + i0
1172 \\ else if (hx & 0x80000000 != 0) {
1173 \\ return Complex(f32).new(0, 0);
1174 \\ } // cexp(+inf +- i inf|nan) = inf + i nan
1175 \\ else {
1176 \\ return Complex(f32).new(x, y - y);
1177 \\ }
1178 \\}
997 \\const x = "" ++
998 \\ \\ hi
999 \\;
11791000 \\
11801001 );
11811002}
11821003
1183test "zig fmt: if nested" {
1004test "zig fmt: infix operator and then multiline string literal" {
11841005 try testCanonical(
1185 \\pub fn foo() void {
1186 \\ return if ((aInt & bInt) >= 0)
1187 \\ if (aInt < bInt)
1188 \\ GE_LESS
1189 \\ else if (aInt == bInt)
1190 \\ GE_EQUAL
1191 \\ else
1192 \\ GE_GREATER
1193 \\ else if (aInt > bInt)
1194 \\ GE_LESS
1195 \\ else if (aInt == bInt)
1196 \\ GE_EQUAL
1197 \\ else
1198 \\ GE_GREATER;
1199 \\}
1006 \\const x = "" ++
1007 \\ \\ hi0
1008 \\ \\ hi1
1009 \\ \\ hi2
1010 \\;
12001011 \\
12011012 );
12021013}
12031014
1204test "zig fmt: respect line breaks in if-else" {
1015test "zig fmt: C pointers" {
12051016 try testCanonical(
1206 \\comptime {
1207 \\ return if (cond) a else b;
1208 \\ return if (cond)
1209 \\ a
1210 \\ else
1211 \\ b;
1212 \\ return if (cond)
1213 \\ a
1214 \\ else if (cond)
1215 \\ b
1216 \\ else
1217 \\ c;
1218 \\}
1017 \\const Ptr = [*c]i32;
12191018 \\
12201019 );
12211020}
12221021
1223test "zig fmt: respect line breaks after infix operators" {
1022test "zig fmt: threadlocal" {
12241023 try testCanonical(
1225 \\comptime {
1226 \\ self.crc =
1227 \\ lookup_tables[0][p[7]] ^
1228 \\ lookup_tables[1][p[6]] ^
1229 \\ lookup_tables[2][p[5]] ^
1230 \\ lookup_tables[3][p[4]] ^
1231 \\ lookup_tables[4][@truncate(u8, self.crc >> 24)] ^
1232 \\ lookup_tables[5][@truncate(u8, self.crc >> 16)] ^
1233 \\ lookup_tables[6][@truncate(u8, self.crc >> 8)] ^
1234 \\ lookup_tables[7][@truncate(u8, self.crc >> 0)];
1235 \\}
1024 \\threadlocal var x: i32 = 1234;
12361025 \\
12371026 );
12381027}
12391028
1240test "zig fmt: fn decl with trailing comma" {
1241 try testTransform(
1242 \\fn foo(a: i32, b: i32,) void {}
1243 ,
1244 \\fn foo(
1245 \\ a: i32,
1246 \\ b: i32,
1247 \\) void {}
1029test "zig fmt: linksection" {
1030 try testCanonical(
1031 \\export var aoeu: u64 linksection(".text.derp") = 1234;
1032 \\export fn _start() linksection(".text.boot") callconv(.Naked) noreturn {}
12481033 \\
12491034 );
12501035}
12511036
1252test "zig fmt: enum decl with no trailing comma" {
1037test "zig fmt: correctly space struct fields with doc comments" {
12531038 try testTransform(
1254 \\const StrLitKind = enum {Normal, C};
1039 \\pub const S = struct {
1040 \\ /// A
1041 \\ a: u8,
1042 \\ /// B
1043 \\ /// B (cont)
1044 \\ b: u8,
1045 \\
1046 \\
1047 \\ /// C
1048 \\ c: u8,
1049 \\};
1050 \\
12551051 ,
1256 \\const StrLitKind = enum { Normal, C };
1052 \\pub const S = struct {
1053 \\ /// A
1054 \\ a: u8,
1055 \\ /// B
1056 \\ /// B (cont)
1057 \\ b: u8,
1058 \\
1059 \\ /// C
1060 \\ c: u8,
1061 \\};
12571062 \\
12581063 );
12591064}
12601065
1261test "zig fmt: switch comment before prong" {
1066test "zig fmt: doc comments on param decl" {
12621067 try testCanonical(
1263 \\comptime {
1264 \\ switch (a) {
1265 \\ // hi
1266 \\ 0 => {},
1267 \\ }
1268 \\}
1068 \\pub const Allocator = struct {
1069 \\ shrinkFn: fn (
1070 \\ self: *Allocator,
1071 \\ /// Guaranteed to be the same as what was returned from most recent call to
1072 \\ /// `allocFn`, `reallocFn`, or `shrinkFn`.
1073 \\ old_mem: []u8,
1074 \\ /// Guaranteed to be the same as what was returned from most recent call to
1075 \\ /// `allocFn`, `reallocFn`, or `shrinkFn`.
1076 \\ old_alignment: u29,
1077 \\ /// Guaranteed to be less than or equal to `old_mem.len`.
1078 \\ new_byte_count: usize,
1079 \\ /// Guaranteed to be less than or equal to `old_alignment`.
1080 \\ new_alignment: u29,
1081 \\ ) []u8,
1082 \\};
12691083 \\
12701084 );
12711085}
12721086
1273test "zig fmt: struct literal no trailing comma" {
1274 try testTransform(
1275 \\const a = foo{ .x = 1, .y = 2 };
1276 \\const a = foo{ .x = 1,
1277 \\ .y = 2 };
1278 ,
1279 \\const a = foo{ .x = 1, .y = 2 };
1280 \\const a = foo{
1281 \\ .x = 1,
1282 \\ .y = 2,
1087test "zig fmt: aligned struct field" {
1088 try testCanonical(
1089 \\pub const S = struct {
1090 \\ f: i32 align(32),
1091 \\};
1092 \\
1093 );
1094 try testCanonical(
1095 \\pub const S = struct {
1096 \\ f: i32 align(32) = 1,
12831097 \\};
12841098 \\
12851099 );
12861100}
12871101
1288test "zig fmt: struct literal containing a multiline expression" {
1289 try testTransform(
1290 \\const a = A{ .x = if (f1()) 10 else 20 };
1291 \\const a = A{ .x = if (f1()) 10 else 20, };
1292 \\const a = A{ .x = if (f1())
1293 \\ 10 else 20 };
1294 \\const a = A{ .x = if (f1()) 10 else 20, .y = f2() + 100 };
1295 \\const a = A{ .x = if (f1()) 10 else 20, .y = f2() + 100, };
1296 \\const a = A{ .x = if (f1())
1297 \\ 10 else 20};
1298 \\const a = A{ .x = switch(g) {0 => "ok", else => "no"} };
1299 \\
1300 ,
1301 \\const a = A{ .x = if (f1()) 10 else 20 };
1302 \\const a = A{
1303 \\ .x = if (f1()) 10 else 20,
1304 \\};
1305 \\const a = A{
1306 \\ .x = if (f1())
1307 \\ 10
1308 \\ else
1309 \\ 20,
1310 \\};
1311 \\const a = A{ .x = if (f1()) 10 else 20, .y = f2() + 100 };
1312 \\const a = A{
1313 \\ .x = if (f1()) 10 else 20,
1314 \\ .y = f2() + 100,
1315 \\};
1316 \\const a = A{
1317 \\ .x = if (f1())
1318 \\ 10
1319 \\ else
1320 \\ 20,
1321 \\};
1322 \\const a = A{
1323 \\ .x = switch (g) {
1324 \\ 0 => "ok",
1325 \\ else => "no",
1326 \\ },
1327 \\};
1102test "zig fmt: comment to disable/enable zig fmt first" {
1103 try testCanonical(
1104 \\// Test trailing comma syntax
1105 \\// zig fmt: off
13281106 \\
1107 \\const struct_trailing_comma = struct { x: i32, y: i32, };
13291108 );
13301109}
13311110
1332test "zig fmt: array literal with hint" {
1111test "zig fmt: comment to disable/enable zig fmt" {
13331112 try testTransform(
1334 \\const a = []u8{
1113 \\const a = b;
1114 \\// zig fmt: off
1115 \\const c = d;
1116 \\// zig fmt: on
1117 \\const e = f;
1118 ,
1119 \\const a = b;
1120 \\// zig fmt: off
1121 \\const c = d;
1122 \\// zig fmt: on
1123 \\const e = f;
1124 \\
1125 );
1126}
1127
1128test "zig fmt: line comment following 'zig fmt: off'" {
1129 try testCanonical(
1130 \\// zig fmt: off
1131 \\// Test
1132 \\const e = f;
1133 );
1134}
1135
1136test "zig fmt: doc comment following 'zig fmt: off'" {
1137 try testCanonical(
1138 \\// zig fmt: off
1139 \\/// test
1140 \\const e = f;
1141 );
1142}
1143
1144test "zig fmt: line and doc comment following 'zig fmt: off'" {
1145 try testCanonical(
1146 \\// zig fmt: off
1147 \\// test 1
1148 \\/// test 2
1149 \\const e = f;
1150 );
1151}
1152
1153test "zig fmt: doc and line comment following 'zig fmt: off'" {
1154 try testCanonical(
1155 \\// zig fmt: off
1156 \\/// test 1
1157 \\// test 2
1158 \\const e = f;
1159 );
1160}
1161
1162test "zig fmt: alternating 'zig fmt: off' and 'zig fmt: on'" {
1163 try testCanonical(
1164 \\// zig fmt: off
1165 \\// zig fmt: on
1166 \\// zig fmt: off
1167 \\const e = f;
1168 \\// zig fmt: off
1169 \\// zig fmt: on
1170 \\// zig fmt: off
1171 \\const a = b;
1172 \\// zig fmt: on
1173 \\const c = d;
1174 \\// zig fmt: on
1175 \\
1176 );
1177}
1178
1179test "zig fmt: line comment following 'zig fmt: on'" {
1180 try testCanonical(
1181 \\// zig fmt: off
1182 \\const e = f;
1183 \\// zig fmt: on
1184 \\// test
1185 \\const e = f;
1186 \\
1187 );
1188}
1189
1190test "zig fmt: doc comment following 'zig fmt: on'" {
1191 try testCanonical(
1192 \\// zig fmt: off
1193 \\const e = f;
1194 \\// zig fmt: on
1195 \\/// test
1196 \\const e = f;
1197 \\
1198 );
1199}
1200
1201test "zig fmt: line and doc comment following 'zig fmt: on'" {
1202 try testCanonical(
1203 \\// zig fmt: off
1204 \\const e = f;
1205 \\// zig fmt: on
1206 \\// test1
1207 \\/// test2
1208 \\const e = f;
1209 \\
1210 );
1211}
1212
1213test "zig fmt: doc and line comment following 'zig fmt: on'" {
1214 try testCanonical(
1215 \\// zig fmt: off
1216 \\const e = f;
1217 \\// zig fmt: on
1218 \\/// test1
1219 \\// test2
1220 \\const e = f;
1221 \\
1222 );
1223}
1224
1225test "zig fmt: 'zig fmt: (off|on)' works in the middle of code" {
1226 try testTransform(
1227 \\test "" {
1228 \\ const x = 42;
1229 \\
1230 \\ if (foobar) |y| {
1231 \\ // zig fmt: off
1232 \\ }// zig fmt: on
1233 \\
1234 \\ const z = 420;
1235 \\}
1236 \\
1237 ,
1238 \\test "" {
1239 \\ const x = 42;
1240 \\
1241 \\ if (foobar) |y| {
1242 \\ // zig fmt: off
1243 \\ }// zig fmt: on
1244 \\
1245 \\ const z = 420;
1246 \\}
1247 \\
1248 );
1249}
1250
1251test "zig fmt: pointer of unknown length" {
1252 try testCanonical(
1253 \\fn foo(ptr: [*]u8) void {}
1254 \\
1255 );
1256}
1257
1258test "zig fmt: spaces around slice operator" {
1259 try testCanonical(
1260 \\var a = b[c..d];
1261 \\var a = b[c..d :0];
1262 \\var a = b[c + 1 .. d];
1263 \\var a = b[c + 1 ..];
1264 \\var a = b[c .. d + 1];
1265 \\var a = b[c .. d + 1 :0];
1266 \\var a = b[c.a..d.e];
1267 \\var a = b[c.a..d.e :0];
1268 \\
1269 );
1270}
1271
1272test "zig fmt: async call in if condition" {
1273 try testCanonical(
1274 \\comptime {
1275 \\ if (async b()) {
1276 \\ a();
1277 \\ }
1278 \\}
1279 \\
1280 );
1281}
1282
1283test "zig fmt: 2nd arg multiline string" {
1284 try testCanonical(
1285 \\comptime {
1286 \\ cases.addAsm("hello world linux x86_64",
1287 \\ \\.text
1288 \\ , "Hello, world!\n");
1289 \\}
1290 \\
1291 );
1292 try testTransform(
1293 \\comptime {
1294 \\ cases.addAsm("hello world linux x86_64",
1295 \\ \\.text
1296 \\ , "Hello, world!\n",);
1297 \\}
1298 ,
1299 \\comptime {
1300 \\ cases.addAsm(
1301 \\ "hello world linux x86_64",
1302 \\ \\.text
1303 \\ ,
1304 \\ "Hello, world!\n",
1305 \\ );
1306 \\}
1307 \\
1308 );
1309}
1310
1311test "zig fmt: 2nd arg multiline string many args" {
1312 try testCanonical(
1313 \\comptime {
1314 \\ cases.addAsm("hello world linux x86_64",
1315 \\ \\.text
1316 \\ , "Hello, world!\n", "Hello, world!\n");
1317 \\}
1318 \\
1319 );
1320}
1321
1322test "zig fmt: final arg multiline string" {
1323 try testCanonical(
1324 \\comptime {
1325 \\ cases.addAsm("hello world linux x86_64", "Hello, world!\n",
1326 \\ \\.text
1327 \\ );
1328 \\}
1329 \\
1330 );
1331}
1332
1333test "zig fmt: if condition wraps" {
1334 try testTransform(
1335 \\comptime {
1336 \\ if (cond and
1337 \\ cond) {
1338 \\ return x;
1339 \\ }
1340 \\ while (cond and
1341 \\ cond) {
1342 \\ return x;
1343 \\ }
1344 \\ if (a == b and
1345 \\ c) {
1346 \\ a = b;
1347 \\ }
1348 \\ while (a == b and
1349 \\ c) {
1350 \\ a = b;
1351 \\ }
1352 \\ if ((cond and
1353 \\ cond)) {
1354 \\ return x;
1355 \\ }
1356 \\ while ((cond and
1357 \\ cond)) {
1358 \\ return x;
1359 \\ }
1360 \\ var a = if (a) |*f| x: {
1361 \\ break :x &a.b;
1362 \\ } else |err| err;
1363 \\ var a = if (cond and
1364 \\ cond) |*f|
1365 \\ x: {
1366 \\ break :x &a.b;
1367 \\ } else |err| err;
1368 \\}
1369 ,
1370 \\comptime {
1371 \\ if (cond and
1372 \\ cond)
1373 \\ {
1374 \\ return x;
1375 \\ }
1376 \\ while (cond and
1377 \\ cond)
1378 \\ {
1379 \\ return x;
1380 \\ }
1381 \\ if (a == b and
1382 \\ c)
1383 \\ {
1384 \\ a = b;
1385 \\ }
1386 \\ while (a == b and
1387 \\ c)
1388 \\ {
1389 \\ a = b;
1390 \\ }
1391 \\ if ((cond and
1392 \\ cond))
1393 \\ {
1394 \\ return x;
1395 \\ }
1396 \\ while ((cond and
1397 \\ cond))
1398 \\ {
1399 \\ return x;
1400 \\ }
1401 \\ var a = if (a) |*f| x: {
1402 \\ break :x &a.b;
1403 \\ } else |err| err;
1404 \\ var a = if (cond and
1405 \\ cond) |*f|
1406 \\ x: {
1407 \\ break :x &a.b;
1408 \\ } else |err| err;
1409 \\}
1410 \\
1411 );
1412}
1413
1414test "zig fmt: if condition has line break but must not wrap" {
1415 try testCanonical(
1416 \\comptime {
1417 \\ if (self.user_input_options.put(
1418 \\ name,
1419 \\ UserInputOption{
1420 \\ .name = name,
1421 \\ .used = false,
1422 \\ },
1423 \\ ) catch unreachable) |*prev_value| {
1424 \\ foo();
1425 \\ bar();
1426 \\ }
1427 \\ if (put(
1428 \\ a,
1429 \\ b,
1430 \\ )) {
1431 \\ foo();
1432 \\ }
1433 \\}
1434 \\
1435 );
1436}
1437
1438test "zig fmt: if condition has line break but must not wrap (no fn call comma)" {
1439 try testCanonical(
1440 \\comptime {
1441 \\ if (self.user_input_options.put(name, UserInputOption{
1442 \\ .name = name,
1443 \\ .used = false,
1444 \\ }) catch unreachable) |*prev_value| {
1445 \\ foo();
1446 \\ bar();
1447 \\ }
1448 \\ if (put(
1449 \\ a,
1450 \\ b,
1451 \\ )) {
1452 \\ foo();
1453 \\ }
1454 \\}
1455 \\
1456 );
1457}
1458
1459test "zig fmt: function call with multiline argument" {
1460 try testCanonical(
1461 \\comptime {
1462 \\ self.user_input_options.put(name, UserInputOption{
1463 \\ .name = name,
1464 \\ .used = false,
1465 \\ });
1466 \\}
1467 \\
1468 );
1469}
1470
1471test "zig fmt: if-else with comment before else" {
1472 try testCanonical(
1473 \\comptime {
1474 \\ // cexp(finite|nan +- i inf|nan) = nan + i nan
1475 \\ if ((hx & 0x7fffffff) != 0x7f800000) {
1476 \\ return Complex(f32).new(y - y, y - y);
1477 \\ } // cexp(-inf +- i inf|nan) = 0 + i0
1478 \\ else if (hx & 0x80000000 != 0) {
1479 \\ return Complex(f32).new(0, 0);
1480 \\ } // cexp(+inf +- i inf|nan) = inf + i nan
1481 \\ else {
1482 \\ return Complex(f32).new(x, y - y);
1483 \\ }
1484 \\}
1485 \\
1486 );
1487}
1488
1489test "zig fmt: if nested" {
1490 try testCanonical(
1491 \\pub fn foo() void {
1492 \\ return if ((aInt & bInt) >= 0)
1493 \\ if (aInt < bInt)
1494 \\ GE_LESS
1495 \\ else if (aInt == bInt)
1496 \\ GE_EQUAL
1497 \\ else
1498 \\ GE_GREATER
1499 \\ // comment
1500 \\ else if (aInt > bInt)
1501 \\ GE_LESS
1502 \\ else if (aInt == bInt)
1503 \\ GE_EQUAL
1504 \\ else
1505 \\ GE_GREATER;
1506 \\ // comment
1507 \\}
1508 \\
1509 );
1510}
1511
1512test "zig fmt: respect line breaks in if-else" {
1513 try testCanonical(
1514 \\comptime {
1515 \\ return if (cond) a else b;
1516 \\ return if (cond)
1517 \\ a
1518 \\ else
1519 \\ b;
1520 \\ return if (cond)
1521 \\ a
1522 \\ else if (cond)
1523 \\ b
1524 \\ else
1525 \\ c;
1526 \\}
1527 \\
1528 );
1529}
1530
1531test "zig fmt: respect line breaks after infix operators" {
1532 try testCanonical(
1533 \\comptime {
1534 \\ self.crc =
1535 \\ lookup_tables[0][p[7]] ^
1536 \\ lookup_tables[1][p[6]] ^
1537 \\ lookup_tables[2][p[5]] ^
1538 \\ lookup_tables[3][p[4]] ^
1539 \\ lookup_tables[4][@truncate(u8, self.crc >> 24)] ^
1540 \\ lookup_tables[5][@truncate(u8, self.crc >> 16)] ^
1541 \\ lookup_tables[6][@truncate(u8, self.crc >> 8)] ^
1542 \\ lookup_tables[7][@truncate(u8, self.crc >> 0)];
1543 \\}
1544 \\
1545 );
1546}
1547
1548test "zig fmt: fn decl with trailing comma" {
1549 try testTransform(
1550 \\fn foo(a: i32, b: i32,) void {}
1551 ,
1552 \\fn foo(
1553 \\ a: i32,
1554 \\ b: i32,
1555 \\) void {}
1556 \\
1557 );
1558}
1559
1560test "zig fmt: enum decl with no trailing comma" {
1561 try testTransform(
1562 \\const StrLitKind = enum {Normal, C};
1563 ,
1564 \\const StrLitKind = enum { Normal, C };
1565 \\
1566 );
1567}
1568
1569test "zig fmt: switch comment before prong" {
1570 try testCanonical(
1571 \\comptime {
1572 \\ switch (a) {
1573 \\ // hi
1574 \\ 0 => {},
1575 \\ }
1576 \\}
1577 \\
1578 );
1579}
1580
1581test "zig fmt: struct literal no trailing comma" {
1582 try testTransform(
1583 \\const a = foo{ .x = 1, .y = 2 };
1584 \\const a = foo{ .x = 1,
1585 \\ .y = 2 };
1586 \\const a = foo{ .x = 1,
1587 \\ .y = 2, };
1588 ,
1589 \\const a = foo{ .x = 1, .y = 2 };
1590 \\const a = foo{ .x = 1, .y = 2 };
1591 \\const a = foo{
1592 \\ .x = 1,
1593 \\ .y = 2,
1594 \\};
1595 \\
1596 );
1597}
1598
1599test "zig fmt: struct literal containing a multiline expression" {
1600 try testTransform(
1601 \\const a = A{ .x = if (f1()) 10 else 20 };
1602 \\const a = A{ .x = if (f1()) 10 else 20, };
1603 \\const a = A{ .x = if (f1())
1604 \\ 10 else 20 };
1605 \\const a = A{ .x = if (f1())
1606 \\ 10 else 20,};
1607 \\const a = A{ .x = if (f1()) 10 else 20, .y = f2() + 100 };
1608 \\const a = A{ .x = if (f1()) 10 else 20, .y = f2() + 100, };
1609 \\const a = A{ .x = if (f1())
1610 \\ 10 else 20};
1611 \\const a = A{ .x = if (f1())
1612 \\ 10 else 20,};
1613 \\const a = A{ .x = switch(g) {0 => "ok", else => "no"} };
1614 \\const a = A{ .x = switch(g) {0 => "ok", else => "no"}, };
1615 \\
1616 ,
1617 \\const a = A{ .x = if (f1()) 10 else 20 };
1618 \\const a = A{
1619 \\ .x = if (f1()) 10 else 20,
1620 \\};
1621 \\const a = A{ .x = if (f1())
1622 \\ 10
1623 \\else
1624 \\ 20 };
1625 \\const a = A{
1626 \\ .x = if (f1())
1627 \\ 10
1628 \\ else
1629 \\ 20,
1630 \\};
1631 \\const a = A{ .x = if (f1()) 10 else 20, .y = f2() + 100 };
1632 \\const a = A{
1633 \\ .x = if (f1()) 10 else 20,
1634 \\ .y = f2() + 100,
1635 \\};
1636 \\const a = A{ .x = if (f1())
1637 \\ 10
1638 \\else
1639 \\ 20 };
1640 \\const a = A{
1641 \\ .x = if (f1())
1642 \\ 10
1643 \\ else
1644 \\ 20,
1645 \\};
1646 \\const a = A{ .x = switch (g) {
1647 \\ 0 => "ok",
1648 \\ else => "no",
1649 \\} };
1650 \\const a = A{
1651 \\ .x = switch (g) {
1652 \\ 0 => "ok",
1653 \\ else => "no",
1654 \\ },
1655 \\};
1656 \\
1657 );
1658}
1659
1660test "zig fmt: array literal with hint" {
1661 try testTransform(
1662 \\const a = []u8{
13351663 \\ 1, 2, //
13361664 \\ 3,
13371665 \\ 4,
......@@ -1368,19 +1696,19 @@ test "zig fmt: array literal with hint" {
13681696 \\};
13691697 ,
13701698 \\const a = []u8{
1371 \\ 1, 2,
1699 \\ 1, 2, //
13721700 \\ 3, 4,
13731701 \\ 5, 6,
13741702 \\ 7,
13751703 \\};
13761704 \\const a = []u8{
1377 \\ 1, 2,
1705 \\ 1, 2, //
13781706 \\ 3, 4,
13791707 \\ 5, 6,
13801708 \\ 7, 8,
13811709 \\};
13821710 \\const a = []u8{
1383 \\ 1, 2,
1711 \\ 1, 2, //
13841712 \\ 3, 4,
13851713 \\ 5,
13861714 \\ 6, // blah
......@@ -1388,21 +1716,19 @@ test "zig fmt: array literal with hint" {
13881716 \\ 8,
13891717 \\};
13901718 \\const a = []u8{
1391 \\ 1, 2,
1719 \\ 1, 2, //
13921720 \\ 3, //
13931721 \\ 4,
1394 \\ 5, 6,
1722 \\ 5,
1723 \\ 6,
13951724 \\ 7,
13961725 \\};
13971726 \\const a = []u8{
13981727 \\ 1,
13991728 \\ 2,
1400 \\ 3,
1401 \\ 4,
1402 \\ 5,
1403 \\ 6,
1404 \\ 7,
1405 \\ 8,
1729 \\ 3, 4, //
1730 \\ 5, 6, //
1731 \\ 7, 8, //
14061732 \\};
14071733 \\
14081734 );
......@@ -1508,11 +1834,21 @@ test "zig fmt: empty block with only comment" {
15081834 );
15091835}
15101836
1511test "zig fmt: no trailing comma on struct decl" {
1512 try testCanonical(
1837test "zig fmt: trailing commas on struct decl" {
1838 try testTransform(
15131839 \\const RoundParam = struct {
15141840 \\ k: usize, s: u32, t: u32
15151841 \\};
1842 \\const RoundParam = struct {
1843 \\ k: usize, s: u32, t: u32,
1844 \\};
1845 ,
1846 \\const RoundParam = struct { k: usize, s: u32, t: u32 };
1847 \\const RoundParam = struct {
1848 \\ k: usize,
1849 \\ s: u32,
1850 \\ t: u32,
1851 \\};
15161852 \\
15171853 );
15181854}
......@@ -1560,11 +1896,7 @@ test "zig fmt: simple asm" {
15601896 \\ : [a] "x" (-> i32)
15611897 \\ : [a] "x" (1)
15621898 \\ );
1563 \\ asm ("still not real assembly"
1564 \\ :
1565 \\ :
1566 \\ : "a", "b"
1567 \\ );
1899 \\ asm ("still not real assembly" ::: "a", "b");
15681900 \\}
15691901 \\
15701902 );
......@@ -1581,7 +1913,7 @@ test "zig fmt: nested struct literal with one item" {
15811913
15821914test "zig fmt: switch cases trailing comma" {
15831915 try testTransform(
1584 \\fn switch_cases(x: i32) void {
1916 \\test "switch cases trailing comma"{
15851917 \\ switch (x) {
15861918 \\ 1,2,3 => {},
15871919 \\ 4,5, => {},
......@@ -1590,7 +1922,7 @@ test "zig fmt: switch cases trailing comma" {
15901922 \\ }
15911923 \\}
15921924 ,
1593 \\fn switch_cases(x: i32) void {
1925 \\test "switch cases trailing comma" {
15941926 \\ switch (x) {
15951927 \\ 1, 2, 3 => {},
15961928 \\ 4,
......@@ -1657,18 +1989,18 @@ test "zig fmt: line comment after doc comment" {
16571989 );
16581990}
16591991
1660test "zig fmt: float literal with exponent" {
1992test "zig fmt: bit field alignment" {
16611993 try testCanonical(
1662 \\test "bit field alignment" {
1994 \\test {
16631995 \\ assert(@TypeOf(&blah.b) == *align(1:3:6) const u3);
16641996 \\}
16651997 \\
16661998 );
16671999}
16682000
1669test "zig fmt: float literal with exponent" {
2001test "zig fmt: nested switch" {
16702002 try testCanonical(
1671 \\test "aoeu" {
2003 \\test {
16722004 \\ switch (state) {
16732005 \\ TermState.Start => switch (c) {
16742006 \\ '\x1b' => state = TermState.Escape,
......@@ -1679,6 +2011,7 @@ test "zig fmt: float literal with exponent" {
16792011 \\
16802012 );
16812013}
2014
16822015test "zig fmt: float literal with exponent" {
16832016 try testCanonical(
16842017 \\pub const f64_true_min = 4.94065645841246544177e-324;
......@@ -2135,7 +2468,7 @@ test "zig fmt: preserve spacing" {
21352468test "zig fmt: return types" {
21362469 try testCanonical(
21372470 \\pub fn main() !void {}
2138 \\pub fn main() anytype {}
2471 \\pub fn main() FooBar {}
21392472 \\pub fn main() i32 {}
21402473 \\
21412474 );
......@@ -2207,6 +2540,33 @@ test "zig fmt: return" {
22072540 );
22082541}
22092542
2543test "zig fmt: function attributes" {
2544 try testCanonical(
2545 \\export fn foo() void {}
2546 \\pub export fn foo() void {}
2547 \\extern fn foo() void;
2548 \\pub extern fn foo() void;
2549 \\extern "c" fn foo() void;
2550 \\pub extern "c" fn foo() void;
2551 \\noinline fn foo() void {}
2552 \\pub noinline fn foo() void {}
2553 \\
2554 );
2555}
2556
2557test "zig fmt: nested pointers with ** tokens" {
2558 try testCanonical(
2559 \\const x: *u32 = undefined;
2560 \\const x: **u32 = undefined;
2561 \\const x: ***u32 = undefined;
2562 \\const x: ****u32 = undefined;
2563 \\const x: *****u32 = undefined;
2564 \\const x: ******u32 = undefined;
2565 \\const x: *******u32 = undefined;
2566 \\
2567 );
2568}
2569
22102570test "zig fmt: pointer attributes" {
22112571 try testCanonical(
22122572 \\extern fn f1(s: *align(*u8) u8) c_int;
......@@ -2220,11 +2580,11 @@ test "zig fmt: pointer attributes" {
22202580
22212581test "zig fmt: slice attributes" {
22222582 try testCanonical(
2223 \\extern fn f1(s: *align(*u8) u8) c_int;
2224 \\extern fn f2(s: **align(1) *const *volatile u8) c_int;
2225 \\extern fn f3(s: *align(1) const *align(1) volatile *const volatile u8) c_int;
2226 \\extern fn f4(s: *align(1) const volatile u8) c_int;
2227 \\extern fn f5(s: [*:0]align(1) const volatile u8) c_int;
2583 \\extern fn f1(s: []align(*u8) u8) c_int;
2584 \\extern fn f2(s: []align(1) []const []volatile u8) c_int;
2585 \\extern fn f3(s: []align(1) const [:0]align(1) volatile []const volatile u8) c_int;
2586 \\extern fn f4(s: []align(1) const volatile u8) c_int;
2587 \\extern fn f5(s: [:0]align(1) const volatile u8) c_int;
22282588 \\
22292589 );
22302590}
......@@ -2241,7 +2601,7 @@ test "zig fmt: test declaration" {
22412601
22422602test "zig fmt: infix operators" {
22432603 try testCanonical(
2244 \\test "infix operators" {
2604 \\test {
22452605 \\ var i = undefined;
22462606 \\ i = 2;
22472607 \\ i *= 2;
......@@ -2345,7 +2705,7 @@ test "zig fmt: call expression" {
23452705
23462706test "zig fmt: anytype type" {
23472707 try testCanonical(
2348 \\fn print(args: anytype) anytype {}
2708 \\fn print(args: anytype) @This() {}
23492709 \\
23502710 );
23512711}
......@@ -2570,7 +2930,11 @@ test "zig fmt: catch" {
25702930 \\test "catch" {
25712931 \\ const a: anyerror!u8 = 0;
25722932 \\ _ = a catch return;
2933 \\ _ = a catch
2934 \\ return;
25732935 \\ _ = a catch |err| return;
2936 \\ _ = a catch |err|
2937 \\ return;
25742938 \\}
25752939 \\
25762940 );
......@@ -2746,12 +3110,6 @@ test "zig fmt: for" {
27463110 \\ d => {},
27473111 \\ };
27483112 \\
2749 \\ for (a) |b|
2750 \\ switch (b) {
2751 \\ c => {},
2752 \\ d => {},
2753 \\ };
2754 \\
27553113 \\ const res = for (a) |v, i| {
27563114 \\ break v;
27573115 \\ } else {
......@@ -2777,7 +3135,8 @@ test "zig fmt: for" {
27773135 \\test "fix for" {
27783136 \\ for (a) |x|
27793137 \\ f(x)
2780 \\ else continue;
3138 \\ else
3139 \\ continue;
27813140 \\}
27823141 \\
27833142 );
......@@ -2948,7 +3307,7 @@ test "zig fmt: nosuspend" {
29483307
29493308test "zig fmt: Block after if" {
29503309 try testCanonical(
2951 \\test "Block after if" {
3310 \\test {
29523311 \\ if (true) {
29533312 \\ const a = 0;
29543313 \\ }
......@@ -2961,7 +3320,7 @@ test "zig fmt: Block after if" {
29613320 );
29623321}
29633322
2964test "zig fmt: use" {
3323test "zig fmt: usingnamespace" {
29653324 try testCanonical(
29663325 \\usingnamespace @import("std");
29673326 \\pub usingnamespace @import("std");
......@@ -3025,10 +3384,7 @@ test "zig fmt: inline asm parameter alignment" {
30253384 \\ asm volatile (
30263385 \\ \\ foo
30273386 \\ \\ bar
3028 \\ :
3029 \\ :
3030 \\ : "", ""
3031 \\ );
3387 \\ ::: "", "");
30323388 \\ asm volatile (
30333389 \\ \\ foo
30343390 \\ \\ bar
......@@ -3087,16 +3443,12 @@ test "zig fmt: file ends with struct field" {
30873443}
30883444
30893445test "zig fmt: comment after empty comment" {
3090 try testTransform(
3446 try testCanonical(
30913447 \\const x = true; //
30923448 \\//
30933449 \\//
30943450 \\//a
30953451 \\
3096 ,
3097 \\const x = true;
3098 \\//a
3099 \\
31003452 );
31013453}
31023454
......@@ -3113,7 +3465,8 @@ test "zig fmt: line comment in array" {
31133465 ,
31143466 \\test "a" {
31153467 \\ var arr = [_]u32{
3116 \\ 0, // 1,
3468 \\ 0,
3469 \\ // 1,
31173470 \\ // 2,
31183471 \\ };
31193472 \\}
......@@ -3141,7 +3494,8 @@ test "zig fmt: comment after params" {
31413494 \\
31423495 ,
31433496 \\fn a(
3144 \\ b: u32, // c: u32,
3497 \\ b: u32,
3498 \\ // c: u32,
31453499 \\ // d: u32,
31463500 \\) void {}
31473501 \\
......@@ -3174,13 +3528,17 @@ test "zig fmt: comment in array initializer/access" {
31743528 \\ var c = b[ //aa
31753529 \\ 0
31763530 \\ ];
3177 \\ var d = [_
3531 \\ var d = [
3532 \\ _
31783533 \\ //aa
3534 \\ :
3535 \\ 0
31793536 \\ ]x{ //aa
31803537 \\ //bb
31813538 \\ 9,
31823539 \\ };
3183 \\ var e = d[0
3540 \\ var e = d[
3541 \\ 0
31843542 \\ //aa
31853543 \\ ];
31863544 \\}
......@@ -3199,7 +3557,8 @@ test "zig fmt: comments at several places in struct init" {
31993557 ,
32003558 \\var bar = Bar{
32013559 \\ .x = 10, // test
3202 \\ .y = "test", // test
3560 \\ .y = "test",
3561 \\ // test
32033562 \\};
32043563 \\
32053564 );
......@@ -3214,7 +3573,7 @@ test "zig fmt: comments at several places in struct init" {
32143573 );
32153574}
32163575
3217test "zig fmt: top level doc comments" {
3576test "zig fmt: container doc comments" {
32183577 try testCanonical(
32193578 \\//! tld 1
32203579 \\//! tld 2
......@@ -3235,25 +3594,25 @@ test "zig fmt: top level doc comments" {
32353594 \\ //! B tld 2
32363595 \\ //! B tld 3
32373596 \\
3238 \\ /// b doc
3597 \\ /// B doc
32393598 \\ b: u32,
32403599 \\};
32413600 \\
32423601 \\/// C doc
3243 \\const C = struct {
3602 \\const C = union(enum) { // comment
32443603 \\ //! C tld 1
32453604 \\ //! C tld 2
32463605 \\ //! C tld 3
3606 \\};
32473607 \\
3248 \\ /// c1 doc
3249 \\ c1: u32,
3250 \\
3251 \\ //! C tld 4
3252 \\ //! C tld 5
3253 \\ //! C tld 6
3608 \\/// D doc
3609 \\const D = union(Foo) {
3610 \\ //! D tld 1
3611 \\ //! D tld 2
3612 \\ //! D tld 3
32543613 \\
3255 \\ /// c2 doc
3256 \\ c2: u32,
3614 \\ /// D doc
3615 \\ b: u32,
32573616 \\};
32583617 \\
32593618 );
......@@ -3275,8 +3634,31 @@ test "zig fmt: extern without container keyword returns error" {
32753634 \\const container = extern {};
32763635 \\
32773636 , &[_]Error{
3278 .ExpectedExpr,
3279 .ExpectedVarDeclOrFn,
3637 .expected_container,
3638 });
3639}
3640
3641test "zig fmt: same line doc comment returns error" {
3642 try testError(
3643 \\const Foo = struct{
3644 \\ bar: u32, /// comment
3645 \\ foo: u32, /// comment
3646 \\ /// commment
3647 \\};
3648 \\
3649 \\const a = 42; /// comment
3650 \\
3651 \\extern fn foo() void; /// comment
3652 \\
3653 \\/// comment
3654 \\
3655 , &[_]Error{
3656 .same_line_doc_comment,
3657 .same_line_doc_comment,
3658 .unattached_doc_comment,
3659 .same_line_doc_comment,
3660 .same_line_doc_comment,
3661 .unattached_doc_comment,
32803662 });
32813663}
32823664
......@@ -3350,26 +3732,6 @@ test "zig fmt: hexadeciaml float literals with underscore separators" {
33503732 );
33513733}
33523734
3353test "zig fmt: convert async fn into callconv(.Async)" {
3354 try testTransform(
3355 \\async fn foo() void {}
3356 ,
3357 \\fn foo() callconv(.Async) void {}
3358 \\
3359 );
3360}
3361
3362test "zig fmt: convert extern fn proto into callconv(.C)" {
3363 try testTransform(
3364 \\extern fn foo0() void {}
3365 \\const foo1 = extern fn () void;
3366 ,
3367 \\extern fn foo0() void {}
3368 \\const foo1 = fn () callconv(.C) void;
3369 \\
3370 );
3371}
3372
33733735test "zig fmt: C var args" {
33743736 try testCanonical(
33753737 \\pub extern "c" fn printf(format: [*:0]const u8, ...) c_int;
......@@ -3458,6 +3820,54 @@ test "zig fmt: test comments in field access chain" {
34583820 );
34593821}
34603822
3823test "zig fmt: allow line break before field access" {
3824 try testCanonical(
3825 \\test {
3826 \\ const w = foo.bar().zippy(zag).iguessthisisok();
3827 \\
3828 \\ const x = foo
3829 \\ .bar()
3830 \\ . // comment
3831 \\ // comment
3832 \\ swooop().zippy(zag)
3833 \\ .iguessthisisok();
3834 \\
3835 \\ const y = view.output.root.server.input_manager.default_seat.wlr_seat.name;
3836 \\
3837 \\ const z = view.output.root.server
3838 \\ .input_manager //
3839 \\ .default_seat
3840 \\ . // comment
3841 \\ // another comment
3842 \\ wlr_seat.name;
3843 \\}
3844 \\
3845 );
3846 try testTransform(
3847 \\test {
3848 \\ const x = foo.
3849 \\ bar()
3850 \\ .zippy(zag).iguessthisisok();
3851 \\
3852 \\ const z = view.output.root.server.
3853 \\ input_manager.
3854 \\ default_seat.wlr_seat.name;
3855 \\}
3856 \\
3857 ,
3858 \\test {
3859 \\ const x = foo
3860 \\ .bar()
3861 \\ .zippy(zag).iguessthisisok();
3862 \\
3863 \\ const z = view.output.root.server
3864 \\ .input_manager
3865 \\ .default_seat.wlr_seat.name;
3866 \\}
3867 \\
3868 );
3869}
3870
34613871test "zig fmt: Indent comma correctly after multiline string literals in arg list (trailing comma)" {
34623872 try testCanonical(
34633873 \\fn foo() void {
......@@ -3495,8 +3905,7 @@ test "zig fmt: Control flow statement as body of blockless if" {
34953905 \\
34963906 \\ const zoom_node = if (focused_node == layout_first) while (it.next()) |node| {
34973907 \\ if (!node.view.pending.float and !node.view.pending.fullscreen) break node;
3498 \\ } else null else
3499 \\ focused_node;
3908 \\ } else null else focused_node;
35003909 \\
35013910 \\ const zoom_node = if (focused_node == layout_first)
35023911 \\ if (it.next()) {
......@@ -3513,14 +3922,13 @@ test "zig fmt: Control flow statement as body of blockless if" {
35133922 \\
35143923 \\ const zoom_node = if (focused_node == layout_first) switch (nodes) {
35153924 \\ 0 => 0,
3516 \\ } else
3517 \\ focused_node;
3925 \\ } else focused_node;
35183926 \\}
35193927 \\
35203928 );
35213929}
35223930
3523test "zig fmt: " {
3931test "zig fmt: regression test for #5722" {
35243932 try testCanonical(
35253933 \\pub fn sendViewTags(self: Self) void {
35263934 \\ var it = ViewStack(View).iterator(self.output.views.first, std.math.maxInt(u32));
......@@ -3580,8 +3988,8 @@ test "zig fmt: multiline string literals should play nice with array initializer
35803988 \\ 0,
35813989 \\ }}}}}}}};
35823990 \\ myFunc(.{
3583 \\ "aaaaaaa", "bbbbbb", "ccccc",
3584 \\ "dddd", ("eee"), ("fff"),
3991 \\ "aaaaaaa", "bbbbbb", "ccccc",
3992 \\ "dddd", ("eee"), ("fff"),
35853993 \\ ("gggg"),
35863994 \\ // Line comment
35873995 \\ \\Multiline String Literals can be quite long
......@@ -3610,9 +4018,11 @@ test "zig fmt: multiline string literals should play nice with array initializer
36104018 \\ (
36114019 \\ \\ xxx
36124020 \\ ),
3613 \\ "xxx", "xxx",
4021 \\ "xxx",
4022 \\ "xxx",
36144023 \\ },
3615 \\ .{ "xxxxxxx", "xxx", "xxx", "xxx" }, .{ "xxxxxxx", "xxx", "xxx", "xxx" },
4024 \\ .{ "xxxxxxx", "xxx", "xxx", "xxx" },
4025 \\ .{ "xxxxxxx", "xxx", "xxx", "xxx" },
36164026 \\ "aaaaaaa", "bbbbbb", "ccccc", // -
36174027 \\ "dddd", ("eee"), ("fff"),
36184028 \\ .{
......@@ -3620,7 +4030,8 @@ test "zig fmt: multiline string literals should play nice with array initializer
36204030 \\ (
36214031 \\ \\ xxx
36224032 \\ ),
3623 \\ "xxxxxxxxxxxxxx", "xxx",
4033 \\ "xxxxxxxxxxxxxx",
4034 \\ "xxx",
36244035 \\ },
36254036 \\ .{
36264037 \\ (
......@@ -3636,10 +4047,10 @@ test "zig fmt: multiline string literals should play nice with array initializer
36364047 );
36374048}
36384049
3639test "zig fmt: use of comments and Multiline string literals may force the parameters over multiple lines" {
4050test "zig fmt: use of comments and multiline string literals may force the parameters over multiple lines" {
36404051 try testCanonical(
36414052 \\pub fn makeMemUndefined(qzz: []u8) i1 {
3642 \\ cases.add( // fixed bug #2032
4053 \\ cases.add( // fixed bug foo
36434054 \\ "compile diagnostic string for top level decl type",
36444055 \\ \\export fn entry() void {
36454056 \\ \\ var foo: u32 = @This(){};
......@@ -3657,72 +4068,474 @@ test "zig fmt: use of comments and Multiline string literals may force the param
36574068 \\ .MakeMemUndefined, @ptrToInt(qzz.ptr), qzz.len, 0, 0, 0));
36584069 \\}
36594070 \\
3660 \\// This looks like garbage don't do this
3661 \\const rparen = tree.prevToken(
3662 \\// the first token for the annotation expressions is the left
3663 \\// parenthesis, hence the need for two prevToken
3664 \\ if (fn_proto.getAlignExpr()) |align_expr|
3665 \\ tree.prevToken(tree.prevToken(align_expr.firstToken()))
3666 \\else if (fn_proto.getSectionExpr()) |section_expr|
3667 \\ tree.prevToken(tree.prevToken(section_expr.firstToken()))
3668 \\else if (fn_proto.getCallconvExpr()) |callconv_expr|
3669 \\ tree.prevToken(tree.prevToken(callconv_expr.firstToken()))
3670 \\else switch (fn_proto.return_type) {
3671 \\ .Explicit => |node| node.firstToken(),
3672 \\ .InferErrorSet => |node| tree.prevToken(node.firstToken()),
3673 \\ .Invalid => unreachable,
3674 \\});
4071 \\// This looks like garbage don't do this
4072 \\const rparen = tree.prevToken(
4073 \\// the first token for the annotation expressions is the left
4074 \\// parenthesis, hence the need for two prevToken
4075 \\if (fn_proto.getAlignExpr()) |align_expr|
4076 \\ tree.prevToken(tree.prevToken(align_expr.firstToken()))
4077 \\else if (fn_proto.getSectionExpr()) |section_expr|
4078 \\ tree.prevToken(tree.prevToken(section_expr.firstToken()))
4079 \\else if (fn_proto.getCallconvExpr()) |callconv_expr|
4080 \\ tree.prevToken(tree.prevToken(callconv_expr.firstToken()))
4081 \\else switch (fn_proto.return_type) {
4082 \\ .Explicit => |node| node.firstToken(),
4083 \\ .InferErrorSet => |node| tree.prevToken(node.firstToken()),
4084 \\ .Invalid => unreachable,
4085 \\});
4086 \\
4087 );
4088}
4089
4090test "zig fmt: single argument trailing commas in @builtins()" {
4091 try testCanonical(
4092 \\pub fn foo(qzz: []u8) i1 {
4093 \\ @panic(
4094 \\ foo,
4095 \\ );
4096 \\ panic(
4097 \\ foo,
4098 \\ );
4099 \\ @panic(
4100 \\ foo,
4101 \\ bar,
4102 \\ );
4103 \\}
4104 \\
4105 );
4106}
4107
4108test "zig fmt: trailing comma should force multiline 1 column" {
4109 try testTransform(
4110 \\pub const UUID_NULL: uuid_t = [16]u8{0,0,0,0,};
4111 \\
4112 ,
4113 \\pub const UUID_NULL: uuid_t = [16]u8{
4114 \\ 0,
4115 \\ 0,
4116 \\ 0,
4117 \\ 0,
4118 \\};
4119 \\
4120 );
4121}
4122
4123test "zig fmt: function params should align nicely" {
4124 try testCanonical(
4125 \\pub fn foo() void {
4126 \\ cases.addRuntimeSafety("slicing operator with sentinel",
4127 \\ \\const std = @import("std");
4128 \\ ++ check_panic_msg ++
4129 \\ \\pub fn main() void {
4130 \\ \\ var buf = [4]u8{'a','b','c',0};
4131 \\ \\ const slice = buf[0..:0];
4132 \\ \\}
4133 \\ );
4134 \\}
4135 \\
4136 );
4137}
4138
4139test "zig fmt: fn proto end with anytype and comma" {
4140 try testCanonical(
4141 \\pub fn format(
4142 \\ out_stream: anytype,
4143 \\) !void {}
4144 \\
4145 );
4146}
4147
4148test "zig fmt: space after top level doc comment" {
4149 try testCanonical(
4150 \\//! top level doc comment
4151 \\
4152 \\field: i32,
4153 \\
4154 );
4155}
4156
4157test "zig fmt: for loop with ptr payload and index" {
4158 try testCanonical(
4159 \\test {
4160 \\ for (self.entries.items) |*item, i| {}
4161 \\ for (self.entries.items) |*item, i|
4162 \\ a = b;
4163 \\ for (self.entries.items) |*item, i| a = b;
4164 \\}
4165 \\
4166 );
4167}
4168
4169test "zig fmt: proper indent line comment after multi-line single expr while loop" {
4170 try testCanonical(
4171 \\test {
4172 \\ while (a) : (b)
4173 \\ foo();
4174 \\
4175 \\ // bar
4176 \\ baz();
4177 \\}
4178 \\
4179 );
4180}
4181
4182test "zig fmt: function with labeled block as return type" {
4183 try testCanonical(
4184 \\fn foo() t: {
4185 \\ break :t bar;
4186 \\} {
4187 \\ baz();
4188 \\}
4189 \\
4190 );
4191}
4192
4193test "zig fmt: line comment after multiline single expr if statement with multiline string" {
4194 try testCanonical(
4195 \\test {
4196 \\ if (foo)
4197 \\ x =
4198 \\ \\hello
4199 \\ \\hello
4200 \\ \\
4201 \\ ;
4202 \\
4203 \\ // bar
4204 \\ baz();
4205 \\
4206 \\ if (foo)
4207 \\ x =
4208 \\ \\hello
4209 \\ \\hello
4210 \\ \\
4211 \\ else
4212 \\ y =
4213 \\ \\hello
4214 \\ \\hello
4215 \\ \\
4216 \\ ;
4217 \\
4218 \\ // bar
4219 \\ baz();
4220 \\}
4221 \\
4222 );
4223}
4224
4225test "zig fmt: respect extra newline between fn and pub usingnamespace" {
4226 try testCanonical(
4227 \\fn foo() void {
4228 \\ bar();
4229 \\}
4230 \\
4231 \\pub usingnamespace baz;
36754232 \\
36764233 );
36774234}
36784235
3679test "zig fmt: single argument trailing commas in @builtins()" {
4236test "zig fmt: respect extra newline between switch items" {
36804237 try testCanonical(
3681 \\pub fn foo(qzz: []u8) i1 {
3682 \\ @panic(
3683 \\ foo,
3684 \\ );
3685 \\ panic(
3686 \\ foo,
3687 \\ );
3688 \\ @panic(
3689 \\ foo,
3690 \\ bar,
3691 \\ );
3692 \\}
4238 \\const a = switch (b) {
4239 \\ .c => {},
4240 \\
4241 \\ .d,
4242 \\ .e,
4243 \\ => f,
4244 \\};
36934245 \\
36944246 );
36954247}
36964248
3697test "zig fmt: trailing comma should force multiline 1 column" {
4249test "zig fmt: insert trailing comma if there are comments between switch values" {
36984250 try testTransform(
3699 \\pub const UUID_NULL: uuid_t = [16]u8{0,0,0,0,};
4251 \\const a = switch (b) {
4252 \\ .c => {},
4253 \\
4254 \\ .d, // foobar
4255 \\ .e
4256 \\ => f,
4257 \\
4258 \\ .g, .h
4259 \\ // comment
4260 \\ => i,
4261 \\};
37004262 \\
37014263 ,
3702 \\pub const UUID_NULL: uuid_t = [16]u8{
3703 \\ 0,
3704 \\ 0,
3705 \\ 0,
3706 \\ 0,
4264 \\const a = switch (b) {
4265 \\ .c => {},
4266 \\
4267 \\ .d, // foobar
4268 \\ .e,
4269 \\ => f,
4270 \\
4271 \\ .g,
4272 \\ .h,
4273 \\ // comment
4274 \\ => i,
37074275 \\};
37084276 \\
37094277 );
37104278}
37114279
3712test "zig fmt: function params should align nicely" {
3713 try testCanonical(
3714 \\pub fn foo() void {
3715 \\ cases.addRuntimeSafety("slicing operator with sentinel",
3716 \\ \\const std = @import("std");
3717 \\ ++ check_panic_msg ++
3718 \\ \\pub fn main() void {
3719 \\ \\ var buf = [4]u8{'a','b','c',0};
3720 \\ \\ const slice = buf[0..:0];
3721 \\ \\}
3722 \\ );
4280test "zig fmt: error for invalid bit range" {
4281 try testError(
4282 \\var x: []align(0:0:0)u8 = bar;
4283 , &[_]Error{
4284 .invalid_bit_range,
4285 });
4286}
4287
4288test "zig fmt: error for invalid align" {
4289 try testError(
4290 \\var x: [10]align(10)u8 = bar;
4291 , &[_]Error{
4292 .invalid_align,
4293 });
4294}
4295
4296test "recovery: top level" {
4297 try testError(
4298 \\test "" {inline}
4299 \\test "" {inline}
4300 , &[_]Error{
4301 .expected_inlinable,
4302 .expected_inlinable,
4303 });
4304}
4305
4306test "recovery: block statements" {
4307 try testError(
4308 \\test "" {
4309 \\ foo + +;
4310 \\ inline;
37234311 \\}
3724 \\
3725 );
4312 , &[_]Error{
4313 .invalid_token,
4314 .expected_inlinable,
4315 });
4316}
4317
4318test "recovery: missing comma" {
4319 try testError(
4320 \\test "" {
4321 \\ switch (foo) {
4322 \\ 2 => {}
4323 \\ 3 => {}
4324 \\ else => {
4325 \\ foo && bar +;
4326 \\ }
4327 \\ }
4328 \\}
4329 , &[_]Error{
4330 .expected_token,
4331 .expected_token,
4332 .invalid_and,
4333 .invalid_token,
4334 });
4335}
4336
4337test "recovery: extra qualifier" {
4338 try testError(
4339 \\const a: *const const u8;
4340 \\test ""
4341 , &[_]Error{
4342 .extra_const_qualifier,
4343 .expected_block,
4344 });
4345}
4346
4347test "recovery: missing return type" {
4348 try testError(
4349 \\fn foo() {
4350 \\ a && b;
4351 \\}
4352 \\test ""
4353 , &[_]Error{
4354 .expected_return_type,
4355 .invalid_and,
4356 .expected_block,
4357 });
4358}
4359
4360test "recovery: continue after invalid decl" {
4361 try testError(
4362 \\fn foo {
4363 \\ inline;
4364 \\}
4365 \\pub test "" {
4366 \\ async a && b;
4367 \\}
4368 , &[_]Error{
4369 .expected_token,
4370 .expected_pub_item,
4371 .expected_param_list,
4372 .invalid_and,
4373 });
4374 try testError(
4375 \\threadlocal test "" {
4376 \\ @a && b;
4377 \\}
4378 , &[_]Error{
4379 .expected_var_decl,
4380 .expected_param_list,
4381 .invalid_and,
4382 });
4383}
4384
4385test "recovery: invalid extern/inline" {
4386 try testError(
4387 \\inline test "" { a && b; }
4388 , &[_]Error{
4389 .expected_fn,
4390 .invalid_and,
4391 });
4392 try testError(
4393 \\extern "" test "" { a && b; }
4394 , &[_]Error{
4395 .expected_var_decl_or_fn,
4396 .invalid_and,
4397 });
4398}
4399
4400test "recovery: missing semicolon" {
4401 try testError(
4402 \\test "" {
4403 \\ comptime a && b
4404 \\ c && d
4405 \\ @foo
4406 \\}
4407 , &[_]Error{
4408 .invalid_and,
4409 .expected_token,
4410 .invalid_and,
4411 .expected_token,
4412 .expected_param_list,
4413 .expected_token,
4414 });
4415}
4416
4417test "recovery: invalid container members" {
4418 try testError(
4419 \\usingnamespace;
4420 \\foo+
4421 \\bar@,
4422 \\while (a == 2) { test "" {}}
4423 \\test "" {
4424 \\ a && b
4425 \\}
4426 , &[_]Error{
4427 .expected_expr,
4428 .expected_token,
4429 .expected_container_members,
4430 .invalid_and,
4431 .expected_token,
4432 });
4433}
4434
4435// TODO after https://github.com/ziglang/zig/issues/35 is implemented,
4436// we should be able to recover from this *at any indentation level*,
4437// reporting a parse error and yet also parsing all the decls even
4438// inside structs.
4439test "recovery: extra '}' at top level" {
4440 try testError(
4441 \\}}}
4442 \\test "" {
4443 \\ a && b;
4444 \\}
4445 , &[_]Error{
4446 .expected_token,
4447 });
4448}
4449
4450test "recovery: mismatched bracket at top level" {
4451 try testError(
4452 \\const S = struct {
4453 \\ arr: 128]?G
4454 \\};
4455 , &[_]Error{
4456 .expected_token,
4457 });
4458}
4459
4460test "recovery: invalid global error set access" {
4461 try testError(
4462 \\test "" {
4463 \\ error && foo;
4464 \\}
4465 , &[_]Error{
4466 .expected_token,
4467 .expected_token,
4468 .invalid_and,
4469 });
4470}
4471
4472test "recovery: invalid asterisk after pointer dereference" {
4473 try testError(
4474 \\test "" {
4475 \\ var sequence = "repeat".*** 10;
4476 \\}
4477 , &[_]Error{
4478 .asterisk_after_ptr_deref,
4479 });
4480 try testError(
4481 \\test "" {
4482 \\ var sequence = "repeat".** 10&&a;
4483 \\}
4484 , &[_]Error{
4485 .asterisk_after_ptr_deref,
4486 .invalid_and,
4487 });
4488}
4489
4490test "recovery: missing semicolon after if, for, while stmt" {
4491 try testError(
4492 \\test "" {
4493 \\ if (foo) bar
4494 \\ for (foo) |a| bar
4495 \\ while (foo) bar
4496 \\ a && b;
4497 \\}
4498 , &[_]Error{
4499 .expected_semi_or_else,
4500 .expected_semi_or_else,
4501 .expected_semi_or_else,
4502 .invalid_and,
4503 });
4504}
4505
4506test "recovery: invalid comptime" {
4507 try testError(
4508 \\comptime
4509 , &[_]Error{
4510 .expected_block_or_field,
4511 });
4512}
4513
4514test "recovery: missing block after for/while loops" {
4515 try testError(
4516 \\test "" { while (foo) }
4517 , &[_]Error{
4518 .expected_block_or_assignment,
4519 });
4520 try testError(
4521 \\test "" { for (foo) |bar| }
4522 , &[_]Error{
4523 .expected_block_or_assignment,
4524 });
4525}
4526
4527test "recovery: missing for payload" {
4528 try testError(
4529 \\comptime {
4530 \\ const a = for(a) {};
4531 \\ const a: for(a) {};
4532 \\ for(a) {}
4533 \\}
4534 , &[_]Error{
4535 .expected_loop_payload,
4536 .expected_loop_payload,
4537 .expected_loop_payload,
4538 });
37264539}
37274540
37284541const std = @import("std");
......@@ -3736,12 +4549,12 @@ var fixed_buffer_mem: [100 * 1024]u8 = undefined;
37364549fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *bool) ![]u8 {
37374550 const stderr = io.getStdErr().writer();
37384551
3739 const tree = try std.zig.parse(allocator, source);
3740 defer tree.deinit();
4552 var tree = try std.zig.parse(allocator, source);
4553 defer tree.deinit(allocator);
37414554
3742 for (tree.errors) |*parse_error| {
3743 const token = tree.token_locs[parse_error.loc()];
3744 const loc = tree.tokenLocation(0, parse_error.loc());
4555 for (tree.errors) |parse_error| {
4556 const token_start = tree.tokens.items(.start)[parse_error.token];
4557 const loc = tree.tokenLocation(0, parse_error.token);
37454558 try stderr.print("(memory buffer):{d}:{d}: error: ", .{ loc.line + 1, loc.column + 1 });
37464559 try tree.renderError(parse_error, stderr);
37474560 try stderr.print("\n{s}\n", .{source[loc.line_start..loc.line_end]});
......@@ -3750,13 +4563,7 @@ fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *b
37504563 while (i < loc.column) : (i += 1) {
37514564 try stderr.writeAll(" ");
37524565 }
3753 }
3754 {
3755 const caret_count = token.end - token.start;
3756 var i: usize = 0;
3757 while (i < caret_count) : (i += 1) {
3758 try stderr.writeAll("~");
3759 }
4566 try stderr.writeAll("^");
37604567 }
37614568 try stderr.writeAll("\n");
37624569 }
......@@ -3764,12 +4571,9 @@ fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *b
37644571 return error.ParseError;
37654572 }
37664573
3767 var buffer = std.ArrayList(u8).init(allocator);
3768 errdefer buffer.deinit();
3769
3770 const writer = buffer.writer();
3771 anything_changed.* = try std.zig.render(allocator, writer, tree);
3772 return buffer.toOwnedSlice();
4574 const formatted = try tree.render(allocator);
4575 anything_changed.* = !mem.eql(u8, formatted, source);
4576 return formatted;
37734577}
37744578fn testTransform(source: []const u8, expected_source: []const u8) !void {
37754579 const needed_alloc_count = x: {
......@@ -3822,14 +4626,14 @@ fn testCanonical(source: []const u8) !void {
38224626 return testTransform(source, source);
38234627}
38244628
3825const Error = std.meta.Tag(std.zig.ast.Error);
4629const Error = std.zig.ast.Error.Tag;
38264630
38274631fn testError(source: []const u8, expected_errors: []const Error) !void {
3828 const tree = try std.zig.parse(std.testing.allocator, source);
3829 defer tree.deinit();
4632 var tree = try std.zig.parse(std.testing.allocator, source);
4633 defer tree.deinit(std.testing.allocator);
38304634
38314635 std.testing.expect(tree.errors.len == expected_errors.len);
38324636 for (expected_errors) |expected, i| {
3833 std.testing.expect(expected == tree.errors[i]);
4637 std.testing.expectEqual(expected, tree.errors[i].tag);
38344638 }
38354639}
lib/std/zig/render.zig+2385-2340
......@@ -6,6 +6,7 @@
66const std = @import("../std.zig");
77const assert = std.debug.assert;
88const mem = std.mem;
9const Allocator = std.mem.Allocator;
910const meta = std.meta;
1011const ast = std.zig.ast;
1112const Token = std.zig.Token;
......@@ -13,2657 +14,2555 @@ const Token = std.zig.Token;
1314const indent_delta = 4;
1415const asm_indent_delta = 2;
1516
16pub const Error = error{
17 /// Ran out of memory allocating call stack frames to complete rendering.
18 OutOfMemory,
19};
17pub const Error = ast.Tree.RenderError;
2018
21/// Returns whether anything changed
22pub fn render(allocator: *mem.Allocator, stream: anytype, tree: *ast.Tree) (@TypeOf(stream).Error || Error)!bool {
23 // cannot render an invalid tree
24 std.debug.assert(tree.errors.len == 0);
19const Ais = AutoIndentingStream(std.ArrayList(u8).Writer);
2520
26 var change_detection_stream = std.io.changeDetectionStream(tree.source, stream);
27 var auto_indenting_stream = std.io.autoIndentingStream(indent_delta, change_detection_stream.writer());
21pub fn renderTree(buffer: *std.ArrayList(u8), tree: ast.Tree) Error!void {
22 assert(tree.errors.len == 0); // Cannot render an invalid tree.
23 var auto_indenting_stream = Ais{
24 .indent_delta = indent_delta,
25 .underlying_writer = buffer.writer(),
26 };
27 const ais = &auto_indenting_stream;
2828
29 try renderRoot(allocator, &auto_indenting_stream, tree);
29 // Render all the line comments at the beginning of the file.
30 const comment_end_loc = tree.tokens.items(.start)[0];
31 _ = try renderComments(ais, tree, 0, comment_end_loc);
3032
31 return change_detection_stream.changeDetected();
32}
33 if (tree.tokens.items(.tag)[0] == .container_doc_comment) {
34 try renderContainerDocComments(ais, tree, 0);
35 }
3336
34fn renderRoot(
35 allocator: *mem.Allocator,
36 ais: anytype,
37 tree: *ast.Tree,
38) (@TypeOf(ais.*).Error || Error)!void {
39
40 // render all the line comments at the beginning of the file
41 for (tree.token_ids) |token_id, i| {
42 if (token_id != .LineComment) break;
43 const token_loc = tree.token_locs[i];
44 try ais.writer().print("{s}\n", .{mem.trimRight(u8, tree.tokenSliceLoc(token_loc), " ")});
45 const next_token = tree.token_locs[i + 1];
46 const loc = tree.tokenLocationLoc(token_loc.end, next_token);
47 if (loc.line >= 2) {
48 try ais.insertNewline();
49 }
37 try renderMembers(buffer.allocator, ais, tree, tree.rootDecls());
38
39 if (ais.disabled_offset) |disabled_offset| {
40 try writeFixingWhitespace(ais.underlying_writer, tree.source[disabled_offset..]);
5041 }
42}
5143
52 var decl_i: ast.NodeIndex = 0;
53 const root_decls = tree.root_node.decls();
44/// Render all members in the given slice, keeping empty lines where appropriate
45fn renderMembers(gpa: *Allocator, ais: *Ais, tree: ast.Tree, members: []const ast.Node.Index) Error!void {
46 if (members.len == 0) return;
47 try renderMember(gpa, ais, tree, members[0], .newline);
48 for (members[1..]) |member| {
49 try renderExtraNewline(ais, tree, member);
50 try renderMember(gpa, ais, tree, member, .newline);
51 }
52}
5453
55 if (root_decls.len == 0) return;
56 while (true) {
57 var decl = root_decls[decl_i];
58
59 // This loop does the following:
60 //
61 // - Iterates through line/doc comment tokens that precedes the current
62 // decl.
63 // - Figures out the first token index (`copy_start_token_index`) which
64 // hasn't been copied to the output stream yet.
65 // - Detects `zig fmt: (off|on)` in the line comment tokens, and
66 // determines whether the current decl should be reformatted or not.
67 //
68 var token_index = decl.firstToken();
69 var fmt_active = true;
70 var found_fmt_directive = false;
71
72 var copy_start_token_index = token_index;
73
74 while (token_index != 0) {
75 token_index -= 1;
76 const token_id = tree.token_ids[token_index];
77 switch (token_id) {
78 .LineComment => {},
79 .DocComment => {
80 copy_start_token_index = token_index;
81 continue;
82 },
83 else => break,
54fn renderMember(gpa: *Allocator, ais: *Ais, tree: ast.Tree, decl: ast.Node.Index, space: Space) Error!void {
55 const token_tags = tree.tokens.items(.tag);
56 const main_tokens = tree.nodes.items(.main_token);
57 const datas = tree.nodes.items(.data);
58 try renderDocComments(ais, tree, tree.firstToken(decl));
59 switch (tree.nodes.items(.tag)[decl]) {
60 .fn_decl => {
61 // Some examples:
62 // pub extern "foo" fn ...
63 // export fn ...
64 const fn_proto = datas[decl].lhs;
65 const fn_token = main_tokens[fn_proto];
66 // Go back to the first token we should render here.
67 var i = fn_token;
68 while (i > 0) {
69 i -= 1;
70 switch (token_tags[i]) {
71 .keyword_extern,
72 .keyword_export,
73 .keyword_pub,
74 .string_literal,
75 .keyword_inline,
76 .keyword_noinline,
77 => continue,
78
79 else => {
80 i += 1;
81 break;
82 },
83 }
8484 }
85
86 const token_loc = tree.token_locs[token_index];
87 if (mem.eql(u8, mem.trim(u8, tree.tokenSliceLoc(token_loc)[2..], " "), "zig fmt: off")) {
88 if (!found_fmt_directive) {
89 fmt_active = false;
90 found_fmt_directive = true;
85 while (i < fn_token) : (i += 1) {
86 if (token_tags[i] == .keyword_inline) {
87 // TODO remove this special case when 0.9.0 is released.
88 // See the commit that introduced this comment for more details.
89 continue;
9190 }
92 } else if (mem.eql(u8, mem.trim(u8, tree.tokenSliceLoc(token_loc)[2..], " "), "zig fmt: on")) {
93 if (!found_fmt_directive) {
94 fmt_active = true;
95 found_fmt_directive = true;
91 try renderToken(ais, tree, i, .space);
92 }
93 assert(datas[decl].rhs != 0);
94 try renderExpression(gpa, ais, tree, fn_proto, .space);
95 return renderExpression(gpa, ais, tree, datas[decl].rhs, space);
96 },
97 .fn_proto_simple,
98 .fn_proto_multi,
99 .fn_proto_one,
100 .fn_proto,
101 => {
102 // Extern function prototypes are parsed as these tags.
103 // Go back to the first token we should render here.
104 const fn_token = main_tokens[decl];
105 var i = fn_token;
106 while (i > 0) {
107 i -= 1;
108 switch (token_tags[i]) {
109 .keyword_extern,
110 .keyword_export,
111 .keyword_pub,
112 .string_literal,
113 .keyword_inline,
114 .keyword_noinline,
115 => continue,
116
117 else => {
118 i += 1;
119 break;
120 },
96121 }
97122 }
98 }
123 while (i < fn_token) : (i += 1) {
124 try renderToken(ais, tree, i, .space);
125 }
126 try renderExpression(gpa, ais, tree, decl, .none);
127 return renderToken(ais, tree, tree.lastToken(decl) + 1, space); // semicolon
128 },
99129
100 if (!fmt_active) {
101 // Reformatting is disabled for the current decl and possibly some
102 // more decls that follow.
103 // Find the next `decl` for which reformatting is re-enabled.
104 token_index = decl.firstToken();
105
106 while (!fmt_active) {
107 decl_i += 1;
108 if (decl_i >= root_decls.len) {
109 // If there's no next reformatted `decl`, just copy the
110 // remaining input tokens and bail out.
111 const start = tree.token_locs[copy_start_token_index].start;
112 try copyFixingWhitespace(ais, tree.source[start..]);
113 return;
114 }
115 decl = root_decls[decl_i];
116 var decl_first_token_index = decl.firstToken();
117
118 while (token_index < decl_first_token_index) : (token_index += 1) {
119 const token_id = tree.token_ids[token_index];
120 switch (token_id) {
121 .LineComment => {},
122 .Eof => unreachable,
123 else => continue,
124 }
125 const token_loc = tree.token_locs[token_index];
126 if (mem.eql(u8, mem.trim(u8, tree.tokenSliceLoc(token_loc)[2..], " "), "zig fmt: on")) {
127 fmt_active = true;
128 } else if (mem.eql(u8, mem.trim(u8, tree.tokenSliceLoc(token_loc)[2..], " "), "zig fmt: off")) {
129 fmt_active = false;
130 }
131 }
130 .@"usingnamespace" => {
131 const main_token = main_tokens[decl];
132 const expr = datas[decl].lhs;
133 if (main_token > 0 and token_tags[main_token - 1] == .keyword_pub) {
134 try renderToken(ais, tree, main_token - 1, .space); // pub
132135 }
136 try renderToken(ais, tree, main_token, .space); // usingnamespace
137 try renderExpression(gpa, ais, tree, expr, .none);
138 return renderToken(ais, tree, tree.lastToken(expr) + 1, space); // ;
139 },
133140
134 // Found the next `decl` for which reformatting is enabled. Copy
135 // the input tokens before the `decl` that haven't been copied yet.
136 var copy_end_token_index = decl.firstToken();
137 token_index = copy_end_token_index;
138 while (token_index != 0) {
139 token_index -= 1;
140 const token_id = tree.token_ids[token_index];
141 switch (token_id) {
142 .LineComment => {},
143 .DocComment => {
144 copy_end_token_index = token_index;
145 continue;
146 },
147 else => break,
148 }
141 .global_var_decl => return renderVarDecl(gpa, ais, tree, tree.globalVarDecl(decl)),
142 .local_var_decl => return renderVarDecl(gpa, ais, tree, tree.localVarDecl(decl)),
143 .simple_var_decl => return renderVarDecl(gpa, ais, tree, tree.simpleVarDecl(decl)),
144 .aligned_var_decl => return renderVarDecl(gpa, ais, tree, tree.alignedVarDecl(decl)),
145
146 .test_decl => {
147 const test_token = main_tokens[decl];
148 try renderToken(ais, tree, test_token, .space);
149 if (token_tags[test_token + 1] == .string_literal) {
150 try renderToken(ais, tree, test_token + 1, .space);
149151 }
152 try renderExpression(gpa, ais, tree, datas[decl].rhs, space);
153 },
150154
151 const start = tree.token_locs[copy_start_token_index].start;
152 const end = tree.token_locs[copy_end_token_index].start;
153 try copyFixingWhitespace(ais, tree.source[start..end]);
154 }
155 .container_field_init => return renderContainerField(gpa, ais, tree, tree.containerFieldInit(decl), space),
156 .container_field_align => return renderContainerField(gpa, ais, tree, tree.containerFieldAlign(decl), space),
157 .container_field => return renderContainerField(gpa, ais, tree, tree.containerField(decl), space),
158 .@"comptime" => return renderExpression(gpa, ais, tree, decl, space),
155159
156 try renderTopLevelDecl(allocator, ais, tree, decl);
157 decl_i += 1;
158 if (decl_i >= root_decls.len) return;
159 try renderExtraNewline(tree, ais, root_decls[decl_i]);
160 .root => unreachable,
161 else => unreachable,
160162 }
161163}
162164
163fn renderExtraNewline(tree: *ast.Tree, ais: anytype, node: *ast.Node) @TypeOf(ais.*).Error!void {
164 return renderExtraNewlineToken(tree, ais, node.firstToken());
165}
166
167fn renderExtraNewlineToken(
168 tree: *ast.Tree,
169 ais: anytype,
170 first_token: ast.TokenIndex,
171) @TypeOf(ais.*).Error!void {
172 var prev_token = first_token;
173 if (prev_token == 0) return;
174 var newline_threshold: usize = 2;
175 while (tree.token_ids[prev_token - 1] == .DocComment) {
176 if (tree.tokenLocation(tree.token_locs[prev_token - 1].end, prev_token).line == 1) {
177 newline_threshold += 1;
178 }
179 prev_token -= 1;
180 }
181 const prev_token_end = tree.token_locs[prev_token - 1].end;
182 const loc = tree.tokenLocation(prev_token_end, first_token);
183 if (loc.line >= newline_threshold) {
184 try ais.insertNewline();
165/// Render all expressions in the slice, keeping empty lines where appropriate
166fn renderExpressions(gpa: *Allocator, ais: *Ais, tree: ast.Tree, expressions: []const ast.Node.Index, space: Space) Error!void {
167 if (expressions.len == 0) return;
168 try renderExpression(gpa, ais, tree, expressions[0], space);
169 for (expressions[1..]) |expression| {
170 try renderExtraNewline(ais, tree, expression);
171 try renderExpression(gpa, ais, tree, expression, space);
185172 }
186173}
187174
188fn renderTopLevelDecl(allocator: *mem.Allocator, ais: anytype, tree: *ast.Tree, decl: *ast.Node) (@TypeOf(ais.*).Error || Error)!void {
189 try renderContainerDecl(allocator, ais, tree, decl, .Newline);
190}
175fn renderExpression(gpa: *Allocator, ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Space) Error!void {
176 const token_tags = tree.tokens.items(.tag);
177 const main_tokens = tree.nodes.items(.main_token);
178 const node_tags = tree.nodes.items(.tag);
179 const datas = tree.nodes.items(.data);
180 switch (node_tags[node]) {
181 .identifier,
182 .integer_literal,
183 .float_literal,
184 .char_literal,
185 .true_literal,
186 .false_literal,
187 .null_literal,
188 .unreachable_literal,
189 .undefined_literal,
190 .anyframe_literal,
191 .string_literal,
192 => return renderToken(ais, tree, main_tokens[node], space),
193
194 .multiline_string_literal => {
195 var locked_indents = ais.lockOneShotIndent();
196 try ais.maybeInsertNewline();
191197
192fn renderContainerDecl(allocator: *mem.Allocator, ais: anytype, tree: *ast.Tree, decl: *ast.Node, space: Space) (@TypeOf(ais.*).Error || Error)!void {
193 switch (decl.tag) {
194 .FnProto => {
195 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
198 var i = datas[node].lhs;
199 while (i <= datas[node].rhs) : (i += 1) try renderToken(ais, tree, i, .newline);
196200
197 try renderDocComments(tree, ais, fn_proto, fn_proto.getDocComments());
201 while (locked_indents > 0) : (locked_indents -= 1) ais.popIndent();
198202
199 if (fn_proto.getBodyNode()) |body_node| {
200 try renderExpression(allocator, ais, tree, decl, .Space);
201 try renderExpression(allocator, ais, tree, body_node, space);
202 } else {
203 try renderExpression(allocator, ais, tree, decl, .None);
204 try renderToken(tree, ais, tree.nextToken(decl.lastToken()), space);
203 switch (space) {
204 .none, .space, .newline, .skip => {},
205 .semicolon => if (token_tags[i] == .semicolon) try renderToken(ais, tree, i, .newline),
206 .comma => if (token_tags[i] == .comma) try renderToken(ais, tree, i, .newline),
207 .comma_space => if (token_tags[i] == .comma) try renderToken(ais, tree, i, .space),
205208 }
206209 },
207210
208 .Use => {
209 const use_decl = @fieldParentPtr(ast.Node.Use, "base", decl);
211 .error_value => {
212 try renderToken(ais, tree, main_tokens[node], .none);
213 try renderToken(ais, tree, main_tokens[node] + 1, .none);
214 return renderToken(ais, tree, main_tokens[node] + 2, space);
215 },
216
217 .@"anytype" => return renderToken(ais, tree, main_tokens[node], space),
210218
211 if (use_decl.visib_token) |visib_token| {
212 try renderToken(tree, ais, visib_token, .Space); // pub
219 .block_two,
220 .block_two_semicolon,
221 => {
222 const statements = [2]ast.Node.Index{ datas[node].lhs, datas[node].rhs };
223 if (datas[node].lhs == 0) {
224 return renderBlock(gpa, ais, tree, node, statements[0..0], space);
225 } else if (datas[node].rhs == 0) {
226 return renderBlock(gpa, ais, tree, node, statements[0..1], space);
227 } else {
228 return renderBlock(gpa, ais, tree, node, statements[0..2], space);
213229 }
214 try renderToken(tree, ais, use_decl.use_token, .Space); // usingnamespace
215 try renderExpression(allocator, ais, tree, use_decl.expr, .None);
216 try renderToken(tree, ais, use_decl.semicolon_token, space); // ;
230 },
231 .block,
232 .block_semicolon,
233 => {
234 const statements = tree.extra_data[datas[node].lhs..datas[node].rhs];
235 return renderBlock(gpa, ais, tree, node, statements, space);
217236 },
218237
219 .VarDecl => {
220 const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", decl);
238 .@"errdefer" => {
239 const defer_token = main_tokens[node];
240 const payload_token = datas[node].lhs;
241 const expr = datas[node].rhs;
221242
222 try renderDocComments(tree, ais, var_decl, var_decl.getDocComments());
223 try renderVarDecl(allocator, ais, tree, var_decl);
243 try renderToken(ais, tree, defer_token, .space);
244 if (payload_token != 0) {
245 try renderToken(ais, tree, payload_token - 1, .none); // |
246 try renderToken(ais, tree, payload_token, .none); // identifier
247 try renderToken(ais, tree, payload_token + 1, .space); // |
248 }
249 return renderExpression(gpa, ais, tree, expr, space);
224250 },
225251
226 .TestDecl => {
227 const test_decl = @fieldParentPtr(ast.Node.TestDecl, "base", decl);
252 .@"defer" => {
253 const defer_token = main_tokens[node];
254 const expr = datas[node].rhs;
255 try renderToken(ais, tree, defer_token, .space);
256 return renderExpression(gpa, ais, tree, expr, space);
257 },
258 .@"comptime", .@"nosuspend" => {
259 const comptime_token = main_tokens[node];
260 const block = datas[node].lhs;
261 try renderToken(ais, tree, comptime_token, .space);
262 return renderExpression(gpa, ais, tree, block, space);
263 },
228264
229 try renderDocComments(tree, ais, test_decl, test_decl.doc_comments);
230 try renderToken(tree, ais, test_decl.test_token, .Space);
231 if (test_decl.name) |name|
232 try renderExpression(allocator, ais, tree, name, .Space);
233 try renderExpression(allocator, ais, tree, test_decl.body_node, space);
265 .@"suspend" => {
266 const suspend_token = main_tokens[node];
267 const body = datas[node].lhs;
268 if (body != 0) {
269 try renderToken(ais, tree, suspend_token, .space);
270 return renderExpression(gpa, ais, tree, body, space);
271 } else {
272 return renderToken(ais, tree, suspend_token, space);
273 }
234274 },
235275
236 .ContainerField => {
237 const field = @fieldParentPtr(ast.Node.ContainerField, "base", decl);
276 .@"catch" => {
277 const main_token = main_tokens[node];
278 const fallback_first = tree.firstToken(datas[node].rhs);
238279
239 try renderDocComments(tree, ais, field, field.doc_comments);
240 if (field.comptime_token) |t| {
241 try renderToken(tree, ais, t, .Space); // comptime
242 }
280 const same_line = tree.tokensOnSameLine(main_token, fallback_first);
281 const after_op_space = if (same_line) Space.space else Space.newline;
243282
244 const src_has_trailing_comma = blk: {
245 const maybe_comma = tree.nextToken(field.lastToken());
246 break :blk tree.token_ids[maybe_comma] == .Comma;
247 };
283 try renderExpression(gpa, ais, tree, datas[node].lhs, .space); // target
248284
249 // The trailing comma is emitted at the end, but if it's not present
250 // we still have to respect the specified `space` parameter
251 const last_token_space: Space = if (src_has_trailing_comma) .None else space;
252
253 if (field.type_expr == null and field.value_expr == null) {
254 try renderToken(tree, ais, field.name_token, last_token_space); // name
255 } else if (field.type_expr != null and field.value_expr == null) {
256 try renderToken(tree, ais, field.name_token, .None); // name
257 try renderToken(tree, ais, tree.nextToken(field.name_token), .Space); // :
258
259 if (field.align_expr) |align_value_expr| {
260 try renderExpression(allocator, ais, tree, field.type_expr.?, .Space); // type
261 const lparen_token = tree.prevToken(align_value_expr.firstToken());
262 const align_kw = tree.prevToken(lparen_token);
263 const rparen_token = tree.nextToken(align_value_expr.lastToken());
264 try renderToken(tree, ais, align_kw, .None); // align
265 try renderToken(tree, ais, lparen_token, .None); // (
266 try renderExpression(allocator, ais, tree, align_value_expr, .None); // alignment
267 try renderToken(tree, ais, rparen_token, last_token_space); // )
268 } else {
269 try renderExpression(allocator, ais, tree, field.type_expr.?, last_token_space); // type
270 }
271 } else if (field.type_expr == null and field.value_expr != null) {
272 try renderToken(tree, ais, field.name_token, .Space); // name
273 try renderToken(tree, ais, tree.nextToken(field.name_token), .Space); // =
274 try renderExpression(allocator, ais, tree, field.value_expr.?, last_token_space); // value
285 if (token_tags[fallback_first - 1] == .pipe) {
286 try renderToken(ais, tree, main_token, .space); // catch keyword
287 try renderToken(ais, tree, main_token + 1, .none); // pipe
288 try renderToken(ais, tree, main_token + 2, .none); // payload identifier
289 try renderToken(ais, tree, main_token + 3, after_op_space); // pipe
275290 } else {
276 try renderToken(tree, ais, field.name_token, .None); // name
277 try renderToken(tree, ais, tree.nextToken(field.name_token), .Space); // :
278
279 if (field.align_expr) |align_value_expr| {
280 try renderExpression(allocator, ais, tree, field.type_expr.?, .Space); // type
281 const lparen_token = tree.prevToken(align_value_expr.firstToken());
282 const align_kw = tree.prevToken(lparen_token);
283 const rparen_token = tree.nextToken(align_value_expr.lastToken());
284 try renderToken(tree, ais, align_kw, .None); // align
285 try renderToken(tree, ais, lparen_token, .None); // (
286 try renderExpression(allocator, ais, tree, align_value_expr, .None); // alignment
287 try renderToken(tree, ais, rparen_token, .Space); // )
288 } else {
289 try renderExpression(allocator, ais, tree, field.type_expr.?, .Space); // type
290 }
291 try renderToken(tree, ais, tree.prevToken(field.value_expr.?.firstToken()), .Space); // =
292 try renderExpression(allocator, ais, tree, field.value_expr.?, last_token_space); // value
291 assert(token_tags[fallback_first - 1] == .keyword_catch);
292 try renderToken(ais, tree, main_token, after_op_space); // catch keyword
293293 }
294294
295 if (src_has_trailing_comma) {
296 const comma = tree.nextToken(field.lastToken());
297 try renderToken(tree, ais, comma, space);
298 }
295 ais.pushIndentOneShot();
296 try renderExpression(gpa, ais, tree, datas[node].rhs, space); // fallback
299297 },
300298
301 .Comptime => {
302 assert(!decl.requireSemiColon());
303 try renderExpression(allocator, ais, tree, decl, space);
304 },
299 .field_access => {
300 const main_token = main_tokens[node];
301 const field_access = datas[node];
305302
306 .DocComment => {
307 const comment = @fieldParentPtr(ast.Node.DocComment, "base", decl);
308 const kind = tree.token_ids[comment.first_line];
309 try renderToken(tree, ais, comment.first_line, .Newline);
310 var tok_i = comment.first_line + 1;
311 while (true) : (tok_i += 1) {
312 const tok_id = tree.token_ids[tok_i];
313 if (tok_id == kind) {
314 try renderToken(tree, ais, tok_i, .Newline);
315 } else if (tok_id == .LineComment) {
316 continue;
317 } else {
318 break;
319 }
303 try renderExpression(gpa, ais, tree, field_access.lhs, .none);
304
305 // Allow a line break between the lhs and the dot if the lhs and rhs
306 // are on different lines.
307 const lhs_last_token = tree.lastToken(field_access.lhs);
308 const same_line = tree.tokensOnSameLine(lhs_last_token, main_token + 1);
309 if (!same_line) {
310 if (!hasComment(tree, lhs_last_token, main_token)) try ais.insertNewline();
311 ais.pushIndentOneShot();
320312 }
313
314 try renderToken(ais, tree, main_token, .none);
315
316 // This check ensures that zag() is indented in the following example:
317 // const x = foo
318 // .bar()
319 // . // comment
320 // zag();
321 if (!same_line and hasComment(tree, main_token, main_token + 1)) {
322 ais.pushIndentOneShot();
323 }
324
325 return renderToken(ais, tree, field_access.rhs, space);
321326 },
322 else => unreachable,
323 }
324}
325327
326fn renderExpression(
327 allocator: *mem.Allocator,
328 ais: anytype,
329 tree: *ast.Tree,
330 base: *ast.Node,
331 space: Space,
332) (@TypeOf(ais.*).Error || Error)!void {
333 switch (base.tag) {
334 .Identifier,
335 .IntegerLiteral,
336 .FloatLiteral,
337 .StringLiteral,
338 .CharLiteral,
339 .BoolLiteral,
340 .NullLiteral,
341 .Unreachable,
342 .ErrorType,
343 .UndefinedLiteral,
328 .error_union,
329 .switch_range,
344330 => {
345 const casted_node = base.cast(ast.Node.OneToken).?;
346 return renderToken(tree, ais, casted_node.token, space);
331 const infix = datas[node];
332 try renderExpression(gpa, ais, tree, infix.lhs, .none);
333 try renderToken(ais, tree, main_tokens[node], .none);
334 return renderExpression(gpa, ais, tree, infix.rhs, space);
347335 },
348336
349 .AnyType => {
350 const any_type = base.castTag(.AnyType).?;
351 if (mem.eql(u8, tree.tokenSlice(any_type.token), "var")) {
352 // TODO remove in next release cycle
353 try ais.writer().writeAll("anytype");
354 if (space == .Comma) try ais.writer().writeAll(",\n");
355 return;
337 .add,
338 .add_wrap,
339 .array_cat,
340 .array_mult,
341 .assign,
342 .assign_bit_and,
343 .assign_bit_or,
344 .assign_bit_shift_left,
345 .assign_bit_shift_right,
346 .assign_bit_xor,
347 .assign_div,
348 .assign_sub,
349 .assign_sub_wrap,
350 .assign_mod,
351 .assign_add,
352 .assign_add_wrap,
353 .assign_mul,
354 .assign_mul_wrap,
355 .bang_equal,
356 .bit_and,
357 .bit_or,
358 .bit_shift_left,
359 .bit_shift_right,
360 .bit_xor,
361 .bool_and,
362 .bool_or,
363 .div,
364 .equal_equal,
365 .greater_or_equal,
366 .greater_than,
367 .less_or_equal,
368 .less_than,
369 .merge_error_sets,
370 .mod,
371 .mul,
372 .mul_wrap,
373 .sub,
374 .sub_wrap,
375 .@"orelse",
376 => {
377 const infix = datas[node];
378 try renderExpression(gpa, ais, tree, infix.lhs, .space);
379 const op_token = main_tokens[node];
380 if (tree.tokensOnSameLine(op_token, op_token + 1)) {
381 try renderToken(ais, tree, op_token, .space);
382 } else {
383 ais.pushIndent();
384 try renderToken(ais, tree, op_token, .newline);
385 ais.popIndent();
356386 }
357 return renderToken(tree, ais, any_type.token, space);
387 ais.pushIndentOneShot();
388 return renderExpression(gpa, ais, tree, infix.rhs, space);
358389 },
359390
360 .Block, .LabeledBlock => {
361 const block: struct {
362 label: ?ast.TokenIndex,
363 statements: []*ast.Node,
364 lbrace: ast.TokenIndex,
365 rbrace: ast.TokenIndex,
366 } = b: {
367 if (base.castTag(.Block)) |block| {
368 break :b .{
369 .label = null,
370 .statements = block.statements(),
371 .lbrace = block.lbrace,
372 .rbrace = block.rbrace,
373 };
374 } else if (base.castTag(.LabeledBlock)) |block| {
375 break :b .{
376 .label = block.label,
377 .statements = block.statements(),
378 .lbrace = block.lbrace,
379 .rbrace = block.rbrace,
380 };
381 } else {
382 unreachable;
383 }
384 };
391 .bit_not,
392 .bool_not,
393 .negation,
394 .negation_wrap,
395 .optional_type,
396 .address_of,
397 => {
398 try renderToken(ais, tree, main_tokens[node], .none);
399 return renderExpression(gpa, ais, tree, datas[node].lhs, space);
400 },
385401
386 if (block.label) |label| {
387 try renderToken(tree, ais, label, Space.None);
388 try renderToken(tree, ais, tree.nextToken(label), Space.Space);
389 }
402 .@"try",
403 .@"resume",
404 .@"await",
405 => {
406 try renderToken(ais, tree, main_tokens[node], .space);
407 return renderExpression(gpa, ais, tree, datas[node].lhs, space);
408 },
390409
391 if (block.statements.len == 0) {
392 ais.pushIndentNextLine();
393 defer ais.popIndent();
394 try renderToken(tree, ais, block.lbrace, Space.None);
395 } else {
396 ais.pushIndentNextLine();
397 defer ais.popIndent();
410 .array_type => return renderArrayType(gpa, ais, tree, tree.arrayType(node), space),
411 .array_type_sentinel => return renderArrayType(gpa, ais, tree, tree.arrayTypeSentinel(node), space),
398412
399 try renderToken(tree, ais, block.lbrace, Space.Newline);
413 .ptr_type_aligned => return renderPtrType(gpa, ais, tree, tree.ptrTypeAligned(node), space),
414 .ptr_type_sentinel => return renderPtrType(gpa, ais, tree, tree.ptrTypeSentinel(node), space),
415 .ptr_type => return renderPtrType(gpa, ais, tree, tree.ptrType(node), space),
416 .ptr_type_bit_range => return renderPtrType(gpa, ais, tree, tree.ptrTypeBitRange(node), space),
400417
401 for (block.statements) |statement, i| {
402 try renderStatement(allocator, ais, tree, statement);
418 .array_init_one, .array_init_one_comma => {
419 var elements: [1]ast.Node.Index = undefined;
420 return renderArrayInit(gpa, ais, tree, tree.arrayInitOne(&elements, node), space);
421 },
422 .array_init_dot_two, .array_init_dot_two_comma => {
423 var elements: [2]ast.Node.Index = undefined;
424 return renderArrayInit(gpa, ais, tree, tree.arrayInitDotTwo(&elements, node), space);
425 },
426 .array_init_dot,
427 .array_init_dot_comma,
428 => return renderArrayInit(gpa, ais, tree, tree.arrayInitDot(node), space),
429 .array_init,
430 .array_init_comma,
431 => return renderArrayInit(gpa, ais, tree, tree.arrayInit(node), space),
432
433 .struct_init_one, .struct_init_one_comma => {
434 var fields: [1]ast.Node.Index = undefined;
435 return renderStructInit(gpa, ais, tree, node, tree.structInitOne(&fields, node), space);
436 },
437 .struct_init_dot_two, .struct_init_dot_two_comma => {
438 var fields: [2]ast.Node.Index = undefined;
439 return renderStructInit(gpa, ais, tree, node, tree.structInitDotTwo(&fields, node), space);
440 },
441 .struct_init_dot,
442 .struct_init_dot_comma,
443 => return renderStructInit(gpa, ais, tree, node, tree.structInitDot(node), space),
444 .struct_init,
445 .struct_init_comma,
446 => return renderStructInit(gpa, ais, tree, node, tree.structInit(node), space),
447
448 .call_one, .call_one_comma, .async_call_one, .async_call_one_comma => {
449 var params: [1]ast.Node.Index = undefined;
450 return renderCall(gpa, ais, tree, tree.callOne(&params, node), space);
451 },
403452
404 if (i + 1 < block.statements.len) {
405 try renderExtraNewline(tree, ais, block.statements[i + 1]);
406 }
407 }
408 }
409 return renderToken(tree, ais, block.rbrace, space);
453 .call,
454 .call_comma,
455 .async_call,
456 .async_call_comma,
457 => return renderCall(gpa, ais, tree, tree.callFull(node), space),
458
459 .array_access => {
460 const suffix = datas[node];
461 const lbracket = tree.firstToken(suffix.rhs) - 1;
462 const rbracket = tree.lastToken(suffix.rhs) + 1;
463 const one_line = tree.tokensOnSameLine(lbracket, rbracket);
464 const inner_space = if (one_line) Space.none else Space.newline;
465 try renderExpression(gpa, ais, tree, suffix.lhs, .none);
466 ais.pushIndentNextLine();
467 try renderToken(ais, tree, lbracket, inner_space); // [
468 try renderExpression(gpa, ais, tree, suffix.rhs, inner_space);
469 ais.popIndent();
470 return renderToken(ais, tree, rbracket, space); // ]
410471 },
411472
412 .Defer => {
413 const defer_node = @fieldParentPtr(ast.Node.Defer, "base", base);
473 .slice_open => return renderSlice(gpa, ais, tree, tree.sliceOpen(node), space),
474 .slice => return renderSlice(gpa, ais, tree, tree.slice(node), space),
475 .slice_sentinel => return renderSlice(gpa, ais, tree, tree.sliceSentinel(node), space),
414476
415 try renderToken(tree, ais, defer_node.defer_token, Space.Space);
416 if (defer_node.payload) |payload| {
417 try renderExpression(allocator, ais, tree, payload, Space.Space);
418 }
419 return renderExpression(allocator, ais, tree, defer_node.expr, space);
477 .deref => {
478 try renderExpression(gpa, ais, tree, datas[node].lhs, .none);
479 return renderToken(ais, tree, main_tokens[node], space);
420480 },
421 .Comptime => {
422 const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", base);
423481
424 try renderToken(tree, ais, comptime_node.comptime_token, Space.Space);
425 return renderExpression(allocator, ais, tree, comptime_node.expr, space);
482 .unwrap_optional => {
483 try renderExpression(gpa, ais, tree, datas[node].lhs, .none);
484 try renderToken(ais, tree, main_tokens[node], .none);
485 return renderToken(ais, tree, datas[node].rhs, space);
426486 },
427 .Nosuspend => {
428 const nosuspend_node = @fieldParentPtr(ast.Node.Nosuspend, "base", base);
429 if (mem.eql(u8, tree.tokenSlice(nosuspend_node.nosuspend_token), "noasync")) {
430 // TODO: remove this
431 try ais.writer().writeAll("nosuspend ");
432 } else {
433 try renderToken(tree, ais, nosuspend_node.nosuspend_token, Space.Space);
487
488 .@"break" => {
489 const main_token = main_tokens[node];
490 const label_token = datas[node].lhs;
491 const target = datas[node].rhs;
492 if (label_token == 0 and target == 0) {
493 try renderToken(ais, tree, main_token, space); // break keyword
494 } else if (label_token == 0 and target != 0) {
495 try renderToken(ais, tree, main_token, .space); // break keyword
496 try renderExpression(gpa, ais, tree, target, space);
497 } else if (label_token != 0 and target == 0) {
498 try renderToken(ais, tree, main_token, .space); // break keyword
499 try renderToken(ais, tree, label_token - 1, .none); // colon
500 try renderToken(ais, tree, label_token, space); // identifier
501 } else if (label_token != 0 and target != 0) {
502 try renderToken(ais, tree, main_token, .space); // break keyword
503 try renderToken(ais, tree, label_token - 1, .none); // colon
504 try renderToken(ais, tree, label_token, .space); // identifier
505 try renderExpression(gpa, ais, tree, target, space);
434506 }
435 return renderExpression(allocator, ais, tree, nosuspend_node.expr, space);
436507 },
437508
438 .Suspend => {
439 const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base);
440
441 if (suspend_node.body) |body| {
442 try renderToken(tree, ais, suspend_node.suspend_token, Space.Space);
443 return renderExpression(allocator, ais, tree, body, space);
509 .@"continue" => {
510 const main_token = main_tokens[node];
511 const label = datas[node].lhs;
512 if (label != 0) {
513 try renderToken(ais, tree, main_token, .space); // continue
514 try renderToken(ais, tree, label - 1, .none); // :
515 return renderToken(ais, tree, label, space); // label
444516 } else {
445 return renderToken(tree, ais, suspend_node.suspend_token, space);
517 return renderToken(ais, tree, main_token, space); // continue
446518 }
447519 },
448520
449 .Catch => {
450 const infix_op_node = @fieldParentPtr(ast.Node.Catch, "base", base);
451
452 const op_space = Space.Space;
453 try renderExpression(allocator, ais, tree, infix_op_node.lhs, op_space);
454
455 const after_op_space = blk: {
456 const same_line = tree.tokensOnSameLine(infix_op_node.op_token, tree.nextToken(infix_op_node.op_token));
457 break :blk if (same_line) op_space else Space.Newline;
458 };
459
460 try renderToken(tree, ais, infix_op_node.op_token, after_op_space);
461
462 if (infix_op_node.payload) |payload| {
463 try renderExpression(allocator, ais, tree, payload, Space.Space);
521 .@"return" => {
522 if (datas[node].lhs != 0) {
523 try renderToken(ais, tree, main_tokens[node], .space);
524 try renderExpression(gpa, ais, tree, datas[node].lhs, space);
525 } else {
526 try renderToken(ais, tree, main_tokens[node], space);
464527 }
528 },
465529
530 .grouped_expression => {
531 try renderToken(ais, tree, main_tokens[node], .none); // lparen
466532 ais.pushIndentOneShot();
467 return renderExpression(allocator, ais, tree, infix_op_node.rhs, space);
533 try renderExpression(gpa, ais, tree, datas[node].lhs, .none);
534 return renderToken(ais, tree, datas[node].rhs, space); // rparen
468535 },
469536
470 .Add,
471 .AddWrap,
472 .ArrayCat,
473 .ArrayMult,
474 .Assign,
475 .AssignBitAnd,
476 .AssignBitOr,
477 .AssignBitShiftLeft,
478 .AssignBitShiftRight,
479 .AssignBitXor,
480 .AssignDiv,
481 .AssignSub,
482 .AssignSubWrap,
483 .AssignMod,
484 .AssignAdd,
485 .AssignAddWrap,
486 .AssignMul,
487 .AssignMulWrap,
488 .BangEqual,
489 .BitAnd,
490 .BitOr,
491 .BitShiftLeft,
492 .BitShiftRight,
493 .BitXor,
494 .BoolAnd,
495 .BoolOr,
496 .Div,
497 .EqualEqual,
498 .ErrorUnion,
499 .GreaterOrEqual,
500 .GreaterThan,
501 .LessOrEqual,
502 .LessThan,
503 .MergeErrorSets,
504 .Mod,
505 .Mul,
506 .MulWrap,
507 .Period,
508 .Range,
509 .Sub,
510 .SubWrap,
511 .OrElse,
512 => {
513 const infix_op_node = @fieldParentPtr(ast.Node.SimpleInfixOp, "base", base);
537 .container_decl,
538 .container_decl_trailing,
539 => return renderContainerDecl(gpa, ais, tree, node, tree.containerDecl(node), space),
514540
515 const op_space = switch (base.tag) {
516 .Period, .ErrorUnion, .Range => Space.None,
517 else => Space.Space,
518 };
519 try renderExpression(allocator, ais, tree, infix_op_node.lhs, op_space);
541 .container_decl_two, .container_decl_two_trailing => {
542 var buffer: [2]ast.Node.Index = undefined;
543 return renderContainerDecl(gpa, ais, tree, node, tree.containerDeclTwo(&buffer, node), space);
544 },
545 .container_decl_arg,
546 .container_decl_arg_trailing,
547 => return renderContainerDecl(gpa, ais, tree, node, tree.containerDeclArg(node), space),
520548
521 const after_op_space = blk: {
522 const loc = tree.tokenLocation(tree.token_locs[infix_op_node.op_token].end, tree.nextToken(infix_op_node.op_token));
523 break :blk if (loc.line == 0) op_space else Space.Newline;
524 };
549 .tagged_union,
550 .tagged_union_trailing,
551 => return renderContainerDecl(gpa, ais, tree, node, tree.taggedUnion(node), space),
525552
526 {
527 ais.pushIndent();
528 defer ais.popIndent();
529 try renderToken(tree, ais, infix_op_node.op_token, after_op_space);
553 .tagged_union_two, .tagged_union_two_trailing => {
554 var buffer: [2]ast.Node.Index = undefined;
555 return renderContainerDecl(gpa, ais, tree, node, tree.taggedUnionTwo(&buffer, node), space);
556 },
557 .tagged_union_enum_tag,
558 .tagged_union_enum_tag_trailing,
559 => return renderContainerDecl(gpa, ais, tree, node, tree.taggedUnionEnumTag(node), space),
560
561 .error_set_decl => {
562 const error_token = main_tokens[node];
563 const lbrace = error_token + 1;
564 const rbrace = datas[node].rhs;
565
566 try renderToken(ais, tree, error_token, .none);
567
568 if (lbrace + 1 == rbrace) {
569 // There is nothing between the braces so render condensed: `error{}`
570 try renderToken(ais, tree, lbrace, .none);
571 return renderToken(ais, tree, rbrace, space);
572 } else if (lbrace + 2 == rbrace and token_tags[lbrace + 1] == .identifier) {
573 // There is exactly one member and no trailing comma or
574 // comments, so render without surrounding spaces: `error{Foo}`
575 try renderToken(ais, tree, lbrace, .none);
576 try renderToken(ais, tree, lbrace + 1, .none); // identifier
577 return renderToken(ais, tree, rbrace, space);
578 } else if (token_tags[rbrace - 1] == .comma) {
579 // There is a trailing comma so render each member on a new line.
580 ais.pushIndentNextLine();
581 try renderToken(ais, tree, lbrace, .newline);
582 var i = lbrace + 1;
583 while (i < rbrace) : (i += 1) {
584 if (i > lbrace + 1) try renderExtraNewlineToken(ais, tree, i);
585 switch (token_tags[i]) {
586 .doc_comment => try renderToken(ais, tree, i, .newline),
587 .identifier => try renderToken(ais, tree, i, .comma),
588 .comma => {},
589 else => unreachable,
590 }
591 }
592 ais.popIndent();
593 return renderToken(ais, tree, rbrace, space);
594 } else {
595 // There is no trailing comma so render everything on one line.
596 try renderToken(ais, tree, lbrace, .space);
597 var i = lbrace + 1;
598 while (i < rbrace) : (i += 1) {
599 switch (token_tags[i]) {
600 .doc_comment => unreachable, // TODO
601 .identifier => try renderToken(ais, tree, i, .comma_space),
602 .comma => {},
603 else => unreachable,
604 }
605 }
606 return renderToken(ais, tree, rbrace, space);
530607 }
531 ais.pushIndentOneShot();
532 return renderExpression(allocator, ais, tree, infix_op_node.rhs, space);
533608 },
534609
535 .BitNot,
536 .BoolNot,
537 .Negation,
538 .NegationWrap,
539 .OptionalType,
540 .AddressOf,
541 => {
542 const casted_node = @fieldParentPtr(ast.Node.SimplePrefixOp, "base", base);
543 try renderToken(tree, ais, casted_node.op_token, Space.None);
544 return renderExpression(allocator, ais, tree, casted_node.rhs, space);
610 .builtin_call_two, .builtin_call_two_comma => {
611 if (datas[node].lhs == 0) {
612 return renderBuiltinCall(gpa, ais, tree, main_tokens[node], &.{}, space);
613 } else if (datas[node].rhs == 0) {
614 return renderBuiltinCall(gpa, ais, tree, main_tokens[node], &.{datas[node].lhs}, space);
615 } else {
616 return renderBuiltinCall(gpa, ais, tree, main_tokens[node], &.{ datas[node].lhs, datas[node].rhs }, space);
617 }
545618 },
546
547 .Try,
548 .Resume,
549 .Await,
550 => {
551 const casted_node = @fieldParentPtr(ast.Node.SimplePrefixOp, "base", base);
552 try renderToken(tree, ais, casted_node.op_token, Space.Space);
553 return renderExpression(allocator, ais, tree, casted_node.rhs, space);
619 .builtin_call, .builtin_call_comma => {
620 const params = tree.extra_data[datas[node].lhs..datas[node].rhs];
621 return renderBuiltinCall(gpa, ais, tree, main_tokens[node], params, space);
554622 },
555623
556 .ArrayType => {
557 const array_type = @fieldParentPtr(ast.Node.ArrayType, "base", base);
558 return renderArrayType(
559 allocator,
560 ais,
561 tree,
562 array_type.op_token,
563 array_type.rhs,
564 array_type.len_expr,
565 null,
566 space,
567 );
624 .fn_proto_simple => {
625 var params: [1]ast.Node.Index = undefined;
626 return renderFnProto(gpa, ais, tree, tree.fnProtoSimple(&params, node), space);
568627 },
569 .ArrayTypeSentinel => {
570 const array_type = @fieldParentPtr(ast.Node.ArrayTypeSentinel, "base", base);
571 return renderArrayType(
572 allocator,
573 ais,
574 tree,
575 array_type.op_token,
576 array_type.rhs,
577 array_type.len_expr,
578 array_type.sentinel,
579 space,
580 );
628 .fn_proto_multi => return renderFnProto(gpa, ais, tree, tree.fnProtoMulti(node), space),
629 .fn_proto_one => {
630 var params: [1]ast.Node.Index = undefined;
631 return renderFnProto(gpa, ais, tree, tree.fnProtoOne(&params, node), space);
581632 },
582
583 .PtrType => {
584 const ptr_type = @fieldParentPtr(ast.Node.PtrType, "base", base);
585 const op_tok_id = tree.token_ids[ptr_type.op_token];
586 switch (op_tok_id) {
587 .Asterisk, .AsteriskAsterisk => try ais.writer().writeByte('*'),
588 .LBracket => if (tree.token_ids[ptr_type.op_token + 2] == .Identifier)
589 try ais.writer().writeAll("[*c")
590 else
591 try ais.writer().writeAll("[*"),
592 else => unreachable,
593 }
594 if (ptr_type.ptr_info.sentinel) |sentinel| {
595 const colon_token = tree.prevToken(sentinel.firstToken());
596 try renderToken(tree, ais, colon_token, Space.None); // :
597 const sentinel_space = switch (op_tok_id) {
598 .LBracket => Space.None,
599 else => Space.Space,
600 };
601 try renderExpression(allocator, ais, tree, sentinel, sentinel_space);
602 }
603 switch (op_tok_id) {
604 .Asterisk, .AsteriskAsterisk => {},
605 .LBracket => try ais.writer().writeByte(']'),
606 else => unreachable,
607 }
608 if (ptr_type.ptr_info.allowzero_token) |allowzero_token| {
609 try renderToken(tree, ais, allowzero_token, Space.Space); // allowzero
610 }
611 if (ptr_type.ptr_info.align_info) |align_info| {
612 const lparen_token = tree.prevToken(align_info.node.firstToken());
613 const align_token = tree.prevToken(lparen_token);
614
615 try renderToken(tree, ais, align_token, Space.None); // align
616 try renderToken(tree, ais, lparen_token, Space.None); // (
617
618 try renderExpression(allocator, ais, tree, align_info.node, Space.None);
619
620 if (align_info.bit_range) |bit_range| {
621 const colon1 = tree.prevToken(bit_range.start.firstToken());
622 const colon2 = tree.prevToken(bit_range.end.firstToken());
623
624 try renderToken(tree, ais, colon1, Space.None); // :
625 try renderExpression(allocator, ais, tree, bit_range.start, Space.None);
626 try renderToken(tree, ais, colon2, Space.None); // :
627 try renderExpression(allocator, ais, tree, bit_range.end, Space.None);
628
629 const rparen_token = tree.nextToken(bit_range.end.lastToken());
630 try renderToken(tree, ais, rparen_token, Space.Space); // )
631 } else {
632 const rparen_token = tree.nextToken(align_info.node.lastToken());
633 try renderToken(tree, ais, rparen_token, Space.Space); // )
634 }
635 }
636 if (ptr_type.ptr_info.const_token) |const_token| {
637 try renderToken(tree, ais, const_token, Space.Space); // const
638 }
639 if (ptr_type.ptr_info.volatile_token) |volatile_token| {
640 try renderToken(tree, ais, volatile_token, Space.Space); // volatile
633 .fn_proto => return renderFnProto(gpa, ais, tree, tree.fnProto(node), space),
634
635 .anyframe_type => {
636 const main_token = main_tokens[node];
637 if (datas[node].rhs != 0) {
638 try renderToken(ais, tree, main_token, .none); // anyframe
639 try renderToken(ais, tree, main_token + 1, .none); // ->
640 return renderExpression(gpa, ais, tree, datas[node].rhs, space);
641 } else {
642 return renderToken(ais, tree, main_token, space); // anyframe
641643 }
642 return renderExpression(allocator, ais, tree, ptr_type.rhs, space);
643644 },
644645
645 .SliceType => {
646 const slice_type = @fieldParentPtr(ast.Node.SliceType, "base", base);
647 try renderToken(tree, ais, slice_type.op_token, Space.None); // [
648 if (slice_type.ptr_info.sentinel) |sentinel| {
649 const colon_token = tree.prevToken(sentinel.firstToken());
650 try renderToken(tree, ais, colon_token, Space.None); // :
651 try renderExpression(allocator, ais, tree, sentinel, Space.None);
652 try renderToken(tree, ais, tree.nextToken(sentinel.lastToken()), Space.None); // ]
646 .@"switch",
647 .switch_comma,
648 => {
649 const switch_token = main_tokens[node];
650 const condition = datas[node].lhs;
651 const extra = tree.extraData(datas[node].rhs, ast.Node.SubRange);
652 const cases = tree.extra_data[extra.start..extra.end];
653 const rparen = tree.lastToken(condition) + 1;
654
655 try renderToken(ais, tree, switch_token, .space); // switch keyword
656 try renderToken(ais, tree, switch_token + 1, .none); // lparen
657 try renderExpression(gpa, ais, tree, condition, .none); // condtion expression
658 try renderToken(ais, tree, rparen, .space); // rparen
659
660 ais.pushIndentNextLine();
661 if (cases.len == 0) {
662 try renderToken(ais, tree, rparen + 1, .none); // lbrace
653663 } else {
654 try renderToken(tree, ais, tree.nextToken(slice_type.op_token), Space.None); // ]
664 try renderToken(ais, tree, rparen + 1, .newline); // lbrace
665 try renderExpressions(gpa, ais, tree, cases, .comma);
655666 }
667 ais.popIndent();
668 return renderToken(ais, tree, tree.lastToken(node), space); // rbrace
669 },
656670
657 if (slice_type.ptr_info.allowzero_token) |allowzero_token| {
658 try renderToken(tree, ais, allowzero_token, Space.Space); // allowzero
659 }
660 if (slice_type.ptr_info.align_info) |align_info| {
661 const lparen_token = tree.prevToken(align_info.node.firstToken());
662 const align_token = tree.prevToken(lparen_token);
671 .switch_case_one => return renderSwitchCase(gpa, ais, tree, tree.switchCaseOne(node), space),
672 .switch_case => return renderSwitchCase(gpa, ais, tree, tree.switchCase(node), space),
663673
664 try renderToken(tree, ais, align_token, Space.None); // align
665 try renderToken(tree, ais, lparen_token, Space.None); // (
674 .while_simple => return renderWhile(gpa, ais, tree, tree.whileSimple(node), space),
675 .while_cont => return renderWhile(gpa, ais, tree, tree.whileCont(node), space),
676 .@"while" => return renderWhile(gpa, ais, tree, tree.whileFull(node), space),
677 .for_simple => return renderWhile(gpa, ais, tree, tree.forSimple(node), space),
678 .@"for" => return renderWhile(gpa, ais, tree, tree.forFull(node), space),
666679
667 try renderExpression(allocator, ais, tree, align_info.node, Space.None);
680 .if_simple => return renderIf(gpa, ais, tree, tree.ifSimple(node), space),
681 .@"if" => return renderIf(gpa, ais, tree, tree.ifFull(node), space),
668682
669 if (align_info.bit_range) |bit_range| {
670 const colon1 = tree.prevToken(bit_range.start.firstToken());
671 const colon2 = tree.prevToken(bit_range.end.firstToken());
683 .asm_simple => return renderAsm(gpa, ais, tree, tree.asmSimple(node), space),
684 .@"asm" => return renderAsm(gpa, ais, tree, tree.asmFull(node), space),
672685
673 try renderToken(tree, ais, colon1, Space.None); // :
674 try renderExpression(allocator, ais, tree, bit_range.start, Space.None);
675 try renderToken(tree, ais, colon2, Space.None); // :
676 try renderExpression(allocator, ais, tree, bit_range.end, Space.None);
686 .enum_literal => {
687 try renderToken(ais, tree, main_tokens[node] - 1, .none); // .
688 return renderToken(ais, tree, main_tokens[node], space); // name
689 },
677690
678 const rparen_token = tree.nextToken(bit_range.end.lastToken());
679 try renderToken(tree, ais, rparen_token, Space.Space); // )
680 } else {
681 const rparen_token = tree.nextToken(align_info.node.lastToken());
682 try renderToken(tree, ais, rparen_token, Space.Space); // )
683 }
691 .fn_decl => unreachable,
692 .container_field => unreachable,
693 .container_field_init => unreachable,
694 .container_field_align => unreachable,
695 .root => unreachable,
696 .global_var_decl => unreachable,
697 .local_var_decl => unreachable,
698 .simple_var_decl => unreachable,
699 .aligned_var_decl => unreachable,
700 .@"usingnamespace" => unreachable,
701 .test_decl => unreachable,
702 .asm_output => unreachable,
703 .asm_input => unreachable,
704 }
705}
706
707fn renderArrayType(
708 gpa: *Allocator,
709 ais: *Ais,
710 tree: ast.Tree,
711 array_type: ast.full.ArrayType,
712 space: Space,
713) Error!void {
714 const rbracket = tree.firstToken(array_type.ast.elem_type) - 1;
715 const one_line = tree.tokensOnSameLine(array_type.ast.lbracket, rbracket);
716 const inner_space = if (one_line) Space.none else Space.newline;
717 ais.pushIndentNextLine();
718 try renderToken(ais, tree, array_type.ast.lbracket, inner_space); // lbracket
719 try renderExpression(gpa, ais, tree, array_type.ast.elem_count, inner_space);
720 if (array_type.ast.sentinel) |sentinel| {
721 try renderToken(ais, tree, tree.firstToken(sentinel) - 1, inner_space); // colon
722 try renderExpression(gpa, ais, tree, sentinel, inner_space);
723 }
724 ais.popIndent();
725 try renderToken(ais, tree, rbracket, .none); // rbracket
726 return renderExpression(gpa, ais, tree, array_type.ast.elem_type, space);
727}
728
729fn renderPtrType(
730 gpa: *Allocator,
731 ais: *Ais,
732 tree: ast.Tree,
733 ptr_type: ast.full.PtrType,
734 space: Space,
735) Error!void {
736 switch (ptr_type.size) {
737 .One => {
738 // Since ** tokens exist and the same token is shared by two
739 // nested pointer types, we check to see if we are the parent
740 // in such a relationship. If so, skip rendering anything for
741 // this pointer type and rely on the child to render our asterisk
742 // as well when it renders the ** token.
743 if (tree.tokens.items(.tag)[ptr_type.ast.main_token] == .asterisk_asterisk and
744 ptr_type.ast.main_token == tree.nodes.items(.main_token)[ptr_type.ast.child_type])
745 {
746 return renderExpression(gpa, ais, tree, ptr_type.ast.child_type, space);
684747 }
685 if (slice_type.ptr_info.const_token) |const_token| {
686 try renderToken(tree, ais, const_token, Space.Space);
748 try renderToken(ais, tree, ptr_type.ast.main_token, .none); // asterisk
749 },
750 .Many => {
751 if (ptr_type.ast.sentinel == 0) {
752 try renderToken(ais, tree, ptr_type.ast.main_token - 1, .none); // lbracket
753 try renderToken(ais, tree, ptr_type.ast.main_token, .none); // asterisk
754 try renderToken(ais, tree, ptr_type.ast.main_token + 1, .none); // rbracket
755 } else {
756 try renderToken(ais, tree, ptr_type.ast.main_token - 1, .none); // lbracket
757 try renderToken(ais, tree, ptr_type.ast.main_token, .none); // asterisk
758 try renderToken(ais, tree, ptr_type.ast.main_token + 1, .none); // colon
759 try renderExpression(gpa, ais, tree, ptr_type.ast.sentinel, .none);
760 try renderToken(ais, tree, tree.lastToken(ptr_type.ast.sentinel) + 1, .none); // rbracket
687761 }
688 if (slice_type.ptr_info.volatile_token) |volatile_token| {
689 try renderToken(tree, ais, volatile_token, Space.Space);
762 },
763 .C => {
764 try renderToken(ais, tree, ptr_type.ast.main_token - 1, .none); // lbracket
765 try renderToken(ais, tree, ptr_type.ast.main_token, .none); // asterisk
766 try renderToken(ais, tree, ptr_type.ast.main_token + 1, .none); // c
767 try renderToken(ais, tree, ptr_type.ast.main_token + 2, .none); // rbracket
768 },
769 .Slice => {
770 if (ptr_type.ast.sentinel == 0) {
771 try renderToken(ais, tree, ptr_type.ast.main_token, .none); // lbracket
772 try renderToken(ais, tree, ptr_type.ast.main_token + 1, .none); // rbracket
773 } else {
774 try renderToken(ais, tree, ptr_type.ast.main_token, .none); // lbracket
775 try renderToken(ais, tree, ptr_type.ast.main_token + 1, .none); // colon
776 try renderExpression(gpa, ais, tree, ptr_type.ast.sentinel, .none);
777 try renderToken(ais, tree, tree.lastToken(ptr_type.ast.sentinel) + 1, .none); // rbracket
690778 }
691 return renderExpression(allocator, ais, tree, slice_type.rhs, space);
692779 },
780 }
693781
694 .ArrayInitializer, .ArrayInitializerDot => {
695 var rtoken: ast.TokenIndex = undefined;
696 var exprs: []*ast.Node = undefined;
697 const lhs: union(enum) { dot: ast.TokenIndex, node: *ast.Node } = switch (base.tag) {
698 .ArrayInitializerDot => blk: {
699 const casted = @fieldParentPtr(ast.Node.ArrayInitializerDot, "base", base);
700 rtoken = casted.rtoken;
701 exprs = casted.list();
702 break :blk .{ .dot = casted.dot };
703 },
704 .ArrayInitializer => blk: {
705 const casted = @fieldParentPtr(ast.Node.ArrayInitializer, "base", base);
706 rtoken = casted.rtoken;
707 exprs = casted.list();
708 break :blk .{ .node = casted.lhs };
709 },
710 else => unreachable,
711 };
712
713 const lbrace = switch (lhs) {
714 .dot => |dot| tree.nextToken(dot),
715 .node => |node| tree.nextToken(node.lastToken()),
716 };
782 if (ptr_type.allowzero_token) |allowzero_token| {
783 try renderToken(ais, tree, allowzero_token, .space);
784 }
717785
718 switch (lhs) {
719 .dot => |dot| try renderToken(tree, ais, dot, Space.None),
720 .node => |node| try renderExpression(allocator, ais, tree, node, Space.None),
721 }
786 if (ptr_type.ast.align_node != 0) {
787 const align_first = tree.firstToken(ptr_type.ast.align_node);
788 try renderToken(ais, tree, align_first - 2, .none); // align
789 try renderToken(ais, tree, align_first - 1, .none); // lparen
790 try renderExpression(gpa, ais, tree, ptr_type.ast.align_node, .none);
791 if (ptr_type.ast.bit_range_start != 0) {
792 assert(ptr_type.ast.bit_range_end != 0);
793 try renderToken(ais, tree, tree.firstToken(ptr_type.ast.bit_range_start) - 1, .none); // colon
794 try renderExpression(gpa, ais, tree, ptr_type.ast.bit_range_start, .none);
795 try renderToken(ais, tree, tree.firstToken(ptr_type.ast.bit_range_end) - 1, .none); // colon
796 try renderExpression(gpa, ais, tree, ptr_type.ast.bit_range_end, .none);
797 try renderToken(ais, tree, tree.lastToken(ptr_type.ast.bit_range_end) + 1, .space); // rparen
798 } else {
799 try renderToken(ais, tree, tree.lastToken(ptr_type.ast.align_node) + 1, .space); // rparen
800 }
801 }
722802
723 if (exprs.len == 0) {
724 try renderToken(tree, ais, lbrace, Space.None);
725 return renderToken(tree, ais, rtoken, space);
726 }
803 if (ptr_type.const_token) |const_token| {
804 try renderToken(ais, tree, const_token, .space);
805 }
727806
728 if (exprs.len == 1 and exprs[0].tag != .MultilineStringLiteral and tree.token_ids[exprs[0].*.lastToken() + 1] == .RBrace) {
729 const expr = exprs[0];
807 if (ptr_type.volatile_token) |volatile_token| {
808 try renderToken(ais, tree, volatile_token, .space);
809 }
730810
731 try renderToken(tree, ais, lbrace, Space.None);
732 try renderExpression(allocator, ais, tree, expr, Space.None);
733 return renderToken(tree, ais, rtoken, space);
734 }
811 try renderExpression(gpa, ais, tree, ptr_type.ast.child_type, space);
812}
735813
736 // scan to find row size
737 if (rowSize(tree, exprs, rtoken) != null) {
738 {
739 ais.pushIndentNextLine();
740 defer ais.popIndent();
741 try renderToken(tree, ais, lbrace, Space.Newline);
742
743 var expr_index: usize = 0;
744 while (rowSize(tree, exprs[expr_index..], rtoken)) |row_size| {
745 const row_exprs = exprs[expr_index..];
746 // A place to store the width of each expression and its column's maximum
747 var widths = try allocator.alloc(usize, row_exprs.len + row_size);
748 defer allocator.free(widths);
749 mem.set(usize, widths, 0);
750
751 var expr_newlines = try allocator.alloc(bool, row_exprs.len);
752 defer allocator.free(expr_newlines);
753 mem.set(bool, expr_newlines, false);
754
755 var expr_widths = widths[0 .. widths.len - row_size];
756 var column_widths = widths[widths.len - row_size ..];
757
758 // Find next row with trailing comment (if any) to end the current section
759 var section_end = sec_end: {
760 var this_line_first_expr: usize = 0;
761 var this_line_size = rowSize(tree, row_exprs, rtoken);
762 for (row_exprs) |expr, i| {
763 // Ignore comment on first line of this section
764 if (i == 0 or tree.tokensOnSameLine(row_exprs[0].firstToken(), expr.lastToken())) continue;
765 // Track start of line containing comment
766 if (!tree.tokensOnSameLine(row_exprs[this_line_first_expr].firstToken(), expr.lastToken())) {
767 this_line_first_expr = i;
768 this_line_size = rowSize(tree, row_exprs[this_line_first_expr..], rtoken);
769 }
770
771 const maybe_comma = expr.lastToken() + 1;
772 const maybe_comment = expr.lastToken() + 2;
773 if (maybe_comment < tree.token_ids.len) {
774 if (tree.token_ids[maybe_comma] == .Comma and
775 tree.token_ids[maybe_comment] == .LineComment and
776 tree.tokensOnSameLine(expr.lastToken(), maybe_comment))
777 {
778 var comment_token_loc = tree.token_locs[maybe_comment];
779 const comment_is_empty = mem.trimRight(u8, tree.tokenSliceLoc(comment_token_loc), " ").len == 2;
780 if (!comment_is_empty) {
781 // Found row ending in comment
782 break :sec_end i - this_line_size.? + 1;
783 }
784 }
785 }
786 }
787 break :sec_end row_exprs.len;
788 };
789 expr_index += section_end;
790
791 const section_exprs = row_exprs[0..section_end];
792
793 // Null stream for counting the printed length of each expression
794 var line_find_stream = std.io.findByteWriter('\n', std.io.null_writer);
795 var counting_stream = std.io.countingWriter(line_find_stream.writer());
796 var auto_indenting_stream = std.io.autoIndentingStream(indent_delta, counting_stream.writer());
797
798 // Calculate size of columns in current section
799 var column_counter: usize = 0;
800 var single_line = true;
801 for (section_exprs) |expr, i| {
802 if (i + 1 < section_exprs.len) {
803 counting_stream.bytes_written = 0;
804 line_find_stream.byte_found = false;
805 try renderExpression(allocator, &auto_indenting_stream, tree, expr, Space.None);
806 const width = @intCast(usize, counting_stream.bytes_written);
807 expr_widths[i] = width;
808 expr_newlines[i] = line_find_stream.byte_found;
809
810 if (!line_find_stream.byte_found) {
811 const column = column_counter % row_size;
812 column_widths[column] = std.math.max(column_widths[column], width);
813
814 const expr_last_token = expr.*.lastToken() + 1;
815 const next_expr = section_exprs[i + 1];
816 const loc = tree.tokenLocation(tree.token_locs[expr_last_token].start, next_expr.*.firstToken());
817
818 column_counter += 1;
819
820 if (loc.line != 0) single_line = false;
821 } else {
822 single_line = false;
823 column_counter = 0;
824 }
825 } else {
826 counting_stream.bytes_written = 0;
827 try renderExpression(allocator, &auto_indenting_stream, tree, expr, Space.None);
828 const width = @intCast(usize, counting_stream.bytes_written);
829 expr_widths[i] = width;
830 expr_newlines[i] = line_find_stream.byte_found;
831
832 if (!line_find_stream.byte_found) {
833 const column = column_counter % row_size;
834 column_widths[column] = std.math.max(column_widths[column], width);
835 }
836 break;
837 }
838 }
814fn renderSlice(
815 gpa: *Allocator,
816 ais: *Ais,
817 tree: ast.Tree,
818 slice: ast.full.Slice,
819 space: Space,
820) Error!void {
821 const node_tags = tree.nodes.items(.tag);
822 const after_start_space_bool = nodeCausesSliceOpSpace(node_tags[slice.ast.start]) or
823 if (slice.ast.end != 0) nodeCausesSliceOpSpace(node_tags[slice.ast.end]) else false;
824 const after_start_space = if (after_start_space_bool) Space.space else Space.none;
825 const after_dots_space = if (slice.ast.end != 0) after_start_space else Space.none;
826
827 try renderExpression(gpa, ais, tree, slice.ast.sliced, .none);
828 try renderToken(ais, tree, slice.ast.lbracket, .none); // lbracket
829
830 const start_last = tree.lastToken(slice.ast.start);
831 try renderExpression(gpa, ais, tree, slice.ast.start, after_start_space);
832 try renderToken(ais, tree, start_last + 1, after_dots_space); // ellipsis2 ("..")
833 if (slice.ast.end == 0) {
834 return renderToken(ais, tree, start_last + 2, space); // rbracket
835 }
839836
840 // Render exprs in current section
841 column_counter = 0;
842 var last_col_index: usize = row_size - 1;
843 for (section_exprs) |expr, i| {
844 if (i + 1 < section_exprs.len) {
845 const next_expr = section_exprs[i + 1];
846 try renderExpression(allocator, ais, tree, expr, Space.None);
847
848 const comma = tree.nextToken(expr.*.lastToken());
849
850 if (column_counter != last_col_index) {
851 if (!expr_newlines[i] and !expr_newlines[i + 1]) {
852 // Neither the current or next expression is multiline
853 try renderToken(tree, ais, comma, Space.Space); // ,
854 assert(column_widths[column_counter % row_size] >= expr_widths[i]);
855 const padding = column_widths[column_counter % row_size] - expr_widths[i];
856 try ais.writer().writeByteNTimes(' ', padding);
857
858 column_counter += 1;
859 continue;
860 }
861 }
862 if (single_line and row_size != 1) {
863 try renderToken(tree, ais, comma, Space.Space); // ,
864 continue;
865 }
866
867 column_counter = 0;
868 try renderToken(tree, ais, comma, Space.Newline); // ,
869 try renderExtraNewline(tree, ais, next_expr);
870 } else {
871 const maybe_comma = tree.nextToken(expr.*.lastToken());
872 if (tree.token_ids[maybe_comma] == .Comma) {
873 try renderExpression(allocator, ais, tree, expr, Space.None); // ,
874 try renderToken(tree, ais, maybe_comma, Space.Newline); // ,
875 } else {
876 try renderExpression(allocator, ais, tree, expr, Space.Comma); // ,
877 }
878 }
879 }
837 const end_last = tree.lastToken(slice.ast.end);
838 const after_end_space = if (slice.ast.sentinel != 0) Space.space else Space.none;
839 try renderExpression(gpa, ais, tree, slice.ast.end, after_end_space);
840 if (slice.ast.sentinel == 0) {
841 return renderToken(ais, tree, end_last + 1, space); // rbracket
842 }
880843
881 if (expr_index == exprs.len) {
882 break;
883 }
884 }
885 }
844 try renderToken(ais, tree, end_last + 1, .none); // colon
845 try renderExpression(gpa, ais, tree, slice.ast.sentinel, .none);
846 try renderToken(ais, tree, tree.lastToken(slice.ast.sentinel) + 1, space); // rbracket
847}
886848
887 return renderToken(tree, ais, rtoken, space);
888 }
849fn renderAsmOutput(
850 gpa: *Allocator,
851 ais: *Ais,
852 tree: ast.Tree,
853 asm_output: ast.Node.Index,
854 space: Space,
855) Error!void {
856 const token_tags = tree.tokens.items(.tag);
857 const node_tags = tree.nodes.items(.tag);
858 const main_tokens = tree.nodes.items(.main_token);
859 const datas = tree.nodes.items(.data);
860 assert(node_tags[asm_output] == .asm_output);
861 const symbolic_name = main_tokens[asm_output];
862
863 try renderToken(ais, tree, symbolic_name - 1, .none); // lbracket
864 try renderToken(ais, tree, symbolic_name, .none); // ident
865 try renderToken(ais, tree, symbolic_name + 1, .space); // rbracket
866 try renderToken(ais, tree, symbolic_name + 2, .space); // "constraint"
867 try renderToken(ais, tree, symbolic_name + 3, .none); // lparen
868
869 if (token_tags[symbolic_name + 4] == .arrow) {
870 try renderToken(ais, tree, symbolic_name + 4, .space); // ->
871 try renderExpression(gpa, ais, tree, datas[asm_output].lhs, Space.none);
872 return renderToken(ais, tree, datas[asm_output].rhs, space); // rparen
873 } else {
874 try renderToken(ais, tree, symbolic_name + 4, .none); // ident
875 return renderToken(ais, tree, symbolic_name + 5, space); // rparen
876 }
877}
889878
890 // Single line
891 try renderToken(tree, ais, lbrace, Space.Space);
892 for (exprs) |expr, i| {
893 if (i + 1 < exprs.len) {
894 const next_expr = exprs[i + 1];
895 try renderExpression(allocator, ais, tree, expr, Space.None);
896 const comma = tree.nextToken(expr.*.lastToken());
897 try renderToken(tree, ais, comma, Space.Space); // ,
898 } else {
899 try renderExpression(allocator, ais, tree, expr, Space.Space);
900 }
901 }
879fn renderAsmInput(
880 gpa: *Allocator,
881 ais: *Ais,
882 tree: ast.Tree,
883 asm_input: ast.Node.Index,
884 space: Space,
885) Error!void {
886 const node_tags = tree.nodes.items(.tag);
887 const main_tokens = tree.nodes.items(.main_token);
888 const datas = tree.nodes.items(.data);
889 assert(node_tags[asm_input] == .asm_input);
890 const symbolic_name = main_tokens[asm_input];
891
892 try renderToken(ais, tree, symbolic_name - 1, .none); // lbracket
893 try renderToken(ais, tree, symbolic_name, .none); // ident
894 try renderToken(ais, tree, symbolic_name + 1, .space); // rbracket
895 try renderToken(ais, tree, symbolic_name + 2, .space); // "constraint"
896 try renderToken(ais, tree, symbolic_name + 3, .none); // lparen
897 try renderExpression(gpa, ais, tree, datas[asm_input].lhs, Space.none);
898 return renderToken(ais, tree, datas[asm_input].rhs, space); // rparen
899}
902900
903 return renderToken(tree, ais, rtoken, space);
904 },
901fn renderVarDecl(gpa: *Allocator, ais: *Ais, tree: ast.Tree, var_decl: ast.full.VarDecl) Error!void {
902 if (var_decl.visib_token) |visib_token| {
903 try renderToken(ais, tree, visib_token, Space.space); // pub
904 }
905905
906 .StructInitializer, .StructInitializerDot => {
907 var rtoken: ast.TokenIndex = undefined;
908 var field_inits: []*ast.Node = undefined;
909 const lhs: union(enum) { dot: ast.TokenIndex, node: *ast.Node } = switch (base.tag) {
910 .StructInitializerDot => blk: {
911 const casted = @fieldParentPtr(ast.Node.StructInitializerDot, "base", base);
912 rtoken = casted.rtoken;
913 field_inits = casted.list();
914 break :blk .{ .dot = casted.dot };
915 },
916 .StructInitializer => blk: {
917 const casted = @fieldParentPtr(ast.Node.StructInitializer, "base", base);
918 rtoken = casted.rtoken;
919 field_inits = casted.list();
920 break :blk .{ .node = casted.lhs };
921 },
922 else => unreachable,
923 };
906 if (var_decl.extern_export_token) |extern_export_token| {
907 try renderToken(ais, tree, extern_export_token, Space.space); // extern
924908
925 const lbrace = switch (lhs) {
926 .dot => |dot| tree.nextToken(dot),
927 .node => |node| tree.nextToken(node.lastToken()),
928 };
909 if (var_decl.lib_name) |lib_name| {
910 try renderToken(ais, tree, lib_name, Space.space); // "lib"
911 }
912 }
929913
930 if (field_inits.len == 0) {
931 switch (lhs) {
932 .dot => |dot| try renderToken(tree, ais, dot, Space.None),
933 .node => |node| try renderExpression(allocator, ais, tree, node, Space.None),
934 }
914 if (var_decl.threadlocal_token) |thread_local_token| {
915 try renderToken(ais, tree, thread_local_token, Space.space); // threadlocal
916 }
935917
936 {
937 ais.pushIndentNextLine();
938 defer ais.popIndent();
939 try renderToken(tree, ais, lbrace, Space.None);
940 }
918 if (var_decl.comptime_token) |comptime_token| {
919 try renderToken(ais, tree, comptime_token, Space.space); // comptime
920 }
941921
942 return renderToken(tree, ais, rtoken, space);
943 }
922 try renderToken(ais, tree, var_decl.ast.mut_token, .space); // var
944923
945 const src_has_trailing_comma = blk: {
946 const maybe_comma = tree.prevToken(rtoken);
947 break :blk tree.token_ids[maybe_comma] == .Comma;
948 };
924 const name_space = if (var_decl.ast.type_node == 0 and
925 (var_decl.ast.align_node != 0 or
926 var_decl.ast.section_node != 0 or
927 var_decl.ast.init_node != 0))
928 Space.space
929 else
930 Space.none;
931 try renderToken(ais, tree, var_decl.ast.mut_token + 1, name_space); // name
949932
950 const src_same_line = blk: {
951 const loc = tree.tokenLocation(tree.token_locs[lbrace].end, rtoken);
952 break :blk loc.line == 0;
953 };
933 if (var_decl.ast.type_node != 0) {
934 try renderToken(ais, tree, var_decl.ast.mut_token + 2, Space.space); // :
935 if (var_decl.ast.align_node != 0 or var_decl.ast.section_node != 0 or
936 var_decl.ast.init_node != 0)
937 {
938 try renderExpression(gpa, ais, tree, var_decl.ast.type_node, .space);
939 } else {
940 try renderExpression(gpa, ais, tree, var_decl.ast.type_node, .none);
941 const semicolon = tree.lastToken(var_decl.ast.type_node) + 1;
942 return renderToken(ais, tree, semicolon, Space.newline); // ;
943 }
944 }
954945
955 const expr_outputs_one_line = blk: {
956 // render field expressions until a LF is found
957 for (field_inits) |field_init| {
958 var find_stream = std.io.findByteWriter('\n', std.io.null_writer);
959 var auto_indenting_stream = std.io.autoIndentingStream(indent_delta, find_stream.writer());
946 if (var_decl.ast.align_node != 0) {
947 const lparen = tree.firstToken(var_decl.ast.align_node) - 1;
948 const align_kw = lparen - 1;
949 const rparen = tree.lastToken(var_decl.ast.align_node) + 1;
950 try renderToken(ais, tree, align_kw, Space.none); // align
951 try renderToken(ais, tree, lparen, Space.none); // (
952 try renderExpression(gpa, ais, tree, var_decl.ast.align_node, Space.none);
953 if (var_decl.ast.section_node != 0 or var_decl.ast.init_node != 0) {
954 try renderToken(ais, tree, rparen, .space); // )
955 } else {
956 try renderToken(ais, tree, rparen, .none); // )
957 return renderToken(ais, tree, rparen + 1, Space.newline); // ;
958 }
959 }
960960
961 try renderExpression(allocator, &auto_indenting_stream, tree, field_init, Space.None);
962 if (find_stream.byte_found) break :blk false;
963 }
964 break :blk true;
965 };
961 if (var_decl.ast.section_node != 0) {
962 const lparen = tree.firstToken(var_decl.ast.section_node) - 1;
963 const section_kw = lparen - 1;
964 const rparen = tree.lastToken(var_decl.ast.section_node) + 1;
965 try renderToken(ais, tree, section_kw, Space.none); // linksection
966 try renderToken(ais, tree, lparen, Space.none); // (
967 try renderExpression(gpa, ais, tree, var_decl.ast.section_node, Space.none);
968 if (var_decl.ast.init_node != 0) {
969 try renderToken(ais, tree, rparen, .space); // )
970 } else {
971 try renderToken(ais, tree, rparen, .none); // )
972 return renderToken(ais, tree, rparen + 1, Space.newline); // ;
973 }
974 }
966975
967 if (field_inits.len == 1) blk: {
968 if (field_inits[0].cast(ast.Node.FieldInitializer)) |field_init| {
969 switch (field_init.expr.tag) {
970 .StructInitializer,
971 .StructInitializerDot,
972 => break :blk,
973 else => {},
974 }
975 }
976 assert(var_decl.ast.init_node != 0);
977 const eq_token = tree.firstToken(var_decl.ast.init_node) - 1;
978 const eq_space: Space = if (tree.tokensOnSameLine(eq_token, eq_token + 1)) .space else .newline;
979 {
980 ais.pushIndent();
981 try renderToken(ais, tree, eq_token, eq_space); // =
982 ais.popIndent();
983 }
984 ais.pushIndentOneShot();
985 try renderExpression(gpa, ais, tree, var_decl.ast.init_node, .semicolon);
986}
976987
977 // if the expression outputs to multiline, make this struct multiline
978 if (!expr_outputs_one_line or src_has_trailing_comma) {
979 break :blk;
980 }
988fn renderIf(gpa: *Allocator, ais: *Ais, tree: ast.Tree, if_node: ast.full.If, space: Space) Error!void {
989 return renderWhile(gpa, ais, tree, .{
990 .ast = .{
991 .while_token = if_node.ast.if_token,
992 .cond_expr = if_node.ast.cond_expr,
993 .cont_expr = 0,
994 .then_expr = if_node.ast.then_expr,
995 .else_expr = if_node.ast.else_expr,
996 },
997 .inline_token = null,
998 .label_token = null,
999 .payload_token = if_node.payload_token,
1000 .else_token = if_node.else_token,
1001 .error_token = if_node.error_token,
1002 }, space);
1003}
9811004
982 switch (lhs) {
983 .dot => |dot| try renderToken(tree, ais, dot, Space.None),
984 .node => |node| try renderExpression(allocator, ais, tree, node, Space.None),
985 }
986 try renderToken(tree, ais, lbrace, Space.Space);
987 try renderExpression(allocator, ais, tree, field_inits[0], Space.Space);
988 return renderToken(tree, ais, rtoken, space);
989 }
1005/// Note that this function is additionally used to render if and for expressions, with
1006/// respective values set to null.
1007fn renderWhile(gpa: *Allocator, ais: *Ais, tree: ast.Tree, while_node: ast.full.While, space: Space) Error!void {
1008 const node_tags = tree.nodes.items(.tag);
1009 const token_tags = tree.tokens.items(.tag);
9901010
991 if (!src_has_trailing_comma and src_same_line and expr_outputs_one_line) {
992 // render all on one line, no trailing comma
993 switch (lhs) {
994 .dot => |dot| try renderToken(tree, ais, dot, Space.None),
995 .node => |node| try renderExpression(allocator, ais, tree, node, Space.None),
996 }
997 try renderToken(tree, ais, lbrace, Space.Space);
1011 if (while_node.label_token) |label| {
1012 try renderToken(ais, tree, label, .none); // label
1013 try renderToken(ais, tree, label + 1, .space); // :
1014 }
9981015
999 for (field_inits) |field_init, i| {
1000 if (i + 1 < field_inits.len) {
1001 try renderExpression(allocator, ais, tree, field_init, Space.None);
1016 if (while_node.inline_token) |inline_token| {
1017 try renderToken(ais, tree, inline_token, .space); // inline
1018 }
10021019
1003 const comma = tree.nextToken(field_init.lastToken());
1004 try renderToken(tree, ais, comma, Space.Space);
1005 } else {
1006 try renderExpression(allocator, ais, tree, field_init, Space.Space);
1007 }
1020 try renderToken(ais, tree, while_node.ast.while_token, .space); // if
1021 try renderToken(ais, tree, while_node.ast.while_token + 1, .none); // lparen
1022 try renderExpression(gpa, ais, tree, while_node.ast.cond_expr, .none); // condition
1023
1024 const then_tag = node_tags[while_node.ast.then_expr];
1025 if (nodeIsBlock(then_tag) and !nodeIsIf(then_tag)) {
1026 if (while_node.payload_token) |payload_token| {
1027 try renderToken(ais, tree, payload_token - 2, .space); // rparen
1028 try renderToken(ais, tree, payload_token - 1, .none); // |
1029 const ident = blk: {
1030 if (token_tags[payload_token] == .asterisk) {
1031 try renderToken(ais, tree, payload_token, .none); // *
1032 break :blk payload_token + 1;
1033 } else {
1034 break :blk payload_token;
10081035 }
1009
1010 return renderToken(tree, ais, rtoken, space);
1011 }
1012
1013 {
1014 switch (lhs) {
1015 .dot => |dot| try renderToken(tree, ais, dot, Space.None),
1016 .node => |node| try renderExpression(allocator, ais, tree, node, Space.None),
1017 }
1018
1019 ais.pushIndentNextLine();
1020 defer ais.popIndent();
1021
1022 try renderToken(tree, ais, lbrace, Space.Newline);
1023
1024 for (field_inits) |field_init, i| {
1025 if (i + 1 < field_inits.len) {
1026 const next_field_init = field_inits[i + 1];
1027 try renderExpression(allocator, ais, tree, field_init, Space.None);
1028
1029 const comma = tree.nextToken(field_init.lastToken());
1030 try renderToken(tree, ais, comma, Space.Newline);
1031
1032 try renderExtraNewline(tree, ais, next_field_init);
1033 } else {
1034 try renderExpression(allocator, ais, tree, field_init, Space.Comma);
1035 }
1036 }
1037 }
1038
1039 return renderToken(tree, ais, rtoken, space);
1040 },
1041
1042 .Call => {
1043 const call = @fieldParentPtr(ast.Node.Call, "base", base);
1044 if (call.async_token) |async_token| {
1045 try renderToken(tree, ais, async_token, Space.Space);
1046 }
1047
1048 try renderExpression(allocator, ais, tree, call.lhs, Space.None);
1049
1050 const lparen = tree.nextToken(call.lhs.lastToken());
1051
1052 if (call.params_len == 0) {
1053 try renderToken(tree, ais, lparen, Space.None);
1054 return renderToken(tree, ais, call.rtoken, space);
1055 }
1056
1057 const src_has_trailing_comma = blk: {
1058 const maybe_comma = tree.prevToken(call.rtoken);
1059 break :blk tree.token_ids[maybe_comma] == .Comma;
10601036 };
1061
1062 if (src_has_trailing_comma) {
1063 {
1064 ais.pushIndent();
1065 defer ais.popIndent();
1066
1067 try renderToken(tree, ais, lparen, Space.Newline); // (
1068 const params = call.params();
1069 for (params) |param_node, i| {
1070 if (i + 1 < params.len) {
1071 const next_node = params[i + 1];
1072 try renderExpression(allocator, ais, tree, param_node, Space.None);
1073
1074 // Unindent the comma for multiline string literals
1075 const maybe_multiline_string = param_node.firstToken();
1076 const is_multiline_string = tree.token_ids[maybe_multiline_string] == .MultilineStringLiteralLine;
1077 if (is_multiline_string) ais.popIndent();
1078 defer if (is_multiline_string) ais.pushIndent();
1079
1080 const comma = tree.nextToken(param_node.lastToken());
1081 try renderToken(tree, ais, comma, Space.Newline); // ,
1082 try renderExtraNewline(tree, ais, next_node);
1083 } else {
1084 try renderExpression(allocator, ais, tree, param_node, Space.Comma);
1085 }
1086 }
1087 }
1088 return renderToken(tree, ais, call.rtoken, space);
1089 }
1090
1091 try renderToken(tree, ais, lparen, Space.None); // (
1092
1093 const params = call.params();
1094 for (params) |param_node, i| {
1095 const maybe_comment = param_node.firstToken() - 1;
1096 const maybe_multiline_string = param_node.firstToken();
1097 if (tree.token_ids[maybe_multiline_string] == .MultilineStringLiteralLine or tree.token_ids[maybe_comment] == .LineComment) {
1098 ais.pushIndentOneShot();
1099 }
1100
1101 try renderExpression(allocator, ais, tree, param_node, Space.None);
1102
1103 if (i + 1 < params.len) {
1104 const comma = tree.nextToken(param_node.lastToken());
1105 try renderToken(tree, ais, comma, Space.Space);
1106 }
1107 }
1108 return renderToken(tree, ais, call.rtoken, space); // )
1109 },
1110
1111 .ArrayAccess => {
1112 const suffix_op = base.castTag(.ArrayAccess).?;
1113
1114 const lbracket = tree.nextToken(suffix_op.lhs.lastToken());
1115 const rbracket = tree.nextToken(suffix_op.index_expr.lastToken());
1116
1117 try renderExpression(allocator, ais, tree, suffix_op.lhs, Space.None);
1118 try renderToken(tree, ais, lbracket, Space.None); // [
1119
1120 const starts_with_comment = tree.token_ids[lbracket + 1] == .LineComment;
1121 const ends_with_comment = tree.token_ids[rbracket - 1] == .LineComment;
1122 {
1123 const new_space = if (ends_with_comment) Space.Newline else Space.None;
1124
1125 ais.pushIndent();
1126 defer ais.popIndent();
1127 try renderExpression(allocator, ais, tree, suffix_op.index_expr, new_space);
1128 }
1129 if (starts_with_comment) try ais.maybeInsertNewline();
1130 return renderToken(tree, ais, rbracket, space); // ]
1131 },
1132
1133 .Slice => {
1134 const suffix_op = base.castTag(.Slice).?;
1135 try renderExpression(allocator, ais, tree, suffix_op.lhs, Space.None);
1136
1137 const lbracket = tree.prevToken(suffix_op.start.firstToken());
1138 const dotdot = tree.nextToken(suffix_op.start.lastToken());
1139
1140 const after_start_space_bool = nodeCausesSliceOpSpace(suffix_op.start) or
1141 (if (suffix_op.end) |end| nodeCausesSliceOpSpace(end) else false);
1142 const after_start_space = if (after_start_space_bool) Space.Space else Space.None;
1143 const after_op_space = if (suffix_op.end != null) after_start_space else Space.None;
1144
1145 try renderToken(tree, ais, lbracket, Space.None); // [
1146 try renderExpression(allocator, ais, tree, suffix_op.start, after_start_space);
1147 try renderToken(tree, ais, dotdot, after_op_space); // ..
1148 if (suffix_op.end) |end| {
1149 const after_end_space = if (suffix_op.sentinel != null) Space.Space else Space.None;
1150 try renderExpression(allocator, ais, tree, end, after_end_space);
1151 }
1152 if (suffix_op.sentinel) |sentinel| {
1153 const colon = tree.prevToken(sentinel.firstToken());
1154 try renderToken(tree, ais, colon, Space.None); // :
1155 try renderExpression(allocator, ais, tree, sentinel, Space.None);
1156 }
1157 return renderToken(tree, ais, suffix_op.rtoken, space); // ]
1158 },
1159
1160 .Deref => {
1161 const suffix_op = base.castTag(.Deref).?;
1162
1163 try renderExpression(allocator, ais, tree, suffix_op.lhs, Space.None);
1164 return renderToken(tree, ais, suffix_op.rtoken, space); // .*
1165 },
1166 .UnwrapOptional => {
1167 const suffix_op = base.castTag(.UnwrapOptional).?;
1168
1169 try renderExpression(allocator, ais, tree, suffix_op.lhs, Space.None);
1170 try renderToken(tree, ais, tree.prevToken(suffix_op.rtoken), Space.None); // .
1171 return renderToken(tree, ais, suffix_op.rtoken, space); // ?
1172 },
1173
1174 .Break => {
1175 const flow_expr = base.castTag(.Break).?;
1176 const maybe_rhs = flow_expr.getRHS();
1177 const maybe_label = flow_expr.getLabel();
1178
1179 if (maybe_label == null and maybe_rhs == null) {
1180 return renderToken(tree, ais, flow_expr.ltoken, space); // break
1181 }
1182
1183 try renderToken(tree, ais, flow_expr.ltoken, Space.Space); // break
1184 if (maybe_label) |label| {
1185 const colon = tree.nextToken(flow_expr.ltoken);
1186 try renderToken(tree, ais, colon, Space.None); // :
1187
1188 if (maybe_rhs == null) {
1189 return renderToken(tree, ais, label, space); // label
1190 }
1191 try renderToken(tree, ais, label, Space.Space); // label
1192 }
1193 return renderExpression(allocator, ais, tree, maybe_rhs.?, space);
1194 },
1195
1196 .Continue => {
1197 const flow_expr = base.castTag(.Continue).?;
1198 if (flow_expr.getLabel()) |label| {
1199 try renderToken(tree, ais, flow_expr.ltoken, Space.Space); // continue
1200 const colon = tree.nextToken(flow_expr.ltoken);
1201 try renderToken(tree, ais, colon, Space.None); // :
1202 return renderToken(tree, ais, label, space); // label
1203 } else {
1204 return renderToken(tree, ais, flow_expr.ltoken, space); // continue
1205 }
1206 },
1207
1208 .Return => {
1209 const flow_expr = base.castTag(.Return).?;
1210 if (flow_expr.getRHS()) |rhs| {
1211 try renderToken(tree, ais, flow_expr.ltoken, Space.Space);
1212 return renderExpression(allocator, ais, tree, rhs, space);
1213 } else {
1214 return renderToken(tree, ais, flow_expr.ltoken, space);
1215 }
1216 },
1217
1218 .Payload => {
1219 const payload = @fieldParentPtr(ast.Node.Payload, "base", base);
1220
1221 try renderToken(tree, ais, payload.lpipe, Space.None);
1222 try renderExpression(allocator, ais, tree, payload.error_symbol, Space.None);
1223 return renderToken(tree, ais, payload.rpipe, space);
1224 },
1225
1226 .PointerPayload => {
1227 const payload = @fieldParentPtr(ast.Node.PointerPayload, "base", base);
1228
1229 try renderToken(tree, ais, payload.lpipe, Space.None);
1230 if (payload.ptr_token) |ptr_token| {
1231 try renderToken(tree, ais, ptr_token, Space.None);
1232 }
1233 try renderExpression(allocator, ais, tree, payload.value_symbol, Space.None);
1234 return renderToken(tree, ais, payload.rpipe, space);
1235 },
1236
1237 .PointerIndexPayload => {
1238 const payload = @fieldParentPtr(ast.Node.PointerIndexPayload, "base", base);
1239
1240 try renderToken(tree, ais, payload.lpipe, Space.None);
1241 if (payload.ptr_token) |ptr_token| {
1242 try renderToken(tree, ais, ptr_token, Space.None);
1243 }
1244 try renderExpression(allocator, ais, tree, payload.value_symbol, Space.None);
1245
1246 if (payload.index_symbol) |index_symbol| {
1247 const comma = tree.nextToken(payload.value_symbol.lastToken());
1248
1249 try renderToken(tree, ais, comma, Space.Space);
1250 try renderExpression(allocator, ais, tree, index_symbol, Space.None);
1251 }
1252
1253 return renderToken(tree, ais, payload.rpipe, space);
1254 },
1255
1256 .GroupedExpression => {
1257 const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", base);
1258
1259 try renderToken(tree, ais, grouped_expr.lparen, Space.None);
1260 {
1261 ais.pushIndentOneShot();
1262 try renderExpression(allocator, ais, tree, grouped_expr.expr, Space.None);
1263 }
1264 return renderToken(tree, ais, grouped_expr.rparen, space);
1265 },
1266
1267 .FieldInitializer => {
1268 const field_init = @fieldParentPtr(ast.Node.FieldInitializer, "base", base);
1269
1270 try renderToken(tree, ais, field_init.period_token, Space.None); // .
1271 try renderToken(tree, ais, field_init.name_token, Space.Space); // name
1272 try renderToken(tree, ais, tree.nextToken(field_init.name_token), Space.Space); // =
1273 return renderExpression(allocator, ais, tree, field_init.expr, space);
1274 },
1275
1276 .ContainerDecl => {
1277 const container_decl = @fieldParentPtr(ast.Node.ContainerDecl, "base", base);
1278
1279 if (container_decl.layout_token) |layout_token| {
1280 try renderToken(tree, ais, layout_token, Space.Space);
1281 }
1282
1283 switch (container_decl.init_arg_expr) {
1284 .None => {
1285 try renderToken(tree, ais, container_decl.kind_token, Space.Space); // union
1286 },
1287 .Enum => |enum_tag_type| {
1288 try renderToken(tree, ais, container_decl.kind_token, Space.None); // union
1289
1290 const lparen = tree.nextToken(container_decl.kind_token);
1291 const enum_token = tree.nextToken(lparen);
1292
1293 try renderToken(tree, ais, lparen, Space.None); // (
1294 try renderToken(tree, ais, enum_token, Space.None); // enum
1295
1296 if (enum_tag_type) |expr| {
1297 try renderToken(tree, ais, tree.nextToken(enum_token), Space.None); // (
1298 try renderExpression(allocator, ais, tree, expr, Space.None);
1299
1300 const rparen = tree.nextToken(expr.lastToken());
1301 try renderToken(tree, ais, rparen, Space.None); // )
1302 try renderToken(tree, ais, tree.nextToken(rparen), Space.Space); // )
1303 } else {
1304 try renderToken(tree, ais, tree.nextToken(enum_token), Space.Space); // )
1305 }
1306 },
1307 .Type => |type_expr| {
1308 try renderToken(tree, ais, container_decl.kind_token, Space.None); // union
1309
1310 const lparen = tree.nextToken(container_decl.kind_token);
1311 const rparen = tree.nextToken(type_expr.lastToken());
1312
1313 try renderToken(tree, ais, lparen, Space.None); // (
1314 try renderExpression(allocator, ais, tree, type_expr, Space.None);
1315 try renderToken(tree, ais, rparen, Space.Space); // )
1316 },
1317 }
1318
1319 if (container_decl.fields_and_decls_len == 0) {
1320 {
1321 ais.pushIndentNextLine();
1322 defer ais.popIndent();
1323 try renderToken(tree, ais, container_decl.lbrace_token, Space.None); // {
1037 try renderToken(ais, tree, ident, .none); // identifier
1038 const pipe = blk: {
1039 if (token_tags[ident + 1] == .comma) {
1040 try renderToken(ais, tree, ident + 1, .space); // ,
1041 try renderToken(ais, tree, ident + 2, .none); // index
1042 break :blk ident + 3;
1043 } else {
1044 break :blk ident + 1;
13241045 }
1325 return renderToken(tree, ais, container_decl.rbrace_token, space); // }
1326 }
1327
1328 const src_has_trailing_comma = blk: {
1329 var maybe_comma = tree.prevToken(container_decl.lastToken());
1330 // Doc comments for a field may also appear after the comma, eg.
1331 // field_name: T, // comment attached to field_name
1332 if (tree.token_ids[maybe_comma] == .DocComment)
1333 maybe_comma = tree.prevToken(maybe_comma);
1334 break :blk tree.token_ids[maybe_comma] == .Comma;
13351046 };
1047 const brace_space = if (while_node.ast.cont_expr == 0 and ais.isLineOverIndented())
1048 Space.newline
1049 else
1050 Space.space;
1051 try renderToken(ais, tree, pipe, brace_space); // |
1052 } else {
1053 const rparen = tree.lastToken(while_node.ast.cond_expr) + 1;
1054 const brace_space = if (while_node.ast.cont_expr == 0 and ais.isLineOverIndented())
1055 Space.newline
1056 else
1057 Space.space;
1058 try renderToken(ais, tree, rparen, brace_space); // rparen
1059 }
1060 if (while_node.ast.cont_expr != 0) {
1061 const rparen = tree.lastToken(while_node.ast.cont_expr) + 1;
1062 const lparen = tree.firstToken(while_node.ast.cont_expr) - 1;
1063 try renderToken(ais, tree, lparen - 1, .space); // :
1064 try renderToken(ais, tree, lparen, .none); // lparen
1065 try renderExpression(gpa, ais, tree, while_node.ast.cont_expr, .none);
1066 const brace_space: Space = if (ais.isLineOverIndented()) .newline else .space;
1067 try renderToken(ais, tree, rparen, brace_space); // rparen
1068 }
1069 if (while_node.ast.else_expr != 0) {
1070 try renderExpression(gpa, ais, tree, while_node.ast.then_expr, Space.space);
1071 try renderToken(ais, tree, while_node.else_token, .space); // else
1072 if (while_node.error_token) |error_token| {
1073 try renderToken(ais, tree, error_token - 1, .none); // |
1074 try renderToken(ais, tree, error_token, .none); // identifier
1075 try renderToken(ais, tree, error_token + 1, .space); // |
1076 }
1077 return renderExpression(gpa, ais, tree, while_node.ast.else_expr, space);
1078 } else {
1079 return renderExpression(gpa, ais, tree, while_node.ast.then_expr, space);
1080 }
1081 }
13361082
1337 const fields_and_decls = container_decl.fieldsAndDecls();
1338
1339 // Check if the first declaration and the { are on the same line
1340 const src_has_newline = !tree.tokensOnSameLine(
1341 container_decl.lbrace_token,
1342 fields_and_decls[0].firstToken(),
1343 );
1344
1345 // We can only print all the elements in-line if all the
1346 // declarations inside are fields
1347 const src_has_only_fields = blk: {
1348 for (fields_and_decls) |decl| {
1349 if (decl.tag != .ContainerField) break :blk false;
1083 const rparen = tree.lastToken(while_node.ast.cond_expr) + 1;
1084 const last_then_token = tree.lastToken(while_node.ast.then_expr);
1085 const src_has_newline = !tree.tokensOnSameLine(rparen, last_then_token);
1086
1087 if (src_has_newline) {
1088 if (while_node.payload_token) |payload_token| {
1089 try renderToken(ais, tree, payload_token - 2, .space); // rparen
1090 try renderToken(ais, tree, payload_token - 1, .none); // |
1091 const ident = blk: {
1092 if (token_tags[payload_token] == .asterisk) {
1093 try renderToken(ais, tree, payload_token, .none); // *
1094 break :blk payload_token + 1;
1095 } else {
1096 break :blk payload_token;
13501097 }
1351 break :blk true;
13521098 };
1353
1354 if (src_has_trailing_comma or !src_has_only_fields) {
1355 // One declaration per line
1356 ais.pushIndentNextLine();
1357 defer ais.popIndent();
1358 try renderToken(tree, ais, container_decl.lbrace_token, .Newline); // {
1359
1360 for (fields_and_decls) |decl, i| {
1361 try renderContainerDecl(allocator, ais, tree, decl, .Newline);
1362
1363 if (i + 1 < fields_and_decls.len) {
1364 try renderExtraNewline(tree, ais, fields_and_decls[i + 1]);
1365 }
1366 }
1367 } else if (src_has_newline) {
1368 // All the declarations on the same line, but place the items on
1369 // their own line
1370 try renderToken(tree, ais, container_decl.lbrace_token, .Newline); // {
1371
1372 ais.pushIndent();
1373 defer ais.popIndent();
1374
1375 for (fields_and_decls) |decl, i| {
1376 const space_after_decl: Space = if (i + 1 >= fields_and_decls.len) .Newline else .Space;
1377 try renderContainerDecl(allocator, ais, tree, decl, space_after_decl);
1378 }
1379 } else {
1380 // All the declarations on the same line
1381 try renderToken(tree, ais, container_decl.lbrace_token, .Space); // {
1382
1383 for (fields_and_decls) |decl| {
1384 try renderContainerDecl(allocator, ais, tree, decl, .Space);
1385 }
1386 }
1387
1388 return renderToken(tree, ais, container_decl.rbrace_token, space); // }
1389 },
1390
1391 .ErrorSetDecl => {
1392 const err_set_decl = @fieldParentPtr(ast.Node.ErrorSetDecl, "base", base);
1393
1394 const lbrace = tree.nextToken(err_set_decl.error_token);
1395
1396 if (err_set_decl.decls_len == 0) {
1397 try renderToken(tree, ais, err_set_decl.error_token, Space.None);
1398 try renderToken(tree, ais, lbrace, Space.None);
1399 return renderToken(tree, ais, err_set_decl.rbrace_token, space);
1400 }
1401
1402 if (err_set_decl.decls_len == 1) blk: {
1403 const node = err_set_decl.decls()[0];
1404
1405 // if there are any doc comments or same line comments
1406 // don't try to put it all on one line
1407 if (node.cast(ast.Node.ErrorTag)) |tag| {
1408 if (tag.doc_comments != null) break :blk;
1099 try renderToken(ais, tree, ident, .none); // identifier
1100 const pipe = blk: {
1101 if (token_tags[ident + 1] == .comma) {
1102 try renderToken(ais, tree, ident + 1, .space); // ,
1103 try renderToken(ais, tree, ident + 2, .none); // index
1104 break :blk ident + 3;
14091105 } else {
1410 break :blk;
1106 break :blk ident + 1;
14111107 }
1412
1413 try renderToken(tree, ais, err_set_decl.error_token, Space.None); // error
1414 try renderToken(tree, ais, lbrace, Space.None); // {
1415 try renderExpression(allocator, ais, tree, node, Space.None);
1416 return renderToken(tree, ais, err_set_decl.rbrace_token, space); // }
1417 }
1418
1419 try renderToken(tree, ais, err_set_decl.error_token, Space.None); // error
1420
1421 const src_has_trailing_comma = blk: {
1422 const maybe_comma = tree.prevToken(err_set_decl.rbrace_token);
1423 break :blk tree.token_ids[maybe_comma] == .Comma;
14241108 };
1425
1426 if (src_has_trailing_comma) {
1427 {
1428 ais.pushIndent();
1429 defer ais.popIndent();
1430
1431 try renderToken(tree, ais, lbrace, Space.Newline); // {
1432 const decls = err_set_decl.decls();
1433 for (decls) |node, i| {
1434 if (i + 1 < decls.len) {
1435 try renderExpression(allocator, ais, tree, node, Space.None);
1436 try renderToken(tree, ais, tree.nextToken(node.lastToken()), Space.Newline); // ,
1437
1438 try renderExtraNewline(tree, ais, decls[i + 1]);
1439 } else {
1440 try renderExpression(allocator, ais, tree, node, Space.Comma);
1441 }
1442 }
1109 const after_space: Space = if (while_node.ast.cont_expr != 0) .space else .newline;
1110 try renderToken(ais, tree, pipe, after_space); // |
1111 } else {
1112 ais.pushIndent();
1113 const after_space: Space = if (while_node.ast.cont_expr != 0) .space else .newline;
1114 try renderToken(ais, tree, rparen, after_space); // rparen
1115 ais.popIndent();
1116 }
1117 if (while_node.ast.cont_expr != 0) {
1118 const cont_rparen = tree.lastToken(while_node.ast.cont_expr) + 1;
1119 const cont_lparen = tree.firstToken(while_node.ast.cont_expr) - 1;
1120 try renderToken(ais, tree, cont_lparen - 1, .space); // :
1121 try renderToken(ais, tree, cont_lparen, .none); // lparen
1122 try renderExpression(gpa, ais, tree, while_node.ast.cont_expr, .none);
1123 try renderToken(ais, tree, cont_rparen, .newline); // rparen
1124 }
1125 if (while_node.ast.else_expr != 0) {
1126 ais.pushIndent();
1127 try renderExpression(gpa, ais, tree, while_node.ast.then_expr, Space.newline);
1128 ais.popIndent();
1129 const else_is_block = nodeIsBlock(node_tags[while_node.ast.else_expr]);
1130 if (else_is_block) {
1131 try renderToken(ais, tree, while_node.else_token, .space); // else
1132 if (while_node.error_token) |error_token| {
1133 try renderToken(ais, tree, error_token - 1, .none); // |
1134 try renderToken(ais, tree, error_token, .none); // identifier
1135 try renderToken(ais, tree, error_token + 1, .space); // |
14431136 }
1444
1445 return renderToken(tree, ais, err_set_decl.rbrace_token, space); // }
1137 return renderExpression(gpa, ais, tree, while_node.ast.else_expr, space);
14461138 } else {
1447 try renderToken(tree, ais, lbrace, Space.Space); // {
1448
1449 const decls = err_set_decl.decls();
1450 for (decls) |node, i| {
1451 if (i + 1 < decls.len) {
1452 try renderExpression(allocator, ais, tree, node, Space.None);
1453
1454 const comma_token = tree.nextToken(node.lastToken());
1455 assert(tree.token_ids[comma_token] == .Comma);
1456 try renderToken(tree, ais, comma_token, Space.Space); // ,
1457 try renderExtraNewline(tree, ais, decls[i + 1]);
1458 } else {
1459 try renderExpression(allocator, ais, tree, node, Space.Space);
1460 }
1461 }
1462
1463 return renderToken(tree, ais, err_set_decl.rbrace_token, space); // }
1464 }
1465 },
1466
1467 .ErrorTag => {
1468 const tag = @fieldParentPtr(ast.Node.ErrorTag, "base", base);
1469
1470 try renderDocComments(tree, ais, tag, tag.doc_comments);
1471 return renderToken(tree, ais, tag.name_token, space); // name
1472 },
1473
1474 .MultilineStringLiteral => {
1475 const multiline_str_literal = @fieldParentPtr(ast.Node.MultilineStringLiteral, "base", base);
1476
1477 {
1478 const locked_indents = ais.lockOneShotIndent();
1479 defer {
1480 var i: u8 = 0;
1481 while (i < locked_indents) : (i += 1) ais.popIndent();
1482 }
1483 try ais.maybeInsertNewline();
1484
1485 for (multiline_str_literal.lines()) |t| try renderToken(tree, ais, t, Space.None);
1486 }
1487 },
1488
1489 .BuiltinCall => {
1490 const builtin_call = @fieldParentPtr(ast.Node.BuiltinCall, "base", base);
1491
1492 // TODO remove after 0.7.0 release
1493 if (mem.eql(u8, tree.tokenSlice(builtin_call.builtin_token), "@OpaqueType"))
1494 return ais.writer().writeAll("opaque {}");
1495
1496 // TODO remove after 0.7.0 release
1497 {
1498 const params = builtin_call.paramsConst();
1499 if (mem.eql(u8, tree.tokenSlice(builtin_call.builtin_token), "@Type") and
1500 params.len == 1)
1501 {
1502 if (params[0].castTag(.EnumLiteral)) |enum_literal|
1503 if (mem.eql(u8, tree.tokenSlice(enum_literal.name), "Opaque"))
1504 return ais.writer().writeAll("opaque {}");
1139 if (while_node.error_token) |error_token| {
1140 try renderToken(ais, tree, while_node.else_token, .space); // else
1141 try renderToken(ais, tree, error_token - 1, .none); // |
1142 try renderToken(ais, tree, error_token, .none); // identifier
1143 try renderToken(ais, tree, error_token + 1, .space); // |
1144 } else {
1145 try renderToken(ais, tree, while_node.else_token, .newline); // else
15051146 }
1147 try renderExpressionIndented(gpa, ais, tree, while_node.ast.else_expr, space);
1148 return;
15061149 }
1150 } else {
1151 try renderExpressionIndented(gpa, ais, tree, while_node.ast.then_expr, space);
1152 return;
1153 }
1154 }
15071155
1508 try renderToken(tree, ais, builtin_call.builtin_token, Space.None); // @name
1509
1510 const src_params_trailing_comma = blk: {
1511 if (builtin_call.params_len == 0) break :blk false;
1512 const last_node = builtin_call.params()[builtin_call.params_len - 1];
1513 const maybe_comma = tree.nextToken(last_node.lastToken());
1514 break :blk tree.token_ids[maybe_comma] == .Comma;
1515 };
1516
1517 const lparen = tree.nextToken(builtin_call.builtin_token);
1518
1519 if (!src_params_trailing_comma) {
1520 try renderToken(tree, ais, lparen, Space.None); // (
1521
1522 // render all on one line, no trailing comma
1523 const params = builtin_call.params();
1524 for (params) |param_node, i| {
1525 const maybe_comment = param_node.firstToken() - 1;
1526 if (param_node.*.tag == .MultilineStringLiteral or tree.token_ids[maybe_comment] == .LineComment) {
1527 ais.pushIndentOneShot();
1528 }
1529 try renderExpression(allocator, ais, tree, param_node, Space.None);
1156 // Render everything on a single line.
15301157
1531 if (i + 1 < params.len) {
1532 const comma_token = tree.nextToken(param_node.lastToken());
1533 try renderToken(tree, ais, comma_token, Space.Space); // ,
1534 }
1535 }
1158 if (while_node.payload_token) |payload_token| {
1159 assert(payload_token - 2 == rparen);
1160 try renderToken(ais, tree, payload_token - 2, .space); // )
1161 try renderToken(ais, tree, payload_token - 1, .none); // |
1162 const ident = blk: {
1163 if (token_tags[payload_token] == .asterisk) {
1164 try renderToken(ais, tree, payload_token, .none); // *
1165 break :blk payload_token + 1;
15361166 } else {
1537 // one param per line
1538 ais.pushIndent();
1539 defer ais.popIndent();
1540 try renderToken(tree, ais, lparen, Space.Newline); // (
1541
1542 for (builtin_call.params()) |param_node| {
1543 try renderExpression(allocator, ais, tree, param_node, Space.Comma);
1544 }
1167 break :blk payload_token;
15451168 }
1546
1547 return renderToken(tree, ais, builtin_call.rparen_token, space); // )
1548 },
1549
1550 .FnProto => {
1551 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", base);
1552
1553 if (fn_proto.getVisibToken()) |visib_token_index| {
1554 const visib_token = tree.token_ids[visib_token_index];
1555 assert(visib_token == .Keyword_pub or visib_token == .Keyword_export);
1556
1557 try renderToken(tree, ais, visib_token_index, Space.Space); // pub
1558 }
1559
1560 if (fn_proto.getExternExportInlineToken()) |extern_export_inline_token| {
1561 if (fn_proto.getIsExternPrototype() == null and fn_proto.getIsInline() == null)
1562 try renderToken(tree, ais, extern_export_inline_token, Space.Space); // extern/export/inline
1563 }
1564
1565 if (fn_proto.getLibName()) |lib_name| {
1566 try renderExpression(allocator, ais, tree, lib_name, Space.Space);
1567 }
1568
1569 const lparen = if (fn_proto.getNameToken()) |name_token| blk: {
1570 try renderToken(tree, ais, fn_proto.fn_token, Space.Space); // fn
1571 try renderToken(tree, ais, name_token, Space.None); // name
1572 break :blk tree.nextToken(name_token);
1573 } else blk: {
1574 try renderToken(tree, ais, fn_proto.fn_token, Space.Space); // fn
1575 break :blk tree.nextToken(fn_proto.fn_token);
1576 };
1577 assert(tree.token_ids[lparen] == .LParen);
1578
1579 const rparen = tree.prevToken(
1580 // the first token for the annotation expressions is the left
1581 // parenthesis, hence the need for two prevToken
1582 if (fn_proto.getAlignExpr()) |align_expr|
1583 tree.prevToken(tree.prevToken(align_expr.firstToken()))
1584 else if (fn_proto.getSectionExpr()) |section_expr|
1585 tree.prevToken(tree.prevToken(section_expr.firstToken()))
1586 else if (fn_proto.getCallconvExpr()) |callconv_expr|
1587 tree.prevToken(tree.prevToken(callconv_expr.firstToken()))
1588 else switch (fn_proto.return_type) {
1589 .Explicit => |node| node.firstToken(),
1590 .InferErrorSet => |node| tree.prevToken(node.firstToken()),
1591 .Invalid => unreachable,
1592 },
1593 );
1594 assert(tree.token_ids[rparen] == .RParen);
1595
1596 const src_params_trailing_comma = blk: {
1597 const maybe_comma = tree.token_ids[rparen - 1];
1598 break :blk maybe_comma == .Comma or maybe_comma == .LineComment;
1599 };
1600
1601 if (!src_params_trailing_comma) {
1602 try renderToken(tree, ais, lparen, Space.None); // (
1603
1604 // render all on one line, no trailing comma
1605 for (fn_proto.params()) |param_decl, i| {
1606 try renderParamDecl(allocator, ais, tree, param_decl, Space.None);
1607
1608 if (i + 1 < fn_proto.params_len or fn_proto.getVarArgsToken() != null) {
1609 const comma = tree.nextToken(param_decl.lastToken());
1610 try renderToken(tree, ais, comma, Space.Space); // ,
1611 }
1612 }
1613 if (fn_proto.getVarArgsToken()) |var_args_token| {
1614 try renderToken(tree, ais, var_args_token, Space.None);
1615 }
1169 };
1170 try renderToken(ais, tree, ident, .none); // identifier
1171 const pipe = blk: {
1172 if (token_tags[ident + 1] == .comma) {
1173 try renderToken(ais, tree, ident + 1, .space); // ,
1174 try renderToken(ais, tree, ident + 2, .none); // index
1175 break :blk ident + 3;
16161176 } else {
1617 // one param per line
1618 ais.pushIndent();
1619 defer ais.popIndent();
1620 try renderToken(tree, ais, lparen, Space.Newline); // (
1621
1622 for (fn_proto.params()) |param_decl| {
1623 try renderParamDecl(allocator, ais, tree, param_decl, Space.Comma);
1624 }
1625 if (fn_proto.getVarArgsToken()) |var_args_token| {
1626 try renderToken(tree, ais, var_args_token, Space.Comma);
1627 }
1177 break :blk ident + 1;
16281178 }
1179 };
1180 try renderToken(ais, tree, pipe, .space); // |
1181 } else {
1182 try renderToken(ais, tree, rparen, .space); // )
1183 }
16291184
1630 try renderToken(tree, ais, rparen, Space.Space); // )
1631
1632 if (fn_proto.getAlignExpr()) |align_expr| {
1633 const align_rparen = tree.nextToken(align_expr.lastToken());
1634 const align_lparen = tree.prevToken(align_expr.firstToken());
1635 const align_kw = tree.prevToken(align_lparen);
1636
1637 try renderToken(tree, ais, align_kw, Space.None); // align
1638 try renderToken(tree, ais, align_lparen, Space.None); // (
1639 try renderExpression(allocator, ais, tree, align_expr, Space.None);
1640 try renderToken(tree, ais, align_rparen, Space.Space); // )
1641 }
1642
1643 if (fn_proto.getSectionExpr()) |section_expr| {
1644 const section_rparen = tree.nextToken(section_expr.lastToken());
1645 const section_lparen = tree.prevToken(section_expr.firstToken());
1646 const section_kw = tree.prevToken(section_lparen);
1647
1648 try renderToken(tree, ais, section_kw, Space.None); // section
1649 try renderToken(tree, ais, section_lparen, Space.None); // (
1650 try renderExpression(allocator, ais, tree, section_expr, Space.None);
1651 try renderToken(tree, ais, section_rparen, Space.Space); // )
1652 }
1185 if (while_node.ast.cont_expr != 0) {
1186 const cont_rparen = tree.lastToken(while_node.ast.cont_expr) + 1;
1187 const cont_lparen = tree.firstToken(while_node.ast.cont_expr) - 1;
1188 try renderToken(ais, tree, cont_lparen - 1, .space); // :
1189 try renderToken(ais, tree, cont_lparen, .none); // lparen
1190 try renderExpression(gpa, ais, tree, while_node.ast.cont_expr, .none);
1191 try renderToken(ais, tree, cont_rparen, .space); // rparen
1192 }
16531193
1654 if (fn_proto.getCallconvExpr()) |callconv_expr| {
1655 const callconv_rparen = tree.nextToken(callconv_expr.lastToken());
1656 const callconv_lparen = tree.prevToken(callconv_expr.firstToken());
1657 const callconv_kw = tree.prevToken(callconv_lparen);
1658
1659 try renderToken(tree, ais, callconv_kw, Space.None); // callconv
1660 try renderToken(tree, ais, callconv_lparen, Space.None); // (
1661 try renderExpression(allocator, ais, tree, callconv_expr, Space.None);
1662 try renderToken(tree, ais, callconv_rparen, Space.Space); // )
1663 } else if (fn_proto.getIsExternPrototype() != null) {
1664 try ais.writer().writeAll("callconv(.C) ");
1665 } else if (fn_proto.getIsAsync() != null) {
1666 try ais.writer().writeAll("callconv(.Async) ");
1667 } else if (fn_proto.getIsInline() != null) {
1668 try ais.writer().writeAll("callconv(.Inline) ");
1669 }
1194 if (while_node.ast.else_expr != 0) {
1195 try renderExpression(gpa, ais, tree, while_node.ast.then_expr, .space);
1196 try renderToken(ais, tree, while_node.else_token, .space); // else
16701197
1671 switch (fn_proto.return_type) {
1672 .Explicit => |node| {
1673 return renderExpression(allocator, ais, tree, node, space);
1674 },
1675 .InferErrorSet => |node| {
1676 try renderToken(tree, ais, tree.prevToken(node.firstToken()), Space.None); // !
1677 return renderExpression(allocator, ais, tree, node, space);
1678 },
1679 .Invalid => unreachable,
1680 }
1681 },
1198 if (while_node.error_token) |error_token| {
1199 try renderToken(ais, tree, error_token - 1, .none); // |
1200 try renderToken(ais, tree, error_token, .none); // identifier
1201 try renderToken(ais, tree, error_token + 1, .space); // |
1202 }
16821203
1683 .AnyFrameType => {
1684 const anyframe_type = @fieldParentPtr(ast.Node.AnyFrameType, "base", base);
1204 return renderExpression(gpa, ais, tree, while_node.ast.else_expr, space);
1205 } else {
1206 return renderExpression(gpa, ais, tree, while_node.ast.then_expr, space);
1207 }
1208}
16851209
1686 if (anyframe_type.result) |result| {
1687 try renderToken(tree, ais, anyframe_type.anyframe_token, Space.None); // anyframe
1688 try renderToken(tree, ais, result.arrow_token, Space.None); // ->
1689 return renderExpression(allocator, ais, tree, result.return_type, space);
1690 } else {
1691 return renderToken(tree, ais, anyframe_type.anyframe_token, space); // anyframe
1692 }
1693 },
1210fn renderContainerField(
1211 gpa: *Allocator,
1212 ais: *Ais,
1213 tree: ast.Tree,
1214 field: ast.full.ContainerField,
1215 space: Space,
1216) Error!void {
1217 const main_tokens = tree.nodes.items(.main_token);
1218 if (field.comptime_token) |t| {
1219 try renderToken(ais, tree, t, .space); // comptime
1220 }
1221 if (field.ast.type_expr == 0 and field.ast.value_expr == 0) {
1222 return renderTokenComma(ais, tree, field.ast.name_token, space); // name
1223 }
1224 if (field.ast.type_expr != 0 and field.ast.value_expr == 0) {
1225 try renderToken(ais, tree, field.ast.name_token, .none); // name
1226 try renderToken(ais, tree, field.ast.name_token + 1, .space); // :
1227
1228 if (field.ast.align_expr != 0) {
1229 try renderExpression(gpa, ais, tree, field.ast.type_expr, .space); // type
1230 const align_token = tree.firstToken(field.ast.align_expr) - 2;
1231 try renderToken(ais, tree, align_token, .none); // align
1232 try renderToken(ais, tree, align_token + 1, .none); // (
1233 try renderExpression(gpa, ais, tree, field.ast.align_expr, .none); // alignment
1234 const rparen = tree.lastToken(field.ast.align_expr) + 1;
1235 return renderTokenComma(ais, tree, rparen, space); // )
1236 } else {
1237 return renderExpressionComma(gpa, ais, tree, field.ast.type_expr, space); // type
1238 }
1239 }
1240 if (field.ast.type_expr == 0 and field.ast.value_expr != 0) {
1241 try renderToken(ais, tree, field.ast.name_token, .space); // name
1242 try renderToken(ais, tree, field.ast.name_token + 1, .space); // =
1243 return renderExpressionComma(gpa, ais, tree, field.ast.value_expr, space); // value
1244 }
16941245
1695 .DocComment => unreachable, // doc comments are attached to nodes
1246 try renderToken(ais, tree, field.ast.name_token, .none); // name
1247 try renderToken(ais, tree, field.ast.name_token + 1, .space); // :
1248 try renderExpression(gpa, ais, tree, field.ast.type_expr, .space); // type
1249
1250 if (field.ast.align_expr != 0) {
1251 const lparen_token = tree.firstToken(field.ast.align_expr) - 1;
1252 const align_kw = lparen_token - 1;
1253 const rparen_token = tree.lastToken(field.ast.align_expr) + 1;
1254 try renderToken(ais, tree, align_kw, .none); // align
1255 try renderToken(ais, tree, lparen_token, .none); // (
1256 try renderExpression(gpa, ais, tree, field.ast.align_expr, .none); // alignment
1257 try renderToken(ais, tree, rparen_token, .space); // )
1258 }
1259 const eq_token = tree.firstToken(field.ast.value_expr) - 1;
1260 try renderToken(ais, tree, eq_token, .space); // =
1261 return renderExpressionComma(gpa, ais, tree, field.ast.value_expr, space); // value
1262}
16961263
1697 .Switch => {
1698 const switch_node = @fieldParentPtr(ast.Node.Switch, "base", base);
1264fn renderBuiltinCall(
1265 gpa: *Allocator,
1266 ais: *Ais,
1267 tree: ast.Tree,
1268 builtin_token: ast.TokenIndex,
1269 params: []const ast.Node.Index,
1270 space: Space,
1271) Error!void {
1272 const token_tags = tree.tokens.items(.tag);
16991273
1700 try renderToken(tree, ais, switch_node.switch_token, Space.Space); // switch
1701 try renderToken(tree, ais, tree.nextToken(switch_node.switch_token), Space.None); // (
1274 try renderToken(ais, tree, builtin_token, .none); // @name
17021275
1703 const rparen = tree.nextToken(switch_node.expr.lastToken());
1704 const lbrace = tree.nextToken(rparen);
1276 if (params.len == 0) {
1277 try renderToken(ais, tree, builtin_token + 1, .none); // (
1278 return renderToken(ais, tree, builtin_token + 2, space); // )
1279 }
17051280
1706 if (switch_node.cases_len == 0) {
1707 try renderExpression(allocator, ais, tree, switch_node.expr, Space.None);
1708 try renderToken(tree, ais, rparen, Space.Space); // )
1709 try renderToken(tree, ais, lbrace, Space.None); // {
1710 return renderToken(tree, ais, switch_node.rbrace, space); // }
1711 }
1281 const last_param = params[params.len - 1];
1282 const after_last_param_token = tree.lastToken(last_param) + 1;
17121283
1713 try renderExpression(allocator, ais, tree, switch_node.expr, Space.None);
1714 try renderToken(tree, ais, rparen, Space.Space); // )
1284 if (token_tags[after_last_param_token] != .comma) {
1285 // Render all on one line, no trailing comma.
1286 try renderToken(ais, tree, builtin_token + 1, .none); // (
17151287
1288 for (params) |param_node, i| {
1289 const first_param_token = tree.firstToken(param_node);
1290 if (token_tags[first_param_token] == .multiline_string_literal_line or
1291 hasSameLineComment(tree, first_param_token - 1))
17161292 {
1717 ais.pushIndentNextLine();
1718 defer ais.popIndent();
1719 try renderToken(tree, ais, lbrace, Space.Newline); // {
1720
1721 const cases = switch_node.cases();
1722 for (cases) |node, i| {
1723 try renderExpression(allocator, ais, tree, node, Space.Comma);
1724
1725 if (i + 1 < cases.len) {
1726 try renderExtraNewline(tree, ais, cases[i + 1]);
1727 }
1728 }
1293 ais.pushIndentOneShot();
17291294 }
1295 try renderExpression(gpa, ais, tree, param_node, .none);
17301296
1731 return renderToken(tree, ais, switch_node.rbrace, space); // }
1732 },
1733
1734 .SwitchCase => {
1735 const switch_case = @fieldParentPtr(ast.Node.SwitchCase, "base", base);
1736
1737 assert(switch_case.items_len != 0);
1738 const src_has_trailing_comma = blk: {
1739 const last_node = switch_case.items()[switch_case.items_len - 1];
1740 const maybe_comma = tree.nextToken(last_node.lastToken());
1741 break :blk tree.token_ids[maybe_comma] == .Comma;
1742 };
1743
1744 if (switch_case.items_len == 1 or !src_has_trailing_comma) {
1745 const items = switch_case.items();
1746 for (items) |node, i| {
1747 if (i + 1 < items.len) {
1748 try renderExpression(allocator, ais, tree, node, Space.None);
1749
1750 const comma_token = tree.nextToken(node.lastToken());
1751 try renderToken(tree, ais, comma_token, Space.Space); // ,
1752 try renderExtraNewline(tree, ais, items[i + 1]);
1753 } else {
1754 try renderExpression(allocator, ais, tree, node, Space.Space);
1755 }
1756 }
1757 } else {
1758 const items = switch_case.items();
1759 for (items) |node, i| {
1760 if (i + 1 < items.len) {
1761 try renderExpression(allocator, ais, tree, node, Space.None);
1762
1763 const comma_token = tree.nextToken(node.lastToken());
1764 try renderToken(tree, ais, comma_token, Space.Newline); // ,
1765 try renderExtraNewline(tree, ais, items[i + 1]);
1766 } else {
1767 try renderExpression(allocator, ais, tree, node, Space.Comma);
1768 }
1769 }
1297 if (i + 1 < params.len) {
1298 const comma_token = tree.lastToken(param_node) + 1;
1299 try renderToken(ais, tree, comma_token, .space); // ,
17701300 }
1301 }
1302 return renderToken(ais, tree, after_last_param_token, space); // )
1303 } else {
1304 // Render one param per line.
1305 ais.pushIndent();
1306 try renderToken(ais, tree, builtin_token + 1, Space.newline); // (
1307
1308 for (params) |param_node| {
1309 try renderExpression(gpa, ais, tree, param_node, .comma);
1310 }
1311 ais.popIndent();
17711312
1772 try renderToken(tree, ais, switch_case.arrow_token, Space.Space); // =>
1773
1774 if (switch_case.payload) |payload| {
1775 try renderExpression(allocator, ais, tree, payload, Space.Space);
1776 }
1777
1778 return renderExpression(allocator, ais, tree, switch_case.expr, space);
1779 },
1780 .SwitchElse => {
1781 const switch_else = @fieldParentPtr(ast.Node.SwitchElse, "base", base);
1782 return renderToken(tree, ais, switch_else.token, space);
1783 },
1784 .Else => {
1785 const else_node = @fieldParentPtr(ast.Node.Else, "base", base);
1786
1787 const body_is_block = nodeIsBlock(else_node.body);
1788 const same_line = body_is_block or tree.tokensOnSameLine(else_node.else_token, else_node.body.lastToken());
1789
1790 const after_else_space = if (same_line or else_node.payload != null) Space.Space else Space.Newline;
1791 try renderToken(tree, ais, else_node.else_token, after_else_space);
1792
1793 if (else_node.payload) |payload| {
1794 const payload_space = if (same_line) Space.Space else Space.Newline;
1795 try renderExpression(allocator, ais, tree, payload, payload_space);
1796 }
1313 return renderToken(ais, tree, after_last_param_token + 1, space); // )
1314 }
1315}
17971316
1798 if (same_line) {
1799 return renderExpression(allocator, ais, tree, else_node.body, space);
1800 } else {
1801 ais.pushIndent();
1802 defer ais.popIndent();
1803 return renderExpression(allocator, ais, tree, else_node.body, space);
1317fn renderFnProto(gpa: *Allocator, ais: *Ais, tree: ast.Tree, fn_proto: ast.full.FnProto, space: Space) Error!void {
1318 const token_tags = tree.tokens.items(.tag);
1319 const token_starts = tree.tokens.items(.start);
1320
1321 const is_inline = fn_proto.ast.fn_token > 0 and
1322 token_tags[fn_proto.ast.fn_token - 1] == .keyword_inline;
1323
1324 const after_fn_token = fn_proto.ast.fn_token + 1;
1325 const lparen = if (token_tags[after_fn_token] == .identifier) blk: {
1326 try renderToken(ais, tree, fn_proto.ast.fn_token, .space); // fn
1327 try renderToken(ais, tree, after_fn_token, .none); // name
1328 break :blk after_fn_token + 1;
1329 } else blk: {
1330 try renderToken(ais, tree, fn_proto.ast.fn_token, .space); // fn
1331 break :blk fn_proto.ast.fn_token + 1;
1332 };
1333 assert(token_tags[lparen] == .l_paren);
1334
1335 const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1;
1336 const rparen = blk: {
1337 // These may appear in any order, so we have to check the token_starts array
1338 // to find out which is first.
1339 var rparen = if (token_tags[maybe_bang] == .bang) maybe_bang - 1 else maybe_bang;
1340 var smallest_start = token_starts[maybe_bang];
1341 if (fn_proto.ast.align_expr != 0) {
1342 const tok = tree.firstToken(fn_proto.ast.align_expr) - 3;
1343 const start = token_starts[tok];
1344 if (start < smallest_start) {
1345 rparen = tok;
1346 smallest_start = start;
18041347 }
1805 },
1806
1807 .While => {
1808 const while_node = @fieldParentPtr(ast.Node.While, "base", base);
1809
1810 if (while_node.label) |label| {
1811 try renderToken(tree, ais, label, Space.None); // label
1812 try renderToken(tree, ais, tree.nextToken(label), Space.Space); // :
1348 }
1349 if (fn_proto.ast.section_expr != 0) {
1350 const tok = tree.firstToken(fn_proto.ast.section_expr) - 3;
1351 const start = token_starts[tok];
1352 if (start < smallest_start) {
1353 rparen = tok;
1354 smallest_start = start;
18131355 }
1814
1815 if (while_node.inline_token) |inline_token| {
1816 try renderToken(tree, ais, inline_token, Space.Space); // inline
1356 }
1357 if (fn_proto.ast.callconv_expr != 0) {
1358 const tok = tree.firstToken(fn_proto.ast.callconv_expr) - 3;
1359 const start = token_starts[tok];
1360 if (start < smallest_start) {
1361 rparen = tok;
1362 smallest_start = start;
18171363 }
1818
1819 try renderToken(tree, ais, while_node.while_token, Space.Space); // while
1820 try renderToken(tree, ais, tree.nextToken(while_node.while_token), Space.None); // (
1821 try renderExpression(allocator, ais, tree, while_node.condition, Space.None);
1822
1823 const cond_rparen = tree.nextToken(while_node.condition.lastToken());
1824
1825 const body_is_block = nodeIsBlock(while_node.body);
1826
1827 var block_start_space: Space = undefined;
1828 var after_body_space: Space = undefined;
1829
1830 if (body_is_block) {
1831 block_start_space = Space.BlockStart;
1832 after_body_space = if (while_node.@"else" == null) space else Space.SpaceOrOutdent;
1833 } else if (tree.tokensOnSameLine(cond_rparen, while_node.body.lastToken())) {
1834 block_start_space = Space.Space;
1835 after_body_space = if (while_node.@"else" == null) space else Space.Space;
1836 } else {
1837 block_start_space = Space.Newline;
1838 after_body_space = if (while_node.@"else" == null) space else Space.Newline;
1364 }
1365 break :blk rparen;
1366 };
1367 assert(token_tags[rparen] == .r_paren);
1368
1369 // The params list is a sparse set that does *not* include anytype or ... parameters.
1370
1371 const trailing_comma = token_tags[rparen - 1] == .comma;
1372 if (!trailing_comma and !hasComment(tree, lparen, rparen)) {
1373 // Render all on one line, no trailing comma.
1374 try renderToken(ais, tree, lparen, .none); // (
1375
1376 var param_i: usize = 0;
1377 var last_param_token = lparen;
1378 while (true) {
1379 last_param_token += 1;
1380 switch (token_tags[last_param_token]) {
1381 .doc_comment => {
1382 try renderToken(ais, tree, last_param_token, .newline);
1383 continue;
1384 },
1385 .ellipsis3 => {
1386 try renderToken(ais, tree, last_param_token, .none); // ...
1387 break;
1388 },
1389 .keyword_noalias, .keyword_comptime => {
1390 try renderToken(ais, tree, last_param_token, .space);
1391 last_param_token += 1;
1392 },
1393 .identifier => {},
1394 .keyword_anytype => {
1395 try renderToken(ais, tree, last_param_token, .none); // anytype
1396 continue;
1397 },
1398 .r_paren => break,
1399 .comma => {
1400 try renderToken(ais, tree, last_param_token, .space); // ,
1401 continue;
1402 },
1403 else => {}, // Parameter type without a name.
18391404 }
1840
1405 if (token_tags[last_param_token] == .identifier and
1406 token_tags[last_param_token + 1] == .colon)
18411407 {
1842 const rparen_space = if (while_node.payload != null or while_node.continue_expr != null) Space.Space else block_start_space;
1843 try renderToken(tree, ais, cond_rparen, rparen_space); // )
1844 }
1845
1846 if (while_node.payload) |payload| {
1847 const payload_space = if (while_node.continue_expr != null) Space.Space else block_start_space;
1848 try renderExpression(allocator, ais, tree, payload, payload_space);
1408 try renderToken(ais, tree, last_param_token, .none); // name
1409 last_param_token += 1;
1410 try renderToken(ais, tree, last_param_token, .space); // :
1411 last_param_token += 1;
1412 }
1413 if (token_tags[last_param_token] == .keyword_anytype) {
1414 try renderToken(ais, tree, last_param_token, .none); // anytype
1415 continue;
1416 }
1417 const param = fn_proto.ast.params[param_i];
1418 param_i += 1;
1419 try renderExpression(gpa, ais, tree, param, .none);
1420 last_param_token = tree.lastToken(param);
1421 }
1422 } else {
1423 // One param per line.
1424 ais.pushIndent();
1425 try renderToken(ais, tree, lparen, .newline); // (
1426
1427 var param_i: usize = 0;
1428 var last_param_token = lparen;
1429 while (true) {
1430 last_param_token += 1;
1431 switch (token_tags[last_param_token]) {
1432 .doc_comment => {
1433 try renderToken(ais, tree, last_param_token, .newline);
1434 continue;
1435 },
1436 .ellipsis3 => {
1437 try renderToken(ais, tree, last_param_token, .comma); // ...
1438 break;
1439 },
1440 .keyword_noalias, .keyword_comptime => {
1441 try renderToken(ais, tree, last_param_token, .space);
1442 last_param_token += 1;
1443 },
1444 .identifier => {},
1445 .keyword_anytype => {
1446 try renderToken(ais, tree, last_param_token, .comma); // anytype
1447 if (token_tags[last_param_token + 1] == .comma)
1448 last_param_token += 1;
1449 continue;
1450 },
1451 .r_paren => break,
1452 else => unreachable,
18491453 }
1454 if (token_tags[last_param_token] == .identifier) {
1455 try renderToken(ais, tree, last_param_token, .none); // name
1456 last_param_token += 1;
1457 try renderToken(ais, tree, last_param_token, .space); // :
1458 last_param_token += 1;
1459 }
1460 if (token_tags[last_param_token] == .keyword_anytype) {
1461 try renderToken(ais, tree, last_param_token, .comma); // anytype
1462 if (token_tags[last_param_token + 1] == .comma)
1463 last_param_token += 1;
1464 continue;
1465 }
1466 const param = fn_proto.ast.params[param_i];
1467 param_i += 1;
1468 try renderExpression(gpa, ais, tree, param, .comma);
1469 last_param_token = tree.lastToken(param);
1470 if (token_tags[last_param_token + 1] == .comma) last_param_token += 1;
1471 }
1472 ais.popIndent();
1473 }
18501474
1851 if (while_node.continue_expr) |continue_expr| {
1852 const rparen = tree.nextToken(continue_expr.lastToken());
1853 const lparen = tree.prevToken(continue_expr.firstToken());
1854 const colon = tree.prevToken(lparen);
1855
1856 try renderToken(tree, ais, colon, Space.Space); // :
1857 try renderToken(tree, ais, lparen, Space.None); // (
1475 try renderToken(ais, tree, rparen, .space); // )
18581476
1859 try renderExpression(allocator, ais, tree, continue_expr, Space.None);
1477 if (fn_proto.ast.align_expr != 0) {
1478 const align_lparen = tree.firstToken(fn_proto.ast.align_expr) - 1;
1479 const align_rparen = tree.lastToken(fn_proto.ast.align_expr) + 1;
18601480
1861 try renderToken(tree, ais, rparen, block_start_space); // )
1862 }
1863
1864 {
1865 if (!body_is_block) ais.pushIndent();
1866 defer if (!body_is_block) ais.popIndent();
1867 try renderExpression(allocator, ais, tree, while_node.body, after_body_space);
1868 }
1481 try renderToken(ais, tree, align_lparen - 1, .none); // align
1482 try renderToken(ais, tree, align_lparen, .none); // (
1483 try renderExpression(gpa, ais, tree, fn_proto.ast.align_expr, .none);
1484 try renderToken(ais, tree, align_rparen, .space); // )
1485 }
18691486
1870 if (while_node.@"else") |@"else"| {
1871 return renderExpression(allocator, ais, tree, &@"else".base, space);
1872 }
1873 },
1487 if (fn_proto.ast.section_expr != 0) {
1488 const section_lparen = tree.firstToken(fn_proto.ast.section_expr) - 1;
1489 const section_rparen = tree.lastToken(fn_proto.ast.section_expr) + 1;
18741490
1875 .For => {
1876 const for_node = @fieldParentPtr(ast.Node.For, "base", base);
1491 try renderToken(ais, tree, section_lparen - 1, .none); // section
1492 try renderToken(ais, tree, section_lparen, .none); // (
1493 try renderExpression(gpa, ais, tree, fn_proto.ast.section_expr, .none);
1494 try renderToken(ais, tree, section_rparen, .space); // )
1495 }
18771496
1878 if (for_node.label) |label| {
1879 try renderToken(tree, ais, label, Space.None); // label
1880 try renderToken(tree, ais, tree.nextToken(label), Space.Space); // :
1881 }
1497 if (fn_proto.ast.callconv_expr != 0) {
1498 const callconv_lparen = tree.firstToken(fn_proto.ast.callconv_expr) - 1;
1499 const callconv_rparen = tree.lastToken(fn_proto.ast.callconv_expr) + 1;
18821500
1883 if (for_node.inline_token) |inline_token| {
1884 try renderToken(tree, ais, inline_token, Space.Space); // inline
1885 }
1886
1887 try renderToken(tree, ais, for_node.for_token, Space.Space); // for
1888 try renderToken(tree, ais, tree.nextToken(for_node.for_token), Space.None); // (
1889 try renderExpression(allocator, ais, tree, for_node.array_expr, Space.None);
1501 try renderToken(ais, tree, callconv_lparen - 1, .none); // callconv
1502 try renderToken(ais, tree, callconv_lparen, .none); // (
1503 try renderExpression(gpa, ais, tree, fn_proto.ast.callconv_expr, .none);
1504 try renderToken(ais, tree, callconv_rparen, .space); // )
1505 } else if (is_inline) {
1506 try ais.writer().writeAll("callconv(.Inline) ");
1507 }
18901508
1891 const rparen = tree.nextToken(for_node.array_expr.lastToken());
1509 if (token_tags[maybe_bang] == .bang) {
1510 try renderToken(ais, tree, maybe_bang, .none); // !
1511 }
1512 return renderExpression(gpa, ais, tree, fn_proto.ast.return_type, space);
1513}
18921514
1893 const body_is_block = for_node.body.tag.isBlock();
1894 const src_one_line_to_body = !body_is_block and tree.tokensOnSameLine(rparen, for_node.body.firstToken());
1895 const body_on_same_line = body_is_block or src_one_line_to_body;
1515fn renderSwitchCase(
1516 gpa: *Allocator,
1517 ais: *Ais,
1518 tree: ast.Tree,
1519 switch_case: ast.full.SwitchCase,
1520 space: Space,
1521) Error!void {
1522 const token_tags = tree.tokens.items(.tag);
1523 const trailing_comma = token_tags[switch_case.ast.arrow_token - 1] == .comma;
1524
1525 // Render everything before the arrow
1526 if (switch_case.ast.values.len == 0) {
1527 try renderToken(ais, tree, switch_case.ast.arrow_token - 1, .space); // else keyword
1528 } else if (switch_case.ast.values.len == 1) {
1529 // render on one line and drop the trailing comma if any
1530 try renderExpression(gpa, ais, tree, switch_case.ast.values[0], .space);
1531 } else if (trailing_comma or
1532 hasComment(tree, tree.firstToken(switch_case.ast.values[0]), switch_case.ast.arrow_token))
1533 {
1534 // Render each value on a new line
1535 try renderExpressions(gpa, ais, tree, switch_case.ast.values, .comma);
1536 } else {
1537 // Render on one line
1538 for (switch_case.ast.values) |value_expr| {
1539 try renderExpression(gpa, ais, tree, value_expr, .comma_space);
1540 }
1541 }
18961542
1897 try renderToken(tree, ais, rparen, Space.Space); // )
1543 // Render the arrow and everything after it
1544 try renderToken(ais, tree, switch_case.ast.arrow_token, .space);
18981545
1899 const space_after_payload = if (body_on_same_line) Space.Space else Space.Newline;
1900 try renderExpression(allocator, ais, tree, for_node.payload, space_after_payload); // |x|
1546 if (switch_case.payload_token) |payload_token| {
1547 try renderToken(ais, tree, payload_token - 1, .none); // pipe
1548 if (token_tags[payload_token] == .asterisk) {
1549 try renderToken(ais, tree, payload_token, .none); // asterisk
1550 try renderToken(ais, tree, payload_token + 1, .none); // identifier
1551 try renderToken(ais, tree, payload_token + 2, .space); // pipe
1552 } else {
1553 try renderToken(ais, tree, payload_token, .none); // identifier
1554 try renderToken(ais, tree, payload_token + 1, .space); // pipe
1555 }
1556 }
19011557
1902 const space_after_body = blk: {
1903 if (for_node.@"else") |@"else"| {
1904 const src_one_line_to_else = tree.tokensOnSameLine(rparen, @"else".firstToken());
1905 if (body_is_block or src_one_line_to_else) {
1906 break :blk Space.Space;
1907 } else {
1908 break :blk Space.Newline;
1909 }
1910 } else {
1911 break :blk space;
1912 }
1913 };
1558 try renderExpression(gpa, ais, tree, switch_case.ast.target_expr, space);
1559}
19141560
1915 {
1916 if (!body_on_same_line) ais.pushIndent();
1917 defer if (!body_on_same_line) ais.popIndent();
1918 try renderExpression(allocator, ais, tree, for_node.body, space_after_body); // { body }
1919 }
1561fn renderBlock(
1562 gpa: *Allocator,
1563 ais: *Ais,
1564 tree: ast.Tree,
1565 block_node: ast.Node.Index,
1566 statements: []const ast.Node.Index,
1567 space: Space,
1568) Error!void {
1569 const token_tags = tree.tokens.items(.tag);
1570 const node_tags = tree.nodes.items(.tag);
1571 const nodes_data = tree.nodes.items(.data);
1572 const lbrace = tree.nodes.items(.main_token)[block_node];
1573
1574 if (token_tags[lbrace - 1] == .colon and
1575 token_tags[lbrace - 2] == .identifier)
1576 {
1577 try renderToken(ais, tree, lbrace - 2, .none);
1578 try renderToken(ais, tree, lbrace - 1, .space);
1579 }
19201580
1921 if (for_node.@"else") |@"else"| {
1922 return renderExpression(allocator, ais, tree, &@"else".base, space); // else
1581 ais.pushIndentNextLine();
1582 if (statements.len == 0) {
1583 try renderToken(ais, tree, lbrace, .none);
1584 } else {
1585 try renderToken(ais, tree, lbrace, .newline);
1586 for (statements) |stmt, i| {
1587 if (i != 0) try renderExtraNewline(ais, tree, stmt);
1588 switch (node_tags[stmt]) {
1589 .global_var_decl => try renderVarDecl(gpa, ais, tree, tree.globalVarDecl(stmt)),
1590 .local_var_decl => try renderVarDecl(gpa, ais, tree, tree.localVarDecl(stmt)),
1591 .simple_var_decl => try renderVarDecl(gpa, ais, tree, tree.simpleVarDecl(stmt)),
1592 .aligned_var_decl => try renderVarDecl(gpa, ais, tree, tree.alignedVarDecl(stmt)),
1593 else => try renderExpression(gpa, ais, tree, stmt, .semicolon),
19231594 }
1924 },
1925
1926 .If => {
1927 const if_node = @fieldParentPtr(ast.Node.If, "base", base);
1595 }
1596 }
1597 ais.popIndent();
19281598
1929 const lparen = tree.nextToken(if_node.if_token);
1930 const rparen = tree.nextToken(if_node.condition.lastToken());
1599 try renderToken(ais, tree, tree.lastToken(block_node), space); // rbrace
1600}
19311601
1932 try renderToken(tree, ais, if_node.if_token, Space.Space); // if
1933 try renderToken(tree, ais, lparen, Space.None); // (
1602fn renderStructInit(
1603 gpa: *Allocator,
1604 ais: *Ais,
1605 tree: ast.Tree,
1606 struct_node: ast.Node.Index,
1607 struct_init: ast.full.StructInit,
1608 space: Space,
1609) Error!void {
1610 const token_tags = tree.tokens.items(.tag);
1611 if (struct_init.ast.type_expr == 0) {
1612 try renderToken(ais, tree, struct_init.ast.lbrace - 1, .none); // .
1613 } else {
1614 try renderExpression(gpa, ais, tree, struct_init.ast.type_expr, .none); // T
1615 }
1616 if (struct_init.ast.fields.len == 0) {
1617 ais.pushIndentNextLine();
1618 try renderToken(ais, tree, struct_init.ast.lbrace, .none); // lbrace
1619 ais.popIndent();
1620 return renderToken(ais, tree, struct_init.ast.lbrace + 1, space); // rbrace
1621 }
19341622
1935 try renderExpression(allocator, ais, tree, if_node.condition, Space.None); // condition
1623 const rbrace = tree.lastToken(struct_node);
1624 const trailing_comma = token_tags[rbrace - 1] == .comma;
1625 if (trailing_comma or hasComment(tree, struct_init.ast.lbrace, rbrace)) {
1626 // Render one field init per line.
1627 ais.pushIndentNextLine();
1628 try renderToken(ais, tree, struct_init.ast.lbrace, .newline);
1629
1630 try renderToken(ais, tree, struct_init.ast.lbrace + 1, .none); // .
1631 try renderToken(ais, tree, struct_init.ast.lbrace + 2, .space); // name
1632 try renderToken(ais, tree, struct_init.ast.lbrace + 3, .space); // =
1633 try renderExpression(gpa, ais, tree, struct_init.ast.fields[0], .comma);
1634
1635 for (struct_init.ast.fields[1..]) |field_init| {
1636 const init_token = tree.firstToken(field_init);
1637 try renderExtraNewlineToken(ais, tree, init_token - 3);
1638 try renderToken(ais, tree, init_token - 3, .none); // .
1639 try renderToken(ais, tree, init_token - 2, .space); // name
1640 try renderToken(ais, tree, init_token - 1, .space); // =
1641 try renderExpression(gpa, ais, tree, field_init, .comma);
1642 }
19361643
1937 const body_is_if_block = if_node.body.tag == .If;
1938 const body_is_block = nodeIsBlock(if_node.body);
1644 ais.popIndent();
1645 } else {
1646 // Render all on one line, no trailing comma.
1647 try renderToken(ais, tree, struct_init.ast.lbrace, .space);
1648
1649 for (struct_init.ast.fields) |field_init| {
1650 const init_token = tree.firstToken(field_init);
1651 try renderToken(ais, tree, init_token - 3, .none); // .
1652 try renderToken(ais, tree, init_token - 2, .space); // name
1653 try renderToken(ais, tree, init_token - 1, .space); // =
1654 try renderExpression(gpa, ais, tree, field_init, .comma_space);
1655 }
1656 }
19391657
1940 if (body_is_if_block) {
1941 try renderExtraNewline(tree, ais, if_node.body);
1942 } else if (body_is_block) {
1943 const after_rparen_space = if (if_node.payload == null) Space.BlockStart else Space.Space;
1944 try renderToken(tree, ais, rparen, after_rparen_space); // )
1658 return renderToken(ais, tree, rbrace, space);
1659}
19451660
1946 if (if_node.payload) |payload| {
1947 try renderExpression(allocator, ais, tree, payload, Space.BlockStart); // |x|
1948 }
1661// TODO: handle comments between elements
1662fn renderArrayInit(
1663 gpa: *Allocator,
1664 ais: *Ais,
1665 tree: ast.Tree,
1666 array_init: ast.full.ArrayInit,
1667 space: Space,
1668) Error!void {
1669 const token_tags = tree.tokens.items(.tag);
1670 const token_starts = tree.tokens.items(.start);
1671
1672 if (array_init.ast.type_expr == 0) {
1673 try renderToken(ais, tree, array_init.ast.lbrace - 1, .none); // .
1674 } else {
1675 try renderExpression(gpa, ais, tree, array_init.ast.type_expr, .none); // T
1676 }
19491677
1950 if (if_node.@"else") |@"else"| {
1951 try renderExpression(allocator, ais, tree, if_node.body, Space.SpaceOrOutdent);
1952 return renderExpression(allocator, ais, tree, &@"else".base, space);
1953 } else {
1954 return renderExpression(allocator, ais, tree, if_node.body, space);
1955 }
1956 }
1678 if (array_init.ast.elements.len == 0) {
1679 ais.pushIndentNextLine();
1680 try renderToken(ais, tree, array_init.ast.lbrace, .none); // lbrace
1681 ais.popIndent();
1682 return renderToken(ais, tree, array_init.ast.lbrace + 1, space); // rbrace
1683 }
19571684
1958 const src_has_newline = !tree.tokensOnSameLine(rparen, if_node.body.lastToken());
1685 const last_elem = array_init.ast.elements[array_init.ast.elements.len - 1];
1686 const last_elem_token = tree.lastToken(last_elem);
1687 const trailing_comma = token_tags[last_elem_token + 1] == .comma;
1688 const rbrace = if (trailing_comma) last_elem_token + 2 else last_elem_token + 1;
1689 assert(token_tags[rbrace] == .r_brace);
1690
1691 if (array_init.ast.elements.len == 1) {
1692 const only_elem = array_init.ast.elements[0];
1693 const first_token = tree.firstToken(only_elem);
1694 if (token_tags[first_token] != .multiline_string_literal_line and
1695 !anythingBetween(tree, last_elem_token, rbrace))
1696 {
1697 try renderToken(ais, tree, array_init.ast.lbrace, .none);
1698 try renderExpression(gpa, ais, tree, only_elem, .none);
1699 return renderToken(ais, tree, rbrace, space);
1700 }
1701 }
19591702
1960 if (src_has_newline) {
1961 const after_rparen_space = if (if_node.payload == null) Space.Newline else Space.Space;
1703 const contains_newlines = !tree.tokensOnSameLine(array_init.ast.lbrace, rbrace);
19621704
1963 {
1964 ais.pushIndent();
1965 defer ais.popIndent();
1966 try renderToken(tree, ais, rparen, after_rparen_space); // )
1967 }
1705 if (!trailing_comma and !contains_newlines) {
1706 // Render all on one line, no trailing comma.
1707 if (array_init.ast.elements.len == 1) {
1708 // If there is only one element, we don't use spaces
1709 try renderToken(ais, tree, array_init.ast.lbrace, .none);
1710 try renderExpression(gpa, ais, tree, array_init.ast.elements[0], .none);
1711 } else {
1712 try renderToken(ais, tree, array_init.ast.lbrace, .space);
1713 for (array_init.ast.elements) |elem| {
1714 try renderExpression(gpa, ais, tree, elem, .comma_space);
1715 }
1716 }
1717 return renderToken(ais, tree, last_elem_token + 1, space); // rbrace
1718 }
19681719
1969 if (if_node.payload) |payload| {
1970 try renderExpression(allocator, ais, tree, payload, Space.Newline);
1720 ais.pushIndentNextLine();
1721 try renderToken(ais, tree, array_init.ast.lbrace, .newline);
1722
1723 var expr_index: usize = 0;
1724 while (rowSize(tree, array_init.ast.elements[expr_index..], rbrace)) |row_size| {
1725 const row_exprs = array_init.ast.elements[expr_index..];
1726 // A place to store the width of each expression and its column's maximum
1727 const widths = try gpa.alloc(usize, row_exprs.len + row_size);
1728 defer gpa.free(widths);
1729 mem.set(usize, widths, 0);
1730
1731 const expr_newlines = try gpa.alloc(bool, row_exprs.len);
1732 defer gpa.free(expr_newlines);
1733 mem.set(bool, expr_newlines, false);
1734
1735 const expr_widths = widths[0..row_exprs.len];
1736 const column_widths = widths[row_exprs.len..];
1737
1738 // Find next row with trailing comment (if any) to end the current section.
1739 const section_end = sec_end: {
1740 var this_line_first_expr: usize = 0;
1741 var this_line_size = rowSize(tree, row_exprs, rbrace);
1742 for (row_exprs) |expr, i| {
1743 // Ignore comment on first line of this section.
1744 if (i == 0) continue;
1745 const expr_last_token = tree.lastToken(expr);
1746 if (tree.tokensOnSameLine(tree.firstToken(row_exprs[0]), expr_last_token))
1747 continue;
1748 // Track start of line containing comment.
1749 if (!tree.tokensOnSameLine(tree.firstToken(row_exprs[this_line_first_expr]), expr_last_token)) {
1750 this_line_first_expr = i;
1751 this_line_size = rowSize(tree, row_exprs[this_line_first_expr..], rbrace);
19711752 }
1972
1973 if (if_node.@"else") |@"else"| {
1974 const else_is_block = nodeIsBlock(@"else".body);
1975
1976 {
1977 ais.pushIndent();
1978 defer ais.popIndent();
1979 try renderExpression(allocator, ais, tree, if_node.body, Space.Newline);
1980 }
1981
1982 if (else_is_block) {
1983 try renderToken(tree, ais, @"else".else_token, Space.Space); // else
1984
1985 if (@"else".payload) |payload| {
1986 try renderExpression(allocator, ais, tree, payload, Space.Space);
1987 }
1988
1989 return renderExpression(allocator, ais, tree, @"else".body, space);
1990 } else {
1991 const after_else_space = if (@"else".payload == null) Space.Newline else Space.Space;
1992 try renderToken(tree, ais, @"else".else_token, after_else_space); // else
1993
1994 if (@"else".payload) |payload| {
1995 try renderExpression(allocator, ais, tree, payload, Space.Newline);
1996 }
1997
1998 ais.pushIndent();
1999 defer ais.popIndent();
2000 return renderExpression(allocator, ais, tree, @"else".body, space);
2001 }
2002 } else {
2003 ais.pushIndent();
2004 defer ais.popIndent();
2005 return renderExpression(allocator, ais, tree, if_node.body, space);
1753
1754 const maybe_comma = expr_last_token + 1;
1755 if (token_tags[maybe_comma] == .comma) {
1756 if (hasSameLineComment(tree, maybe_comma))
1757 break :sec_end i - this_line_size.? + 1;
20061758 }
20071759 }
1760 break :sec_end row_exprs.len;
1761 };
1762 expr_index += section_end;
20081763
2009 // Single line if statement
1764 const section_exprs = row_exprs[0..section_end];
20101765
2011 try renderToken(tree, ais, rparen, Space.Space); // )
1766 var sub_expr_buffer = std.ArrayList(u8).init(gpa);
1767 defer sub_expr_buffer.deinit();
20121768
2013 if (if_node.payload) |payload| {
2014 try renderExpression(allocator, ais, tree, payload, Space.Space);
2015 }
1769 var auto_indenting_stream = Ais{
1770 .indent_delta = indent_delta,
1771 .underlying_writer = sub_expr_buffer.writer(),
1772 };
20161773
2017 if (if_node.@"else") |@"else"| {
2018 try renderExpression(allocator, ais, tree, if_node.body, Space.Space);
2019 try renderToken(tree, ais, @"else".else_token, Space.Space);
1774 // Calculate size of columns in current section
1775 var column_counter: usize = 0;
1776 var single_line = true;
1777 var contains_newline = false;
1778 for (section_exprs) |expr, i| {
1779 sub_expr_buffer.shrinkRetainingCapacity(0);
1780 if (i + 1 < section_exprs.len) {
1781 try renderExpression(gpa, &auto_indenting_stream, tree, expr, .none);
1782 const width = sub_expr_buffer.items.len;
1783 const this_contains_newline = mem.indexOfScalar(u8, sub_expr_buffer.items, '\n') != null;
1784 contains_newline = contains_newline or this_contains_newline;
1785 expr_widths[i] = width;
1786 expr_newlines[i] = this_contains_newline;
1787
1788 if (!this_contains_newline) {
1789 const column = column_counter % row_size;
1790 column_widths[column] = std.math.max(column_widths[column], width);
1791
1792 const expr_last_token = tree.lastToken(expr) + 1;
1793 const next_expr = section_exprs[i + 1];
1794 column_counter += 1;
1795 if (!tree.tokensOnSameLine(expr_last_token, tree.firstToken(next_expr))) single_line = false;
1796 } else {
1797 single_line = false;
1798 column_counter = 0;
1799 }
1800 } else {
1801 try renderExpression(gpa, &auto_indenting_stream, tree, expr, .none);
1802 const width = sub_expr_buffer.items.len;
1803 contains_newline = contains_newline or mem.indexOfScalar(u8, sub_expr_buffer.items, '\n') != null;
1804 expr_widths[i] = width;
1805 expr_newlines[i] = contains_newline;
1806
1807 if (!contains_newline) {
1808 const column = column_counter % row_size;
1809 column_widths[column] = std.math.max(column_widths[column], width);
1810 }
1811 break;
1812 }
1813 }
20201814
2021 if (@"else".payload) |payload| {
2022 try renderExpression(allocator, ais, tree, payload, Space.Space);
1815 // Render exprs in current section.
1816 column_counter = 0;
1817 var last_col_index: usize = row_size - 1;
1818 for (section_exprs) |expr, i| {
1819 if (i + 1 < section_exprs.len) {
1820 const next_expr = section_exprs[i + 1];
1821 try renderExpression(gpa, ais, tree, expr, .none);
1822
1823 const comma = tree.lastToken(expr) + 1;
1824
1825 if (column_counter != last_col_index) {
1826 if (!expr_newlines[i] and !expr_newlines[i + 1]) {
1827 // Neither the current or next expression is multiline
1828 try renderToken(ais, tree, comma, .space); // ,
1829 assert(column_widths[column_counter % row_size] >= expr_widths[i]);
1830 const padding = column_widths[column_counter % row_size] - expr_widths[i];
1831 try ais.writer().writeByteNTimes(' ', padding);
1832
1833 column_counter += 1;
1834 continue;
1835 }
1836 }
1837 if (single_line and row_size != 1) {
1838 try renderToken(ais, tree, comma, .space); // ,
1839 continue;
20231840 }
20241841
2025 return renderExpression(allocator, ais, tree, @"else".body, space);
1842 column_counter = 0;
1843 try renderToken(ais, tree, comma, .newline); // ,
1844 try renderExtraNewline(ais, tree, next_expr);
20261845 } else {
2027 return renderExpression(allocator, ais, tree, if_node.body, space);
1846 try renderExpression(gpa, ais, tree, expr, .comma); // ,
20281847 }
2029 },
1848 }
20301849
2031 .Asm => {
2032 const asm_node = @fieldParentPtr(ast.Node.Asm, "base", base);
1850 if (expr_index == array_init.ast.elements.len)
1851 break;
1852 }
20331853
2034 try renderToken(tree, ais, asm_node.asm_token, Space.Space); // asm
1854 ais.popIndent();
1855 return renderToken(ais, tree, rbrace, space); // rbrace
1856}
20351857
2036 if (asm_node.volatile_token) |volatile_token| {
2037 try renderToken(tree, ais, volatile_token, Space.Space); // volatile
2038 try renderToken(tree, ais, tree.nextToken(volatile_token), Space.None); // (
2039 } else {
2040 try renderToken(tree, ais, tree.nextToken(asm_node.asm_token), Space.None); // (
2041 }
1858fn renderContainerDecl(
1859 gpa: *Allocator,
1860 ais: *Ais,
1861 tree: ast.Tree,
1862 container_decl_node: ast.Node.Index,
1863 container_decl: ast.full.ContainerDecl,
1864 space: Space,
1865) Error!void {
1866 const token_tags = tree.tokens.items(.tag);
1867 const node_tags = tree.nodes.items(.tag);
20421868
2043 asmblk: {
2044 ais.pushIndent();
2045 defer ais.popIndent();
1869 if (container_decl.layout_token) |layout_token| {
1870 try renderToken(ais, tree, layout_token, .space);
1871 }
20461872
2047 if (asm_node.outputs.len == 0 and asm_node.inputs.len == 0 and asm_node.clobbers.len == 0) {
2048 try renderExpression(allocator, ais, tree, asm_node.template, Space.None);
2049 break :asmblk;
2050 }
1873 var lbrace: ast.TokenIndex = undefined;
1874 if (container_decl.ast.enum_token) |enum_token| {
1875 try renderToken(ais, tree, container_decl.ast.main_token, .none); // union
1876 try renderToken(ais, tree, enum_token - 1, .none); // lparen
1877 try renderToken(ais, tree, enum_token, .none); // enum
1878 if (container_decl.ast.arg != 0) {
1879 try renderToken(ais, tree, enum_token + 1, .none); // lparen
1880 try renderExpression(gpa, ais, tree, container_decl.ast.arg, .none);
1881 const rparen = tree.lastToken(container_decl.ast.arg) + 1;
1882 try renderToken(ais, tree, rparen, .none); // rparen
1883 try renderToken(ais, tree, rparen + 1, .space); // rparen
1884 lbrace = rparen + 2;
1885 } else {
1886 try renderToken(ais, tree, enum_token + 1, .space); // rparen
1887 lbrace = enum_token + 2;
1888 }
1889 } else if (container_decl.ast.arg != 0) {
1890 try renderToken(ais, tree, container_decl.ast.main_token, .none); // union
1891 try renderToken(ais, tree, container_decl.ast.main_token + 1, .none); // lparen
1892 try renderExpression(gpa, ais, tree, container_decl.ast.arg, .none);
1893 const rparen = tree.lastToken(container_decl.ast.arg) + 1;
1894 try renderToken(ais, tree, rparen, .space); // rparen
1895 lbrace = rparen + 1;
1896 } else {
1897 try renderToken(ais, tree, container_decl.ast.main_token, .space); // union
1898 lbrace = container_decl.ast.main_token + 1;
1899 }
20511900
2052 try renderExpression(allocator, ais, tree, asm_node.template, Space.Newline);
1901 const rbrace = tree.lastToken(container_decl_node);
1902 if (container_decl.ast.members.len == 0) {
1903 ais.pushIndentNextLine();
1904 if (token_tags[lbrace + 1] == .container_doc_comment) {
1905 try renderToken(ais, tree, lbrace, .newline); // lbrace
1906 try renderContainerDocComments(ais, tree, lbrace + 1);
1907 } else {
1908 try renderToken(ais, tree, lbrace, .none); // lbrace
1909 }
1910 ais.popIndent();
1911 return renderToken(ais, tree, rbrace, space); // rbrace
1912 }
20531913
2054 ais.setIndentDelta(asm_indent_delta);
2055 defer ais.setIndentDelta(indent_delta);
1914 const src_has_trailing_comma = token_tags[rbrace - 1] == .comma;
1915 if (!src_has_trailing_comma) one_line: {
1916 // We can only print all the members in-line if all the members are fields.
1917 for (container_decl.ast.members) |member| {
1918 if (!node_tags[member].isContainerField()) break :one_line;
1919 }
1920 // All the declarations on the same line.
1921 try renderToken(ais, tree, lbrace, .space); // lbrace
1922 for (container_decl.ast.members) |member| {
1923 try renderMember(gpa, ais, tree, member, .space);
1924 }
1925 return renderToken(ais, tree, rbrace, space); // rbrace
1926 }
20561927
2057 const colon1 = tree.nextToken(asm_node.template.lastToken());
1928 // One member per line.
1929 ais.pushIndentNextLine();
1930 try renderToken(ais, tree, lbrace, .newline); // lbrace
1931 if (token_tags[lbrace + 1] == .container_doc_comment) {
1932 try renderContainerDocComments(ais, tree, lbrace + 1);
1933 }
1934 try renderMembers(gpa, ais, tree, container_decl.ast.members);
1935 ais.popIndent();
20581936
2059 const colon2 = if (asm_node.outputs.len == 0) blk: {
2060 try renderToken(tree, ais, colon1, Space.Newline); // :
1937 return renderToken(ais, tree, rbrace, space); // rbrace
1938}
20611939
2062 break :blk tree.nextToken(colon1);
2063 } else blk: {
2064 try renderToken(tree, ais, colon1, Space.Space); // :
1940fn renderAsm(
1941 gpa: *Allocator,
1942 ais: *Ais,
1943 tree: ast.Tree,
1944 asm_node: ast.full.Asm,
1945 space: Space,
1946) Error!void {
1947 const token_tags = tree.tokens.items(.tag);
20651948
2066 ais.pushIndent();
2067 defer ais.popIndent();
1949 try renderToken(ais, tree, asm_node.ast.asm_token, .space); // asm
20681950
2069 for (asm_node.outputs) |*asm_output, i| {
2070 if (i + 1 < asm_node.outputs.len) {
2071 const next_asm_output = asm_node.outputs[i + 1];
2072 try renderAsmOutput(allocator, ais, tree, asm_output, Space.None);
1951 if (asm_node.volatile_token) |volatile_token| {
1952 try renderToken(ais, tree, volatile_token, .space); // volatile
1953 try renderToken(ais, tree, volatile_token + 1, .none); // lparen
1954 } else {
1955 try renderToken(ais, tree, asm_node.ast.asm_token + 1, .none); // lparen
1956 }
20731957
2074 const comma = tree.prevToken(next_asm_output.firstToken());
2075 try renderToken(tree, ais, comma, Space.Newline); // ,
2076 try renderExtraNewlineToken(tree, ais, next_asm_output.firstToken());
2077 } else if (asm_node.inputs.len == 0 and asm_node.clobbers.len == 0) {
2078 try renderAsmOutput(allocator, ais, tree, asm_output, Space.Newline);
2079 break :asmblk;
1958 if (asm_node.ast.items.len == 0) {
1959 ais.pushIndent();
1960 if (asm_node.first_clobber) |first_clobber| {
1961 // asm ("foo" ::: "a", "b")
1962 // asm ("foo" ::: "a", "b",)
1963 try renderExpression(gpa, ais, tree, asm_node.ast.template, .space);
1964 // Render the three colons.
1965 try renderToken(ais, tree, first_clobber - 3, .none);
1966 try renderToken(ais, tree, first_clobber - 2, .none);
1967 try renderToken(ais, tree, first_clobber - 1, .space);
1968
1969 var tok_i = first_clobber;
1970 while (true) : (tok_i += 1) {
1971 try renderToken(ais, tree, tok_i, .none);
1972 tok_i += 1;
1973 switch (token_tags[tok_i]) {
1974 .r_paren => {
1975 ais.popIndent();
1976 return renderToken(ais, tree, tok_i, space);
1977 },
1978 .comma => {
1979 if (token_tags[tok_i + 1] == .r_paren) {
1980 ais.popIndent();
1981 return renderToken(ais, tree, tok_i + 1, space);
20801982 } else {
2081 try renderAsmOutput(allocator, ais, tree, asm_output, Space.Newline);
2082 const comma_or_colon = tree.nextToken(asm_output.lastToken());
2083 break :blk switch (tree.token_ids[comma_or_colon]) {
2084 .Comma => tree.nextToken(comma_or_colon),
2085 else => comma_or_colon,
2086 };
1983 try renderToken(ais, tree, tok_i, .space);
20871984 }
2088 }
2089 unreachable;
2090 };
1985 },
1986 else => unreachable,
1987 }
1988 }
1989 } else {
1990 // asm ("foo")
1991 try renderExpression(gpa, ais, tree, asm_node.ast.template, .none);
1992 ais.popIndent();
1993 return renderToken(ais, tree, asm_node.ast.rparen, space); // rparen
1994 }
1995 }
20911996
2092 const colon3 = if (asm_node.inputs.len == 0) blk: {
2093 try renderToken(tree, ais, colon2, Space.Newline); // :
2094 break :blk tree.nextToken(colon2);
2095 } else blk: {
2096 try renderToken(tree, ais, colon2, Space.Space); // :
2097 ais.pushIndent();
2098 defer ais.popIndent();
2099 for (asm_node.inputs) |*asm_input, i| {
2100 if (i + 1 < asm_node.inputs.len) {
2101 const next_asm_input = &asm_node.inputs[i + 1];
2102 try renderAsmInput(allocator, ais, tree, asm_input, Space.None);
2103
2104 const comma = tree.prevToken(next_asm_input.firstToken());
2105 try renderToken(tree, ais, comma, Space.Newline); // ,
2106 try renderExtraNewlineToken(tree, ais, next_asm_input.firstToken());
2107 } else if (asm_node.clobbers.len == 0) {
2108 try renderAsmInput(allocator, ais, tree, asm_input, Space.Newline);
2109 break :asmblk;
2110 } else {
2111 try renderAsmInput(allocator, ais, tree, asm_input, Space.Newline);
2112 const comma_or_colon = tree.nextToken(asm_input.lastToken());
2113 break :blk switch (tree.token_ids[comma_or_colon]) {
2114 .Comma => tree.nextToken(comma_or_colon),
2115 else => comma_or_colon,
2116 };
2117 }
2118 }
2119 unreachable;
1997 ais.pushIndent();
1998 try renderExpression(gpa, ais, tree, asm_node.ast.template, .newline);
1999 ais.setIndentDelta(asm_indent_delta);
2000 const colon1 = tree.lastToken(asm_node.ast.template) + 1;
2001
2002 const colon2 = if (asm_node.outputs.len == 0) colon2: {
2003 try renderToken(ais, tree, colon1, .newline); // :
2004 break :colon2 colon1 + 1;
2005 } else colon2: {
2006 try renderToken(ais, tree, colon1, .space); // :
2007
2008 ais.pushIndent();
2009 for (asm_node.outputs) |asm_output, i| {
2010 if (i + 1 < asm_node.outputs.len) {
2011 const next_asm_output = asm_node.outputs[i + 1];
2012 try renderAsmOutput(gpa, ais, tree, asm_output, .none);
2013
2014 const comma = tree.firstToken(next_asm_output) - 1;
2015 try renderToken(ais, tree, comma, .newline); // ,
2016 try renderExtraNewlineToken(ais, tree, tree.firstToken(next_asm_output));
2017 } else if (asm_node.inputs.len == 0 and asm_node.first_clobber == null) {
2018 try renderAsmOutput(gpa, ais, tree, asm_output, .newline);
2019 ais.popIndent();
2020 ais.setIndentDelta(indent_delta);
2021 ais.popIndent();
2022 return renderToken(ais, tree, asm_node.ast.rparen, space); // rparen
2023 } else {
2024 try renderAsmOutput(gpa, ais, tree, asm_output, .newline);
2025 const comma_or_colon = tree.lastToken(asm_output) + 1;
2026 ais.popIndent();
2027 break :colon2 switch (token_tags[comma_or_colon]) {
2028 .comma => comma_or_colon + 1,
2029 else => comma_or_colon,
21202030 };
2121
2122 try renderToken(tree, ais, colon3, Space.Space); // :
2123 ais.pushIndent();
2124 defer ais.popIndent();
2125 for (asm_node.clobbers) |clobber_node, i| {
2126 if (i + 1 >= asm_node.clobbers.len) {
2127 try renderExpression(allocator, ais, tree, clobber_node, Space.Newline);
2128 break :asmblk;
2129 } else {
2130 try renderExpression(allocator, ais, tree, clobber_node, Space.None);
2131 const comma = tree.nextToken(clobber_node.lastToken());
2132 try renderToken(tree, ais, comma, Space.Space); // ,
2133 }
2134 }
21352031 }
2032 } else unreachable;
2033 };
21362034
2137 return renderToken(tree, ais, asm_node.rparen, space);
2138 },
2035 const colon3 = if (asm_node.inputs.len == 0) colon3: {
2036 try renderToken(ais, tree, colon2, .newline); // :
2037 break :colon3 colon2 + 1;
2038 } else colon3: {
2039 try renderToken(ais, tree, colon2, .space); // :
2040 ais.pushIndent();
2041 for (asm_node.inputs) |asm_input, i| {
2042 if (i + 1 < asm_node.inputs.len) {
2043 const next_asm_input = asm_node.inputs[i + 1];
2044 try renderAsmInput(gpa, ais, tree, asm_input, .none);
2045
2046 const first_token = tree.firstToken(next_asm_input);
2047 try renderToken(ais, tree, first_token - 1, .newline); // ,
2048 try renderExtraNewlineToken(ais, tree, first_token);
2049 } else if (asm_node.first_clobber == null) {
2050 try renderAsmInput(gpa, ais, tree, asm_input, .newline);
2051 ais.popIndent();
2052 ais.setIndentDelta(indent_delta);
2053 ais.popIndent();
2054 return renderToken(ais, tree, asm_node.ast.rparen, space); // rparen
2055 } else {
2056 try renderAsmInput(gpa, ais, tree, asm_input, .newline);
2057 const comma_or_colon = tree.lastToken(asm_input) + 1;
2058 ais.popIndent();
2059 break :colon3 switch (token_tags[comma_or_colon]) {
2060 .comma => comma_or_colon + 1,
2061 else => comma_or_colon,
2062 };
2063 }
2064 }
2065 unreachable;
2066 };
21392067
2140 .EnumLiteral => {
2141 const enum_literal = @fieldParentPtr(ast.Node.EnumLiteral, "base", base);
2068 try renderToken(ais, tree, colon3, .space); // :
2069 const first_clobber = asm_node.first_clobber.?;
2070 var tok_i = first_clobber;
2071 while (true) {
2072 switch (token_tags[tok_i + 1]) {
2073 .r_paren => {
2074 ais.setIndentDelta(indent_delta);
2075 ais.popIndent();
2076 try renderToken(ais, tree, tok_i, .newline);
2077 return renderToken(ais, tree, tok_i + 1, space);
2078 },
2079 .comma => {
2080 try renderToken(ais, tree, tok_i, .none);
2081 try renderToken(ais, tree, tok_i + 1, .space);
2082 tok_i += 2;
2083 },
2084 else => unreachable,
2085 }
2086 } else unreachable; // TODO shouldn't need this on while(true)
2087}
21422088
2143 try renderToken(tree, ais, enum_literal.dot, Space.None); // .
2144 return renderToken(tree, ais, enum_literal.name, space); // name
2145 },
2089fn renderCall(
2090 gpa: *Allocator,
2091 ais: *Ais,
2092 tree: ast.Tree,
2093 call: ast.full.Call,
2094 space: Space,
2095) Error!void {
2096 const token_tags = tree.tokens.items(.tag);
2097 const main_tokens = tree.nodes.items(.main_token);
21462098
2147 .ContainerField,
2148 .Root,
2149 .VarDecl,
2150 .Use,
2151 .TestDecl,
2152 => unreachable,
2099 if (call.async_token) |async_token| {
2100 try renderToken(ais, tree, async_token, .space);
2101 }
2102 try renderExpression(gpa, ais, tree, call.ast.fn_expr, .none);
2103
2104 const lparen = call.ast.lparen;
2105 const params = call.ast.params;
2106 if (params.len == 0) {
2107 ais.pushIndentNextLine();
2108 try renderToken(ais, tree, lparen, .none);
2109 ais.popIndent();
2110 return renderToken(ais, tree, lparen + 1, space); // )
21532111 }
2154}
21552112
2156fn renderArrayType(
2157 allocator: *mem.Allocator,
2158 ais: anytype,
2159 tree: *ast.Tree,
2160 lbracket: ast.TokenIndex,
2161 rhs: *ast.Node,
2162 len_expr: *ast.Node,
2163 opt_sentinel: ?*ast.Node,
2164 space: Space,
2165) (@TypeOf(ais.*).Error || Error)!void {
2166 const rbracket = tree.nextToken(if (opt_sentinel) |sentinel|
2167 sentinel.lastToken()
2168 else
2169 len_expr.lastToken());
2113 const last_param = params[params.len - 1];
2114 const after_last_param_tok = tree.lastToken(last_param) + 1;
2115 if (token_tags[after_last_param_tok] == .comma) {
2116 ais.pushIndentNextLine();
2117 try renderToken(ais, tree, lparen, .newline); // (
2118 for (params) |param_node, i| {
2119 if (i + 1 < params.len) {
2120 try renderExpression(gpa, ais, tree, param_node, .none);
21702121
2171 const starts_with_comment = tree.token_ids[lbracket + 1] == .LineComment;
2172 const ends_with_comment = tree.token_ids[rbracket - 1] == .LineComment;
2173 const new_space = if (ends_with_comment) Space.Newline else Space.None;
2174 {
2175 const do_indent = (starts_with_comment or ends_with_comment);
2176 if (do_indent) ais.pushIndent();
2177 defer if (do_indent) ais.popIndent();
2122 // Unindent the comma for multiline string literals.
2123 const is_multiline_string =
2124 token_tags[tree.firstToken(param_node)] == .multiline_string_literal_line;
2125 if (is_multiline_string) ais.popIndent();
21782126
2179 try renderToken(tree, ais, lbracket, Space.None); // [
2180 try renderExpression(allocator, ais, tree, len_expr, new_space);
2127 const comma = tree.lastToken(param_node) + 1;
2128 try renderToken(ais, tree, comma, .newline); // ,
21812129
2182 if (starts_with_comment) {
2183 try ais.maybeInsertNewline();
2130 if (is_multiline_string) ais.pushIndent();
2131
2132 try renderExtraNewline(ais, tree, params[i + 1]);
2133 } else {
2134 try renderExpression(gpa, ais, tree, param_node, .comma);
2135 }
21842136 }
2185 if (opt_sentinel) |sentinel| {
2186 const colon_token = tree.prevToken(sentinel.firstToken());
2187 try renderToken(tree, ais, colon_token, Space.None); // :
2188 try renderExpression(allocator, ais, tree, sentinel, Space.None);
2137 ais.popIndent();
2138 return renderToken(ais, tree, after_last_param_tok + 1, space); // )
2139 }
2140
2141 try renderToken(ais, tree, lparen, .none); // (
2142
2143 for (params) |param_node, i| {
2144 const first_param_token = tree.firstToken(param_node);
2145 if (token_tags[first_param_token] == .multiline_string_literal_line or
2146 hasSameLineComment(tree, first_param_token - 1))
2147 {
2148 ais.pushIndentOneShot();
21892149 }
2190 if (starts_with_comment) {
2191 try ais.maybeInsertNewline();
2150 try renderExpression(gpa, ais, tree, param_node, .none);
2151
2152 if (i + 1 < params.len) {
2153 const comma = tree.lastToken(param_node) + 1;
2154 const next_multiline_string =
2155 token_tags[tree.firstToken(params[i + 1])] == .multiline_string_literal_line;
2156 const comma_space: Space = if (next_multiline_string) .none else .space;
2157 try renderToken(ais, tree, comma, comma_space);
21922158 }
21932159 }
2194 try renderToken(tree, ais, rbracket, Space.None); // ]
21952160
2196 return renderExpression(allocator, ais, tree, rhs, space);
2161 return renderToken(ais, tree, after_last_param_tok, space); // )
21972162}
21982163
2199fn renderAsmOutput(
2200 allocator: *mem.Allocator,
2201 ais: anytype,
2202 tree: *ast.Tree,
2203 asm_output: *const ast.Node.Asm.Output,
2204 space: Space,
2205) (@TypeOf(ais.*).Error || Error)!void {
2206 try ais.writer().writeAll("[");
2207 try renderExpression(allocator, ais, tree, asm_output.symbolic_name, Space.None);
2208 try ais.writer().writeAll("] ");
2209 try renderExpression(allocator, ais, tree, asm_output.constraint, Space.None);
2210 try ais.writer().writeAll(" (");
2211
2212 switch (asm_output.kind) {
2213 .Variable => |variable_name| {
2214 try renderExpression(allocator, ais, tree, &variable_name.base, Space.None);
2164/// Renders the given expression indented, popping the indent before rendering
2165/// any following line comments
2166fn renderExpressionIndented(gpa: *Allocator, ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Space) Error!void {
2167 const token_starts = tree.tokens.items(.start);
2168 const token_tags = tree.tokens.items(.tag);
2169
2170 ais.pushIndent();
2171
2172 var last_token = tree.lastToken(node);
2173 const punctuation = switch (space) {
2174 .none, .space, .newline, .skip => false,
2175 .comma => true,
2176 .comma_space => token_tags[last_token + 1] == .comma,
2177 .semicolon => token_tags[last_token + 1] == .semicolon,
2178 };
2179
2180 try renderExpression(gpa, ais, tree, node, if (punctuation) .none else .skip);
2181
2182 switch (space) {
2183 .none, .space, .newline, .skip => {},
2184 .comma => {
2185 if (token_tags[last_token + 1] == .comma) {
2186 try renderToken(ais, tree, last_token + 1, .skip);
2187 last_token += 1;
2188 } else {
2189 try ais.writer().writeByte(',');
2190 }
2191 },
2192 .comma_space => if (token_tags[last_token + 1] == .comma) {
2193 try renderToken(ais, tree, last_token + 1, .skip);
2194 last_token += 1;
22152195 },
2216 .Return => |return_type| {
2217 try ais.writer().writeAll("-> ");
2218 try renderExpression(allocator, ais, tree, return_type, Space.None);
2196 .semicolon => if (token_tags[last_token + 1] == .semicolon) {
2197 try renderToken(ais, tree, last_token + 1, .skip);
2198 last_token += 1;
22192199 },
22202200 }
22212201
2222 return renderToken(tree, ais, asm_output.lastToken(), space); // )
2223}
2202 ais.popIndent();
22242203
2225fn renderAsmInput(
2226 allocator: *mem.Allocator,
2227 ais: anytype,
2228 tree: *ast.Tree,
2229 asm_input: *const ast.Node.Asm.Input,
2230 space: Space,
2231) (@TypeOf(ais.*).Error || Error)!void {
2232 try ais.writer().writeAll("[");
2233 try renderExpression(allocator, ais, tree, asm_input.symbolic_name, Space.None);
2234 try ais.writer().writeAll("] ");
2235 try renderExpression(allocator, ais, tree, asm_input.constraint, Space.None);
2236 try ais.writer().writeAll(" (");
2237 try renderExpression(allocator, ais, tree, asm_input.expr, Space.None);
2238 return renderToken(tree, ais, asm_input.lastToken(), space); // )
2239}
2204 if (space == .skip) return;
22402205
2241fn renderVarDecl(
2242 allocator: *mem.Allocator,
2243 ais: anytype,
2244 tree: *ast.Tree,
2245 var_decl: *ast.Node.VarDecl,
2246) (@TypeOf(ais.*).Error || Error)!void {
2247 if (var_decl.getVisibToken()) |visib_token| {
2248 try renderToken(tree, ais, visib_token, Space.Space); // pub
2249 }
2206 const comment_start = token_starts[last_token] + tokenSliceForRender(tree, last_token).len;
2207 const comment = try renderComments(ais, tree, comment_start, token_starts[last_token + 1]);
22502208
2251 if (var_decl.getExternExportToken()) |extern_export_token| {
2252 try renderToken(tree, ais, extern_export_token, Space.Space); // extern
2209 if (!comment) switch (space) {
2210 .none => {},
2211 .space,
2212 .comma_space,
2213 => try ais.writer().writeByte(' '),
2214 .newline,
2215 .comma,
2216 .semicolon,
2217 => try ais.insertNewline(),
2218 .skip => unreachable,
2219 };
2220}
22532221
2254 if (var_decl.getLibName()) |lib_name| {
2255 try renderExpression(allocator, ais, tree, lib_name, Space.Space); // "lib"
2256 }
2222/// Render an expression, and the comma that follows it, if it is present in the source.
2223fn renderExpressionComma(gpa: *Allocator, ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Space) Error!void {
2224 const token_tags = tree.tokens.items(.tag);
2225 const maybe_comma = tree.lastToken(node) + 1;
2226 if (token_tags[maybe_comma] == .comma) {
2227 try renderExpression(gpa, ais, tree, node, .none);
2228 return renderToken(ais, tree, maybe_comma, space);
2229 } else {
2230 return renderExpression(gpa, ais, tree, node, space);
22572231 }
2232}
22582233
2259 if (var_decl.getComptimeToken()) |comptime_token| {
2260 try renderToken(tree, ais, comptime_token, Space.Space); // comptime
2234fn renderTokenComma(ais: *Ais, tree: ast.Tree, token: ast.TokenIndex, space: Space) Error!void {
2235 const token_tags = tree.tokens.items(.tag);
2236 const maybe_comma = token + 1;
2237 if (token_tags[maybe_comma] == .comma) {
2238 try renderToken(ais, tree, token, .none);
2239 return renderToken(ais, tree, maybe_comma, space);
2240 } else {
2241 return renderToken(ais, tree, token, space);
22612242 }
2243}
22622244
2263 if (var_decl.getThreadLocalToken()) |thread_local_token| {
2264 try renderToken(tree, ais, thread_local_token, Space.Space); // threadlocal
2265 }
2266 try renderToken(tree, ais, var_decl.mut_token, Space.Space); // var
2245const Space = enum {
2246 /// Output the token lexeme only.
2247 none,
2248 /// Output the token lexeme followed by a single space.
2249 space,
2250 /// Output the token lexeme followed by a newline.
2251 newline,
2252 /// If the next token is a comma, render it as well. If not, insert one.
2253 /// In either case, a newline will be inserted afterwards.
2254 comma,
2255 /// Additionally consume the next token if it is a comma.
2256 /// In either case, a space will be inserted afterwards.
2257 comma_space,
2258 /// Additionally consume the next token if it is a semicolon.
2259 /// In either case, a newline will be inserted afterwards.
2260 semicolon,
2261 /// Skip rendering whitespace and comments. If this is used, the caller
2262 /// *must* handle handle whitespace and comments manually.
2263 skip,
2264};
22672265
2268 const name_space = if (var_decl.getTypeNode() == null and
2269 (var_decl.getAlignNode() != null or
2270 var_decl.getSectionNode() != null or
2271 var_decl.getInitNode() != null))
2272 Space.Space
2273 else
2274 Space.None;
2275 try renderToken(tree, ais, var_decl.name_token, name_space);
2276
2277 if (var_decl.getTypeNode()) |type_node| {
2278 try renderToken(tree, ais, tree.nextToken(var_decl.name_token), Space.Space);
2279 const s = if (var_decl.getAlignNode() != null or
2280 var_decl.getSectionNode() != null or
2281 var_decl.getInitNode() != null) Space.Space else Space.None;
2282 try renderExpression(allocator, ais, tree, type_node, s);
2283 }
2284
2285 if (var_decl.getAlignNode()) |align_node| {
2286 const lparen = tree.prevToken(align_node.firstToken());
2287 const align_kw = tree.prevToken(lparen);
2288 const rparen = tree.nextToken(align_node.lastToken());
2289 try renderToken(tree, ais, align_kw, Space.None); // align
2290 try renderToken(tree, ais, lparen, Space.None); // (
2291 try renderExpression(allocator, ais, tree, align_node, Space.None);
2292 const s = if (var_decl.getSectionNode() != null or var_decl.getInitNode() != null) Space.Space else Space.None;
2293 try renderToken(tree, ais, rparen, s); // )
2294 }
2295
2296 if (var_decl.getSectionNode()) |section_node| {
2297 const lparen = tree.prevToken(section_node.firstToken());
2298 const section_kw = tree.prevToken(lparen);
2299 const rparen = tree.nextToken(section_node.lastToken());
2300 try renderToken(tree, ais, section_kw, Space.None); // linksection
2301 try renderToken(tree, ais, lparen, Space.None); // (
2302 try renderExpression(allocator, ais, tree, section_node, Space.None);
2303 const s = if (var_decl.getInitNode() != null) Space.Space else Space.None;
2304 try renderToken(tree, ais, rparen, s); // )
2305 }
2306
2307 if (var_decl.getInitNode()) |init_node| {
2308 const eq_token = var_decl.getEqToken().?;
2309 const eq_space = blk: {
2310 const loc = tree.tokenLocation(tree.token_locs[eq_token].end, tree.nextToken(eq_token));
2311 break :blk if (loc.line == 0) Space.Space else Space.Newline;
2312 };
2266fn renderToken(ais: *Ais, tree: ast.Tree, token_index: ast.TokenIndex, space: Space) Error!void {
2267 const token_tags = tree.tokens.items(.tag);
2268 const token_starts = tree.tokens.items(.start);
23132269
2314 {
2315 ais.pushIndent();
2316 defer ais.popIndent();
2317 try renderToken(tree, ais, eq_token, eq_space); // =
2318 }
2319 ais.pushIndentOneShot();
2320 try renderExpression(allocator, ais, tree, init_node, Space.None);
2321 }
2270 const token_start = token_starts[token_index];
2271 const lexeme = tokenSliceForRender(tree, token_index);
23222272
2323 try renderToken(tree, ais, var_decl.semicolon_token, Space.Newline);
2324}
2273 try ais.writer().writeAll(lexeme);
23252274
2326fn renderParamDecl(
2327 allocator: *mem.Allocator,
2328 ais: anytype,
2329 tree: *ast.Tree,
2330 param_decl: ast.Node.FnProto.ParamDecl,
2331 space: Space,
2332) (@TypeOf(ais.*).Error || Error)!void {
2333 try renderDocComments(tree, ais, param_decl, param_decl.doc_comments);
2275 if (space == .skip) return;
23342276
2335 if (param_decl.comptime_token) |comptime_token| {
2336 try renderToken(tree, ais, comptime_token, Space.Space);
2337 }
2338 if (param_decl.noalias_token) |noalias_token| {
2339 try renderToken(tree, ais, noalias_token, Space.Space);
2340 }
2341 if (param_decl.name_token) |name_token| {
2342 try renderToken(tree, ais, name_token, Space.None);
2343 try renderToken(tree, ais, tree.nextToken(name_token), Space.Space); // :
2344 }
2345 switch (param_decl.param_type) {
2346 .any_type, .type_expr => |node| try renderExpression(allocator, ais, tree, node, space),
2277 if (space == .comma and token_tags[token_index + 1] != .comma) {
2278 try ais.writer().writeByte(',');
23472279 }
2348}
23492280
2350fn renderStatement(
2351 allocator: *mem.Allocator,
2352 ais: anytype,
2353 tree: *ast.Tree,
2354 base: *ast.Node,
2355) (@TypeOf(ais.*).Error || Error)!void {
2356 switch (base.tag) {
2357 .VarDecl => {
2358 const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base);
2359 try renderVarDecl(allocator, ais, tree, var_decl);
2281 const comment = try renderComments(ais, tree, token_start + lexeme.len, token_starts[token_index + 1]);
2282 switch (space) {
2283 .none => {},
2284 .space => if (!comment) try ais.writer().writeByte(' '),
2285 .newline => if (!comment) try ais.insertNewline(),
2286
2287 .comma => if (token_tags[token_index + 1] == .comma) {
2288 try renderToken(ais, tree, token_index + 1, .newline);
2289 } else if (!comment) {
2290 try ais.insertNewline();
23602291 },
2361 else => {
2362 if (base.requireSemiColon()) {
2363 try renderExpression(allocator, ais, tree, base, Space.None);
23642292
2365 const semicolon_index = tree.nextToken(base.lastToken());
2366 assert(tree.token_ids[semicolon_index] == .Semicolon);
2367 try renderToken(tree, ais, semicolon_index, Space.Newline);
2368 } else {
2369 try renderExpression(allocator, ais, tree, base, Space.Newline);
2370 }
2293 .comma_space => if (token_tags[token_index + 1] == .comma) {
2294 try renderToken(ais, tree, token_index + 1, .space);
2295 } else if (!comment) {
2296 try ais.writer().writeByte(' ');
2297 },
2298
2299 .semicolon => if (token_tags[token_index + 1] == .semicolon) {
2300 try renderToken(ais, tree, token_index + 1, .newline);
2301 } else if (!comment) {
2302 try ais.insertNewline();
23712303 },
2304
2305 .skip => unreachable,
23722306 }
23732307}
23742308
2375const Space = enum {
2376 None,
2377 Newline,
2378 Comma,
2379 Space,
2380 SpaceOrOutdent,
2381 NoNewline,
2382 NoComment,
2383 BlockStart,
2384};
2309/// Returns true if there exists a comment between the start of token
2310/// `start_token` and the start of token `end_token`. This is used to determine
2311/// if e.g. a fn_proto should be wrapped and have a trailing comma inserted
2312/// even if there is none in the source.
2313fn hasComment(tree: ast.Tree, start_token: ast.TokenIndex, end_token: ast.TokenIndex) bool {
2314 const token_starts = tree.tokens.items(.start);
23852315
2386fn renderTokenOffset(
2387 tree: *ast.Tree,
2388 ais: anytype,
2389 token_index: ast.TokenIndex,
2390 space: Space,
2391 token_skip_bytes: usize,
2392) (@TypeOf(ais.*).Error || Error)!void {
2393 if (space == Space.BlockStart) {
2394 // If placing the lbrace on the current line would cause an uggly gap then put the lbrace on the next line
2395 const new_space = if (ais.isLineOverIndented()) Space.Newline else Space.Space;
2396 return renderToken(tree, ais, token_index, new_space);
2397 }
2316 const start = token_starts[start_token];
2317 const end = token_starts[end_token];
23982318
2399 var token_loc = tree.token_locs[token_index];
2400 try ais.writer().writeAll(mem.trimRight(u8, tree.tokenSliceLoc(token_loc)[token_skip_bytes..], " "));
2319 return mem.indexOf(u8, tree.source[start..end], "//") != null;
2320}
24012321
2402 if (space == Space.NoComment)
2403 return;
2322/// Assumes that start is the first byte past the previous token and
2323/// that end is the last byte before the next token.
2324fn renderComments(ais: *Ais, tree: ast.Tree, start: usize, end: usize) Error!bool {
2325 var index: usize = start;
2326 while (mem.indexOf(u8, tree.source[index..end], "//")) |offset| {
2327 const comment_start = index + offset;
24042328
2405 var next_token_id = tree.token_ids[token_index + 1];
2406 var next_token_loc = tree.token_locs[token_index + 1];
2329 // If there is no newline, the comment ends with EOF
2330 const newline_index = mem.indexOfScalar(u8, tree.source[comment_start..end], '\n');
2331 const newline = if (newline_index) |i| comment_start + i else null;
24072332
2408 if (space == Space.Comma) switch (next_token_id) {
2409 .Comma => return renderToken(tree, ais, token_index + 1, Space.Newline),
2410 .LineComment => {
2411 try ais.writer().writeAll(", ");
2412 return renderToken(tree, ais, token_index + 1, Space.Newline);
2413 },
2414 else => {
2415 if (token_index + 2 < tree.token_ids.len and
2416 tree.token_ids[token_index + 2] == .MultilineStringLiteralLine)
2417 {
2418 try ais.writer().writeAll(",");
2419 return;
2420 } else {
2421 try ais.writer().writeAll(",");
2333 const untrimmed_comment = tree.source[comment_start .. newline orelse tree.source.len];
2334 const trimmed_comment = mem.trimRight(u8, untrimmed_comment, &std.ascii.spaces);
2335
2336 // Don't leave any whitespace at the start of the file
2337 if (index != 0) {
2338 if (index == start and mem.containsAtLeast(u8, tree.source[index..comment_start], 2, "\n")) {
2339 // Leave up to one empty line before the first comment
24222340 try ais.insertNewline();
2423 return;
2341 try ais.insertNewline();
2342 } else if (mem.indexOfScalar(u8, tree.source[index..comment_start], '\n') != null) {
2343 // Respect the newline directly before the comment.
2344 // Note: This allows an empty line between comments
2345 try ais.insertNewline();
2346 } else if (index == start) {
2347 // Otherwise if the first comment is on the same line as
2348 // the token before it, prefix it with a single space.
2349 try ais.writer().writeByte(' ');
24242350 }
2425 },
2426 };
2427
2428 // Skip over same line doc comments
2429 var offset: usize = 1;
2430 if (next_token_id == .DocComment) {
2431 const loc = tree.tokenLocationLoc(token_loc.end, next_token_loc);
2432 if (loc.line == 0) {
2433 offset += 1;
2434 next_token_id = tree.token_ids[token_index + offset];
2435 next_token_loc = tree.token_locs[token_index + offset];
24362351 }
2437 }
24382352
2439 if (next_token_id != .LineComment) {
2440 switch (space) {
2441 Space.None, Space.NoNewline => return,
2442 Space.Newline => {
2443 if (next_token_id == .MultilineStringLiteralLine) {
2444 return;
2445 } else {
2446 try ais.insertNewline();
2447 return;
2448 }
2449 },
2450 Space.Space, Space.SpaceOrOutdent => {
2451 if (next_token_id == .MultilineStringLiteralLine)
2452 return;
2453 try ais.writer().writeByte(' ');
2454 return;
2455 },
2456 Space.NoComment, Space.Comma, Space.BlockStart => unreachable,
2457 }
2458 }
2353 try ais.writer().print("{s}\n", .{trimmed_comment});
2354 index = 1 + (newline orelse return true);
24592355
2460 while (true) {
2461 const comment_is_empty = mem.trimRight(u8, tree.tokenSliceLoc(next_token_loc), " ").len == 2;
2462 if (comment_is_empty) {
2463 switch (space) {
2464 Space.Newline => {
2465 offset += 1;
2466 token_loc = next_token_loc;
2467 next_token_id = tree.token_ids[token_index + offset];
2468 next_token_loc = tree.token_locs[token_index + offset];
2469 if (next_token_id != .LineComment) {
2470 try ais.insertNewline();
2471 return;
2472 }
2473 },
2474 else => break,
2356 if (ais.disabled_offset) |disabled_offset| {
2357 if (mem.eql(u8, trimmed_comment, "// zig fmt: on")) {
2358 // write the source for which formatting was disabled directly
2359 // to the underlying writer, fixing up invaild whitespace
2360 try writeFixingWhitespace(ais.underlying_writer, tree.source[disabled_offset..index]);
2361 ais.disabled_offset = null;
24752362 }
2476 } else {
2477 break;
2363 } else if (mem.eql(u8, trimmed_comment, "// zig fmt: off")) {
2364 ais.disabled_offset = index;
24782365 }
24792366 }
24802367
2481 var loc = tree.tokenLocationLoc(token_loc.end, next_token_loc);
2482 if (loc.line == 0) {
2483 if (tree.token_ids[token_index] != .MultilineStringLiteralLine) {
2484 try ais.writer().writeByte(' ');
2485 }
2486 try ais.writer().writeAll(mem.trimRight(u8, tree.tokenSliceLoc(next_token_loc), " "));
2487 offset = 2;
2488 token_loc = next_token_loc;
2489 next_token_loc = tree.token_locs[token_index + offset];
2490 next_token_id = tree.token_ids[token_index + offset];
2491 if (next_token_id != .LineComment) {
2492 switch (space) {
2493 .None, .Space, .SpaceOrOutdent => {
2494 try ais.insertNewline();
2495 },
2496 .Newline => {
2497 if (next_token_id == .MultilineStringLiteralLine) {
2498 return;
2499 } else {
2500 try ais.insertNewline();
2501 return;
2502 }
2503 },
2504 .NoNewline => {},
2505 .NoComment, .Comma, .BlockStart => unreachable,
2506 }
2507 return;
2508 }
2509 loc = tree.tokenLocationLoc(token_loc.end, next_token_loc);
2368 if (index != start and mem.containsAtLeast(u8, tree.source[index - 1 .. end], 2, "\n")) {
2369 try ais.insertNewline();
25102370 }
25112371
2512 while (true) {
2513 // translate-c doesn't generate correct newlines
2514 // in generated code (loc.line == 0) so treat that case
2515 // as though there was meant to be a newline between the tokens
2516 var newline_count = if (loc.line <= 1) @as(u8, 1) else @as(u8, 2);
2517 while (newline_count > 0) : (newline_count -= 1) try ais.insertNewline();
2518 try ais.writer().writeAll(mem.trimRight(u8, tree.tokenSliceLoc(next_token_loc), " "));
2519
2520 offset += 1;
2521 token_loc = next_token_loc;
2522 next_token_loc = tree.token_locs[token_index + offset];
2523 next_token_id = tree.token_ids[token_index + offset];
2524 if (next_token_id != .LineComment) {
2525 switch (space) {
2526 .Newline => {
2527 if (next_token_id == .MultilineStringLiteralLine) {
2528 return;
2529 } else {
2530 try ais.insertNewline();
2531 return;
2532 }
2533 },
2534 .None, .Space, .SpaceOrOutdent => {
2535 try ais.insertNewline();
2536 },
2537 .NoNewline => {},
2538 .NoComment, .Comma, .BlockStart => unreachable,
2539 }
2540 return;
2541 }
2542 loc = tree.tokenLocationLoc(token_loc.end, next_token_loc);
2372 return index != start;
2373}
2374
2375fn renderExtraNewline(ais: *Ais, tree: ast.Tree, node: ast.Node.Index) Error!void {
2376 return renderExtraNewlineToken(ais, tree, tree.firstToken(node));
2377}
2378
2379/// Check if there is an empty line immediately before the given token. If so, render it.
2380fn renderExtraNewlineToken(ais: *Ais, tree: ast.Tree, token_index: ast.TokenIndex) Error!void {
2381 const token_starts = tree.tokens.items(.start);
2382 const token_start = token_starts[token_index];
2383 if (token_start == 0) return;
2384 const prev_token_end = if (token_index == 0)
2385 0
2386 else
2387 token_starts[token_index - 1] + tokenSliceForRender(tree, token_index - 1).len;
2388
2389 // If there is a comment present, it will handle the empty line
2390 if (mem.indexOf(u8, tree.source[prev_token_end..token_start], "//") != null) return;
2391
2392 // Iterate backwards to the end of the previous token, stopping if a
2393 // non-whitespace character is encountered or two newlines have been found.
2394 var i = token_start - 1;
2395 var newlines: u2 = 0;
2396 while (std.ascii.isSpace(tree.source[i])) : (i -= 1) {
2397 if (tree.source[i] == '\n') newlines += 1;
2398 if (newlines == 2) return ais.insertNewline();
2399 if (i == prev_token_end) break;
25432400 }
25442401}
25452402
2546fn renderToken(
2547 tree: *ast.Tree,
2548 ais: anytype,
2549 token_index: ast.TokenIndex,
2550 space: Space,
2551) (@TypeOf(ais.*).Error || Error)!void {
2552 return renderTokenOffset(tree, ais, token_index, space, 0);
2403/// end_token is the token one past the last doc comment token. This function
2404/// searches backwards from there.
2405fn renderDocComments(ais: *Ais, tree: ast.Tree, end_token: ast.TokenIndex) Error!void {
2406 // Search backwards for the first doc comment.
2407 const token_tags = tree.tokens.items(.tag);
2408 if (end_token == 0) return;
2409 var tok = end_token - 1;
2410 while (token_tags[tok] == .doc_comment) {
2411 if (tok == 0) break;
2412 tok -= 1;
2413 } else {
2414 tok += 1;
2415 }
2416 const first_tok = tok;
2417 if (first_tok == end_token) return;
2418 try renderExtraNewlineToken(ais, tree, first_tok);
2419
2420 while (token_tags[tok] == .doc_comment) : (tok += 1) {
2421 try renderToken(ais, tree, tok, .newline);
2422 }
25532423}
25542424
2555fn renderDocComments(
2556 tree: *ast.Tree,
2557 ais: anytype,
2558 node: anytype,
2559 doc_comments: ?*ast.Node.DocComment,
2560) (@TypeOf(ais.*).Error || Error)!void {
2561 const comment = doc_comments orelse return;
2562 return renderDocCommentsToken(tree, ais, comment, node.firstToken());
2425/// start_token is first container doc comment token.
2426fn renderContainerDocComments(ais: *Ais, tree: ast.Tree, start_token: ast.TokenIndex) Error!void {
2427 const token_tags = tree.tokens.items(.tag);
2428 var tok = start_token;
2429 while (token_tags[tok] == .container_doc_comment) : (tok += 1) {
2430 try renderToken(ais, tree, tok, .newline);
2431 }
2432 // Render extra newline if there is one between final container doc comment and
2433 // the next token. If the next token is a doc comment, that code path
2434 // will have its own logic to insert a newline.
2435 if (token_tags[tok] != .doc_comment) {
2436 try renderExtraNewlineToken(ais, tree, tok);
2437 }
25632438}
25642439
2565fn renderDocCommentsToken(
2566 tree: *ast.Tree,
2567 ais: anytype,
2568 comment: *ast.Node.DocComment,
2569 first_token: ast.TokenIndex,
2570) (@TypeOf(ais.*).Error || Error)!void {
2571 var tok_i = comment.first_line;
2572 while (true) : (tok_i += 1) {
2573 switch (tree.token_ids[tok_i]) {
2574 .DocComment, .ContainerDocComment => {
2575 if (comment.first_line < first_token) {
2576 try renderToken(tree, ais, tok_i, Space.Newline);
2577 } else {
2578 try renderToken(tree, ais, tok_i, Space.NoComment);
2579 try ais.insertNewline();
2580 }
2581 },
2582 .LineComment => continue,
2583 else => break,
2584 }
2440fn tokenSliceForRender(tree: ast.Tree, token_index: ast.TokenIndex) []const u8 {
2441 var ret = tree.tokenSlice(token_index);
2442 if (tree.tokens.items(.tag)[token_index] == .multiline_string_literal_line) {
2443 assert(ret[ret.len - 1] == '\n');
2444 ret.len -= 1;
25852445 }
2446 return ret;
25862447}
25872448
2588fn nodeIsBlock(base: *const ast.Node) bool {
2589 return switch (base.tag) {
2590 .Block,
2591 .LabeledBlock,
2592 .If,
2593 .For,
2594 .While,
2595 .Switch,
2596 => true,
2597 else => false,
2449fn hasSameLineComment(tree: ast.Tree, token_index: ast.TokenIndex) bool {
2450 const token_starts = tree.tokens.items(.start);
2451 const between_source = tree.source[token_starts[token_index]..token_starts[token_index + 1]];
2452 for (between_source) |byte| switch (byte) {
2453 '\n' => return false,
2454 '/' => return true,
2455 else => continue,
2456 };
2457 return false;
2458}
2459
2460/// Returns `true` if and only if there are any tokens or line comments between
2461/// start_token and end_token.
2462fn anythingBetween(tree: ast.Tree, start_token: ast.TokenIndex, end_token: ast.TokenIndex) bool {
2463 if (start_token + 1 != end_token) return true;
2464 const token_starts = tree.tokens.items(.start);
2465 const between_source = tree.source[token_starts[start_token]..token_starts[start_token + 1]];
2466 for (between_source) |byte| switch (byte) {
2467 '/' => return true,
2468 else => continue,
25982469 };
2470 return false;
25992471}
26002472
2601fn nodeCausesSliceOpSpace(base: *ast.Node) bool {
2602 return switch (base.tag) {
2603 .Catch,
2604 .Add,
2605 .AddWrap,
2606 .ArrayCat,
2607 .ArrayMult,
2608 .Assign,
2609 .AssignBitAnd,
2610 .AssignBitOr,
2611 .AssignBitShiftLeft,
2612 .AssignBitShiftRight,
2613 .AssignBitXor,
2614 .AssignDiv,
2615 .AssignSub,
2616 .AssignSubWrap,
2617 .AssignMod,
2618 .AssignAdd,
2619 .AssignAddWrap,
2620 .AssignMul,
2621 .AssignMulWrap,
2622 .BangEqual,
2623 .BitAnd,
2624 .BitOr,
2625 .BitShiftLeft,
2626 .BitShiftRight,
2627 .BitXor,
2628 .BoolAnd,
2629 .BoolOr,
2630 .Div,
2631 .EqualEqual,
2632 .ErrorUnion,
2633 .GreaterOrEqual,
2634 .GreaterThan,
2635 .LessOrEqual,
2636 .LessThan,
2637 .MergeErrorSets,
2638 .Mod,
2639 .Mul,
2640 .MulWrap,
2641 .Range,
2642 .Sub,
2643 .SubWrap,
2644 .OrElse,
2473fn writeFixingWhitespace(writer: std.ArrayList(u8).Writer, slice: []const u8) Error!void {
2474 for (slice) |byte| switch (byte) {
2475 '\t' => try writer.writeAll(" " ** 4),
2476 '\r' => {},
2477 else => try writer.writeByte(byte),
2478 };
2479}
2480
2481fn nodeIsBlock(tag: ast.Node.Tag) bool {
2482 return switch (tag) {
2483 .block,
2484 .block_semicolon,
2485 .block_two,
2486 .block_two_semicolon,
2487 .@"if",
2488 .if_simple,
2489 .@"for",
2490 .for_simple,
2491 .@"while",
2492 .while_simple,
2493 .while_cont,
2494 .@"switch",
2495 .switch_comma,
26452496 => true,
2497 else => false,
2498 };
2499}
26462500
2501fn nodeIsIf(tag: ast.Node.Tag) bool {
2502 return switch (tag) {
2503 .@"if", .if_simple => true,
26472504 else => false,
26482505 };
26492506}
26502507
2651fn copyFixingWhitespace(ais: anytype, slice: []const u8) @TypeOf(ais.*).Error!void {
2652 for (slice) |byte| switch (byte) {
2653 '\t' => try ais.writer().writeAll(" "),
2654 '\r' => {},
2655 else => try ais.writer().writeByte(byte),
2508fn nodeCausesSliceOpSpace(tag: ast.Node.Tag) bool {
2509 return switch (tag) {
2510 .@"catch",
2511 .add,
2512 .add_wrap,
2513 .array_cat,
2514 .array_mult,
2515 .assign,
2516 .assign_bit_and,
2517 .assign_bit_or,
2518 .assign_bit_shift_left,
2519 .assign_bit_shift_right,
2520 .assign_bit_xor,
2521 .assign_div,
2522 .assign_sub,
2523 .assign_sub_wrap,
2524 .assign_mod,
2525 .assign_add,
2526 .assign_add_wrap,
2527 .assign_mul,
2528 .assign_mul_wrap,
2529 .bang_equal,
2530 .bit_and,
2531 .bit_or,
2532 .bit_shift_left,
2533 .bit_shift_right,
2534 .bit_xor,
2535 .bool_and,
2536 .bool_or,
2537 .div,
2538 .equal_equal,
2539 .error_union,
2540 .greater_or_equal,
2541 .greater_than,
2542 .less_or_equal,
2543 .less_than,
2544 .merge_error_sets,
2545 .mod,
2546 .mul,
2547 .mul_wrap,
2548 .sub,
2549 .sub_wrap,
2550 .@"orelse",
2551 => true,
2552
2553 else => false,
26562554 };
26572555}
26582556
26592557// Returns the number of nodes in `expr` that are on the same line as `rtoken`,
26602558// or null if they all are on the same line.
2661fn rowSize(tree: *ast.Tree, exprs: []*ast.Node, rtoken: ast.TokenIndex) ?usize {
2662 const first_token = exprs[0].firstToken();
2663 const first_loc = tree.tokenLocation(tree.token_locs[first_token].start, rtoken);
2664 if (first_loc.line == 0) {
2665 const maybe_comma = tree.prevToken(rtoken);
2666 if (tree.token_ids[maybe_comma] == .Comma)
2559fn rowSize(tree: ast.Tree, exprs: []const ast.Node.Index, rtoken: ast.TokenIndex) ?usize {
2560 const token_tags = tree.tokens.items(.tag);
2561
2562 const first_token = tree.firstToken(exprs[0]);
2563 if (tree.tokensOnSameLine(first_token, rtoken)) {
2564 const maybe_comma = rtoken - 1;
2565 if (token_tags[maybe_comma] == .comma)
26672566 return 1;
26682567 return null; // no newlines
26692568 }
......@@ -2671,9 +2570,8 @@ fn rowSize(tree: *ast.Tree, exprs: []*ast.Node, rtoken: ast.TokenIndex) ?usize {
26712570 var count: usize = 1;
26722571 for (exprs) |expr, i| {
26732572 if (i + 1 < exprs.len) {
2674 const expr_last_token = expr.lastToken() + 1;
2675 const loc = tree.tokenLocation(tree.token_locs[expr_last_token].start, exprs[i + 1].firstToken());
2676 if (loc.line != 0) return count;
2573 const expr_last_token = tree.lastToken(expr) + 1;
2574 if (!tree.tokensOnSameLine(expr_last_token, tree.firstToken(exprs[i + 1]))) return count;
26772575 count += 1;
26782576 } else {
26792577 return count;
......@@ -2681,3 +2579,150 @@ fn rowSize(tree: *ast.Tree, exprs: []*ast.Node, rtoken: ast.TokenIndex) ?usize {
26812579 }
26822580 unreachable;
26832581}
2582
2583/// Automatically inserts indentation of written data by keeping
2584/// track of the current indentation level
2585fn AutoIndentingStream(comptime UnderlyingWriter: type) type {
2586 return struct {
2587 const Self = @This();
2588 pub const Error = UnderlyingWriter.Error;
2589 pub const Writer = std.io.Writer(*Self, Error, write);
2590
2591 underlying_writer: UnderlyingWriter,
2592
2593 /// Offset into the source at which formatting has been disabled with
2594 /// a `zig fmt: off` comment.
2595 ///
2596 /// If non-null, the AutoIndentingStream will not write any bytes
2597 /// to the underlying writer. It will however continue to track the
2598 /// indentation level.
2599 disabled_offset: ?usize = null,
2600
2601 indent_count: usize = 0,
2602 indent_delta: usize,
2603 current_line_empty: bool = true,
2604 /// automatically popped when applied
2605 indent_one_shot_count: usize = 0,
2606 /// the most recently applied indent
2607 applied_indent: usize = 0,
2608 /// not used until the next line
2609 indent_next_line: usize = 0,
2610
2611 pub fn writer(self: *Self) Writer {
2612 return .{ .context = self };
2613 }
2614
2615 pub fn write(self: *Self, bytes: []const u8) Error!usize {
2616 if (bytes.len == 0)
2617 return @as(usize, 0);
2618
2619 try self.applyIndent();
2620 return self.writeNoIndent(bytes);
2621 }
2622
2623 // Change the indent delta without changing the final indentation level
2624 pub fn setIndentDelta(self: *Self, new_indent_delta: usize) void {
2625 if (self.indent_delta == new_indent_delta) {
2626 return;
2627 } else if (self.indent_delta > new_indent_delta) {
2628 assert(self.indent_delta % new_indent_delta == 0);
2629 self.indent_count = self.indent_count * (self.indent_delta / new_indent_delta);
2630 } else {
2631 // assert that the current indentation (in spaces) in a multiple of the new delta
2632 assert((self.indent_count * self.indent_delta) % new_indent_delta == 0);
2633 self.indent_count = self.indent_count / (new_indent_delta / self.indent_delta);
2634 }
2635 self.indent_delta = new_indent_delta;
2636 }
2637
2638 fn writeNoIndent(self: *Self, bytes: []const u8) Error!usize {
2639 if (bytes.len == 0)
2640 return @as(usize, 0);
2641
2642 if (self.disabled_offset == null) try self.underlying_writer.writeAll(bytes);
2643 if (bytes[bytes.len - 1] == '\n')
2644 self.resetLine();
2645 return bytes.len;
2646 }
2647
2648 pub fn insertNewline(self: *Self) Error!void {
2649 _ = try self.writeNoIndent("\n");
2650 }
2651
2652 fn resetLine(self: *Self) void {
2653 self.current_line_empty = true;
2654 self.indent_next_line = 0;
2655 }
2656
2657 /// Insert a newline unless the current line is blank
2658 pub fn maybeInsertNewline(self: *Self) Error!void {
2659 if (!self.current_line_empty)
2660 try self.insertNewline();
2661 }
2662
2663 /// Push default indentation
2664 /// Doesn't actually write any indentation.
2665 /// Just primes the stream to be able to write the correct indentation if it needs to.
2666 pub fn pushIndent(self: *Self) void {
2667 self.indent_count += 1;
2668 }
2669
2670 /// Push an indent that is automatically popped after being applied
2671 pub fn pushIndentOneShot(self: *Self) void {
2672 self.indent_one_shot_count += 1;
2673 self.pushIndent();
2674 }
2675
2676 /// Turns all one-shot indents into regular indents
2677 /// Returns number of indents that must now be manually popped
2678 pub fn lockOneShotIndent(self: *Self) usize {
2679 var locked_count = self.indent_one_shot_count;
2680 self.indent_one_shot_count = 0;
2681 return locked_count;
2682 }
2683
2684 /// Push an indent that should not take effect until the next line
2685 pub fn pushIndentNextLine(self: *Self) void {
2686 self.indent_next_line += 1;
2687 self.pushIndent();
2688 }
2689
2690 pub fn popIndent(self: *Self) void {
2691 assert(self.indent_count != 0);
2692 self.indent_count -= 1;
2693
2694 if (self.indent_next_line > 0)
2695 self.indent_next_line -= 1;
2696 }
2697
2698 /// Writes ' ' bytes if the current line is empty
2699 fn applyIndent(self: *Self) Error!void {
2700 const current_indent = self.currentIndent();
2701 if (self.current_line_empty and current_indent > 0) {
2702 if (self.disabled_offset == null) {
2703 try self.underlying_writer.writeByteNTimes(' ', current_indent);
2704 }
2705 self.applied_indent = current_indent;
2706 }
2707
2708 self.indent_count -= self.indent_one_shot_count;
2709 self.indent_one_shot_count = 0;
2710 self.current_line_empty = false;
2711 }
2712
2713 /// Checks to see if the most recent indentation exceeds the currently pushed indents
2714 pub fn isLineOverIndented(self: *Self) bool {
2715 if (self.current_line_empty) return false;
2716 return self.applied_indent > self.currentIndent();
2717 }
2718
2719 fn currentIndent(self: *Self) usize {
2720 var indent_current: usize = 0;
2721 if (self.indent_count > 0) {
2722 const indent_count = self.indent_count - self.indent_next_line;
2723 indent_current = indent_count * self.indent_delta;
2724 }
2725 return indent_current;
2726 }
2727 };
2728}
lib/std/zig/tokenizer.zig+832-826
......@@ -7,7 +7,7 @@ const std = @import("../std.zig");
77const mem = std.mem;
88
99pub const Token = struct {
10 id: Id,
10 tag: Tag,
1111 loc: Loc,
1212
1313 pub const Loc = struct {
......@@ -15,315 +15,315 @@ pub const Token = struct {
1515 end: usize,
1616 };
1717
18 pub const keywords = std.ComptimeStringMap(Id, .{
19 .{ "align", .Keyword_align },
20 .{ "allowzero", .Keyword_allowzero },
21 .{ "and", .Keyword_and },
22 .{ "anyframe", .Keyword_anyframe },
23 .{ "anytype", .Keyword_anytype },
24 .{ "asm", .Keyword_asm },
25 .{ "async", .Keyword_async },
26 .{ "await", .Keyword_await },
27 .{ "break", .Keyword_break },
28 .{ "callconv", .Keyword_callconv },
29 .{ "catch", .Keyword_catch },
30 .{ "comptime", .Keyword_comptime },
31 .{ "const", .Keyword_const },
32 .{ "continue", .Keyword_continue },
33 .{ "defer", .Keyword_defer },
34 .{ "else", .Keyword_else },
35 .{ "enum", .Keyword_enum },
36 .{ "errdefer", .Keyword_errdefer },
37 .{ "error", .Keyword_error },
38 .{ "export", .Keyword_export },
39 .{ "extern", .Keyword_extern },
40 .{ "false", .Keyword_false },
41 .{ "fn", .Keyword_fn },
42 .{ "for", .Keyword_for },
43 .{ "if", .Keyword_if },
44 .{ "inline", .Keyword_inline },
45 .{ "noalias", .Keyword_noalias },
46 .{ "noasync", .Keyword_nosuspend }, // TODO: remove this
47 .{ "noinline", .Keyword_noinline },
48 .{ "nosuspend", .Keyword_nosuspend },
49 .{ "null", .Keyword_null },
50 .{ "opaque", .Keyword_opaque },
51 .{ "or", .Keyword_or },
52 .{ "orelse", .Keyword_orelse },
53 .{ "packed", .Keyword_packed },
54 .{ "pub", .Keyword_pub },
55 .{ "resume", .Keyword_resume },
56 .{ "return", .Keyword_return },
57 .{ "linksection", .Keyword_linksection },
58 .{ "struct", .Keyword_struct },
59 .{ "suspend", .Keyword_suspend },
60 .{ "switch", .Keyword_switch },
61 .{ "test", .Keyword_test },
62 .{ "threadlocal", .Keyword_threadlocal },
63 .{ "true", .Keyword_true },
64 .{ "try", .Keyword_try },
65 .{ "undefined", .Keyword_undefined },
66 .{ "union", .Keyword_union },
67 .{ "unreachable", .Keyword_unreachable },
68 .{ "usingnamespace", .Keyword_usingnamespace },
69 .{ "var", .Keyword_var },
70 .{ "volatile", .Keyword_volatile },
71 .{ "while", .Keyword_while },
18 pub const keywords = std.ComptimeStringMap(Tag, .{
19 .{ "align", .keyword_align },
20 .{ "allowzero", .keyword_allowzero },
21 .{ "and", .keyword_and },
22 .{ "anyframe", .keyword_anyframe },
23 .{ "anytype", .keyword_anytype },
24 .{ "asm", .keyword_asm },
25 .{ "async", .keyword_async },
26 .{ "await", .keyword_await },
27 .{ "break", .keyword_break },
28 .{ "callconv", .keyword_callconv },
29 .{ "catch", .keyword_catch },
30 .{ "comptime", .keyword_comptime },
31 .{ "const", .keyword_const },
32 .{ "continue", .keyword_continue },
33 .{ "defer", .keyword_defer },
34 .{ "else", .keyword_else },
35 .{ "enum", .keyword_enum },
36 .{ "errdefer", .keyword_errdefer },
37 .{ "error", .keyword_error },
38 .{ "export", .keyword_export },
39 .{ "extern", .keyword_extern },
40 .{ "false", .keyword_false },
41 .{ "fn", .keyword_fn },
42 .{ "for", .keyword_for },
43 .{ "if", .keyword_if },
44 .{ "inline", .keyword_inline },
45 .{ "noalias", .keyword_noalias },
46 .{ "noinline", .keyword_noinline },
47 .{ "nosuspend", .keyword_nosuspend },
48 .{ "null", .keyword_null },
49 .{ "opaque", .keyword_opaque },
50 .{ "or", .keyword_or },
51 .{ "orelse", .keyword_orelse },
52 .{ "packed", .keyword_packed },
53 .{ "pub", .keyword_pub },
54 .{ "resume", .keyword_resume },
55 .{ "return", .keyword_return },
56 .{ "linksection", .keyword_linksection },
57 .{ "struct", .keyword_struct },
58 .{ "suspend", .keyword_suspend },
59 .{ "switch", .keyword_switch },
60 .{ "test", .keyword_test },
61 .{ "threadlocal", .keyword_threadlocal },
62 .{ "true", .keyword_true },
63 .{ "try", .keyword_try },
64 .{ "undefined", .keyword_undefined },
65 .{ "union", .keyword_union },
66 .{ "unreachable", .keyword_unreachable },
67 .{ "usingnamespace", .keyword_usingnamespace },
68 .{ "var", .keyword_var },
69 .{ "volatile", .keyword_volatile },
70 .{ "while", .keyword_while },
7271 });
7372
74 pub fn getKeyword(bytes: []const u8) ?Id {
73 pub fn getKeyword(bytes: []const u8) ?Tag {
7574 return keywords.get(bytes);
7675 }
7776
78 pub const Id = enum {
79 Invalid,
80 Invalid_ampersands,
81 Invalid_periodasterisks,
82 Identifier,
83 StringLiteral,
84 MultilineStringLiteralLine,
85 CharLiteral,
86 Eof,
87 Builtin,
88 Bang,
89 Pipe,
90 PipePipe,
91 PipeEqual,
92 Equal,
93 EqualEqual,
94 EqualAngleBracketRight,
95 BangEqual,
96 LParen,
97 RParen,
98 Semicolon,
99 Percent,
100 PercentEqual,
101 LBrace,
102 RBrace,
103 LBracket,
104 RBracket,
105 Period,
106 PeriodAsterisk,
107 Ellipsis2,
108 Ellipsis3,
109 Caret,
110 CaretEqual,
111 Plus,
112 PlusPlus,
113 PlusEqual,
114 PlusPercent,
115 PlusPercentEqual,
116 Minus,
117 MinusEqual,
118 MinusPercent,
119 MinusPercentEqual,
120 Asterisk,
121 AsteriskEqual,
122 AsteriskAsterisk,
123 AsteriskPercent,
124 AsteriskPercentEqual,
125 Arrow,
126 Colon,
127 Slash,
128 SlashEqual,
129 Comma,
130 Ampersand,
131 AmpersandEqual,
132 QuestionMark,
133 AngleBracketLeft,
134 AngleBracketLeftEqual,
135 AngleBracketAngleBracketLeft,
136 AngleBracketAngleBracketLeftEqual,
137 AngleBracketRight,
138 AngleBracketRightEqual,
139 AngleBracketAngleBracketRight,
140 AngleBracketAngleBracketRightEqual,
141 Tilde,
142 IntegerLiteral,
143 FloatLiteral,
144 LineComment,
145 DocComment,
146 ContainerDocComment,
147 ShebangLine,
148 Keyword_align,
149 Keyword_allowzero,
150 Keyword_and,
151 Keyword_anyframe,
152 Keyword_anytype,
153 Keyword_asm,
154 Keyword_async,
155 Keyword_await,
156 Keyword_break,
157 Keyword_callconv,
158 Keyword_catch,
159 Keyword_comptime,
160 Keyword_const,
161 Keyword_continue,
162 Keyword_defer,
163 Keyword_else,
164 Keyword_enum,
165 Keyword_errdefer,
166 Keyword_error,
167 Keyword_export,
168 Keyword_extern,
169 Keyword_false,
170 Keyword_fn,
171 Keyword_for,
172 Keyword_if,
173 Keyword_inline,
174 Keyword_noalias,
175 Keyword_noinline,
176 Keyword_nosuspend,
177 Keyword_null,
178 Keyword_opaque,
179 Keyword_or,
180 Keyword_orelse,
181 Keyword_packed,
182 Keyword_pub,
183 Keyword_resume,
184 Keyword_return,
185 Keyword_linksection,
186 Keyword_struct,
187 Keyword_suspend,
188 Keyword_switch,
189 Keyword_test,
190 Keyword_threadlocal,
191 Keyword_true,
192 Keyword_try,
193 Keyword_undefined,
194 Keyword_union,
195 Keyword_unreachable,
196 Keyword_usingnamespace,
197 Keyword_var,
198 Keyword_volatile,
199 Keyword_while,
200
201 pub fn symbol(id: Id) []const u8 {
202 return switch (id) {
203 .Invalid => "Invalid",
204 .Invalid_ampersands => "&&",
205 .Invalid_periodasterisks => ".**",
206 .Identifier => "Identifier",
207 .StringLiteral => "StringLiteral",
208 .MultilineStringLiteralLine => "MultilineStringLiteralLine",
209 .CharLiteral => "CharLiteral",
210 .Eof => "Eof",
211 .Builtin => "Builtin",
212 .IntegerLiteral => "IntegerLiteral",
213 .FloatLiteral => "FloatLiteral",
214 .LineComment => "LineComment",
215 .DocComment => "DocComment",
216 .ContainerDocComment => "ContainerDocComment",
217 .ShebangLine => "ShebangLine",
218
219 .Bang => "!",
220 .Pipe => "|",
221 .PipePipe => "||",
222 .PipeEqual => "|=",
223 .Equal => "=",
224 .EqualEqual => "==",
225 .EqualAngleBracketRight => "=>",
226 .BangEqual => "!=",
227 .LParen => "(",
228 .RParen => ")",
229 .Semicolon => ";",
230 .Percent => "%",
231 .PercentEqual => "%=",
232 .LBrace => "{",
233 .RBrace => "}",
234 .LBracket => "[",
235 .RBracket => "]",
236 .Period => ".",
237 .PeriodAsterisk => ".*",
238 .Ellipsis2 => "..",
239 .Ellipsis3 => "...",
240 .Caret => "^",
241 .CaretEqual => "^=",
242 .Plus => "+",
243 .PlusPlus => "++",
244 .PlusEqual => "+=",
245 .PlusPercent => "+%",
246 .PlusPercentEqual => "+%=",
247 .Minus => "-",
248 .MinusEqual => "-=",
249 .MinusPercent => "-%",
250 .MinusPercentEqual => "-%=",
251 .Asterisk => "*",
252 .AsteriskEqual => "*=",
253 .AsteriskAsterisk => "**",
254 .AsteriskPercent => "*%",
255 .AsteriskPercentEqual => "*%=",
256 .Arrow => "->",
257 .Colon => ":",
258 .Slash => "/",
259 .SlashEqual => "/=",
260 .Comma => ",",
261 .Ampersand => "&",
262 .AmpersandEqual => "&=",
263 .QuestionMark => "?",
264 .AngleBracketLeft => "<",
265 .AngleBracketLeftEqual => "<=",
266 .AngleBracketAngleBracketLeft => "<<",
267 .AngleBracketAngleBracketLeftEqual => "<<=",
268 .AngleBracketRight => ">",
269 .AngleBracketRightEqual => ">=",
270 .AngleBracketAngleBracketRight => ">>",
271 .AngleBracketAngleBracketRightEqual => ">>=",
272 .Tilde => "~",
273 .Keyword_align => "align",
274 .Keyword_allowzero => "allowzero",
275 .Keyword_and => "and",
276 .Keyword_anyframe => "anyframe",
277 .Keyword_anytype => "anytype",
278 .Keyword_asm => "asm",
279 .Keyword_async => "async",
280 .Keyword_await => "await",
281 .Keyword_break => "break",
282 .Keyword_callconv => "callconv",
283 .Keyword_catch => "catch",
284 .Keyword_comptime => "comptime",
285 .Keyword_const => "const",
286 .Keyword_continue => "continue",
287 .Keyword_defer => "defer",
288 .Keyword_else => "else",
289 .Keyword_enum => "enum",
290 .Keyword_errdefer => "errdefer",
291 .Keyword_error => "error",
292 .Keyword_export => "export",
293 .Keyword_extern => "extern",
294 .Keyword_false => "false",
295 .Keyword_fn => "fn",
296 .Keyword_for => "for",
297 .Keyword_if => "if",
298 .Keyword_inline => "inline",
299 .Keyword_noalias => "noalias",
300 .Keyword_noinline => "noinline",
301 .Keyword_nosuspend => "nosuspend",
302 .Keyword_null => "null",
303 .Keyword_opaque => "opaque",
304 .Keyword_or => "or",
305 .Keyword_orelse => "orelse",
306 .Keyword_packed => "packed",
307 .Keyword_pub => "pub",
308 .Keyword_resume => "resume",
309 .Keyword_return => "return",
310 .Keyword_linksection => "linksection",
311 .Keyword_struct => "struct",
312 .Keyword_suspend => "suspend",
313 .Keyword_switch => "switch",
314 .Keyword_test => "test",
315 .Keyword_threadlocal => "threadlocal",
316 .Keyword_true => "true",
317 .Keyword_try => "try",
318 .Keyword_undefined => "undefined",
319 .Keyword_union => "union",
320 .Keyword_unreachable => "unreachable",
321 .Keyword_usingnamespace => "usingnamespace",
322 .Keyword_var => "var",
323 .Keyword_volatile => "volatile",
324 .Keyword_while => "while",
77 pub const Tag = enum {
78 invalid,
79 invalid_ampersands,
80 invalid_periodasterisks,
81 identifier,
82 string_literal,
83 multiline_string_literal_line,
84 char_literal,
85 eof,
86 builtin,
87 bang,
88 pipe,
89 pipe_pipe,
90 pipe_equal,
91 equal,
92 equal_equal,
93 equal_angle_bracket_right,
94 bang_equal,
95 l_paren,
96 r_paren,
97 semicolon,
98 percent,
99 percent_equal,
100 l_brace,
101 r_brace,
102 l_bracket,
103 r_bracket,
104 period,
105 period_asterisk,
106 ellipsis2,
107 ellipsis3,
108 caret,
109 caret_equal,
110 plus,
111 plus_plus,
112 plus_equal,
113 plus_percent,
114 plus_percent_equal,
115 minus,
116 minus_equal,
117 minus_percent,
118 minus_percent_equal,
119 asterisk,
120 asterisk_equal,
121 asterisk_asterisk,
122 asterisk_percent,
123 asterisk_percent_equal,
124 arrow,
125 colon,
126 slash,
127 slash_equal,
128 comma,
129 ampersand,
130 ampersand_equal,
131 question_mark,
132 angle_bracket_left,
133 angle_bracket_left_equal,
134 angle_bracket_angle_bracket_left,
135 angle_bracket_angle_bracket_left_equal,
136 angle_bracket_right,
137 angle_bracket_right_equal,
138 angle_bracket_angle_bracket_right,
139 angle_bracket_angle_bracket_right_equal,
140 tilde,
141 integer_literal,
142 float_literal,
143 doc_comment,
144 container_doc_comment,
145 keyword_align,
146 keyword_allowzero,
147 keyword_and,
148 keyword_anyframe,
149 keyword_anytype,
150 keyword_asm,
151 keyword_async,
152 keyword_await,
153 keyword_break,
154 keyword_callconv,
155 keyword_catch,
156 keyword_comptime,
157 keyword_const,
158 keyword_continue,
159 keyword_defer,
160 keyword_else,
161 keyword_enum,
162 keyword_errdefer,
163 keyword_error,
164 keyword_export,
165 keyword_extern,
166 keyword_false,
167 keyword_fn,
168 keyword_for,
169 keyword_if,
170 keyword_inline,
171 keyword_noalias,
172 keyword_noinline,
173 keyword_nosuspend,
174 keyword_null,
175 keyword_opaque,
176 keyword_or,
177 keyword_orelse,
178 keyword_packed,
179 keyword_pub,
180 keyword_resume,
181 keyword_return,
182 keyword_linksection,
183 keyword_struct,
184 keyword_suspend,
185 keyword_switch,
186 keyword_test,
187 keyword_threadlocal,
188 keyword_true,
189 keyword_try,
190 keyword_undefined,
191 keyword_union,
192 keyword_unreachable,
193 keyword_usingnamespace,
194 keyword_var,
195 keyword_volatile,
196 keyword_while,
197
198 pub fn lexeme(tag: Tag) ?[]const u8 {
199 return switch (tag) {
200 .invalid,
201 .identifier,
202 .string_literal,
203 .multiline_string_literal_line,
204 .char_literal,
205 .eof,
206 .builtin,
207 .integer_literal,
208 .float_literal,
209 .doc_comment,
210 .container_doc_comment,
211 => null,
212
213 .invalid_ampersands => "&&",
214 .invalid_periodasterisks => ".**",
215 .bang => "!",
216 .pipe => "|",
217 .pipe_pipe => "||",
218 .pipe_equal => "|=",
219 .equal => "=",
220 .equal_equal => "==",
221 .equal_angle_bracket_right => "=>",
222 .bang_equal => "!=",
223 .l_paren => "(",
224 .r_paren => ")",
225 .semicolon => ";",
226 .percent => "%",
227 .percent_equal => "%=",
228 .l_brace => "{",
229 .r_brace => "}",
230 .l_bracket => "[",
231 .r_bracket => "]",
232 .period => ".",
233 .period_asterisk => ".*",
234 .ellipsis2 => "..",
235 .ellipsis3 => "...",
236 .caret => "^",
237 .caret_equal => "^=",
238 .plus => "+",
239 .plus_plus => "++",
240 .plus_equal => "+=",
241 .plus_percent => "+%",
242 .plus_percent_equal => "+%=",
243 .minus => "-",
244 .minus_equal => "-=",
245 .minus_percent => "-%",
246 .minus_percent_equal => "-%=",
247 .asterisk => "*",
248 .asterisk_equal => "*=",
249 .asterisk_asterisk => "**",
250 .asterisk_percent => "*%",
251 .asterisk_percent_equal => "*%=",
252 .arrow => "->",
253 .colon => ":",
254 .slash => "/",
255 .slash_equal => "/=",
256 .comma => ",",
257 .ampersand => "&",
258 .ampersand_equal => "&=",
259 .question_mark => "?",
260 .angle_bracket_left => "<",
261 .angle_bracket_left_equal => "<=",
262 .angle_bracket_angle_bracket_left => "<<",
263 .angle_bracket_angle_bracket_left_equal => "<<=",
264 .angle_bracket_right => ">",
265 .angle_bracket_right_equal => ">=",
266 .angle_bracket_angle_bracket_right => ">>",
267 .angle_bracket_angle_bracket_right_equal => ">>=",
268 .tilde => "~",
269 .keyword_align => "align",
270 .keyword_allowzero => "allowzero",
271 .keyword_and => "and",
272 .keyword_anyframe => "anyframe",
273 .keyword_anytype => "anytype",
274 .keyword_asm => "asm",
275 .keyword_async => "async",
276 .keyword_await => "await",
277 .keyword_break => "break",
278 .keyword_callconv => "callconv",
279 .keyword_catch => "catch",
280 .keyword_comptime => "comptime",
281 .keyword_const => "const",
282 .keyword_continue => "continue",
283 .keyword_defer => "defer",
284 .keyword_else => "else",
285 .keyword_enum => "enum",
286 .keyword_errdefer => "errdefer",
287 .keyword_error => "error",
288 .keyword_export => "export",
289 .keyword_extern => "extern",
290 .keyword_false => "false",
291 .keyword_fn => "fn",
292 .keyword_for => "for",
293 .keyword_if => "if",
294 .keyword_inline => "inline",
295 .keyword_noalias => "noalias",
296 .keyword_noinline => "noinline",
297 .keyword_nosuspend => "nosuspend",
298 .keyword_null => "null",
299 .keyword_opaque => "opaque",
300 .keyword_or => "or",
301 .keyword_orelse => "orelse",
302 .keyword_packed => "packed",
303 .keyword_pub => "pub",
304 .keyword_resume => "resume",
305 .keyword_return => "return",
306 .keyword_linksection => "linksection",
307 .keyword_struct => "struct",
308 .keyword_suspend => "suspend",
309 .keyword_switch => "switch",
310 .keyword_test => "test",
311 .keyword_threadlocal => "threadlocal",
312 .keyword_true => "true",
313 .keyword_try => "try",
314 .keyword_undefined => "undefined",
315 .keyword_union => "union",
316 .keyword_unreachable => "unreachable",
317 .keyword_usingnamespace => "usingnamespace",
318 .keyword_var => "var",
319 .keyword_volatile => "volatile",
320 .keyword_while => "while",
325321 };
326322 }
323
324 pub fn symbol(tag: Tag) []const u8 {
325 return tag.lexeme() orelse @tagName(tag);
326 }
327327 };
328328};
329329
......@@ -334,7 +334,7 @@ pub const Tokenizer = struct {
334334
335335 /// For debugging purposes
336336 pub fn dump(self: *Tokenizer, token: *const Token) void {
337 std.debug.warn("{s} \"{s}\"\n", .{ @tagName(token.id), self.buffer[token.start..token.end] });
337 std.debug.warn("{s} \"{s}\"\n", .{ @tagName(token.tag), self.buffer[token.start..token.end] });
338338 }
339339
340340 pub fn init(buffer: []const u8) Tokenizer {
......@@ -421,7 +421,7 @@ pub const Tokenizer = struct {
421421 const start_index = self.index;
422422 var state: State = .start;
423423 var result = Token{
424 .id = .Eof,
424 .tag = .eof,
425425 .loc = .{
426426 .start = self.index,
427427 .end = undefined,
......@@ -438,14 +438,14 @@ pub const Tokenizer = struct {
438438 },
439439 '"' => {
440440 state = .string_literal;
441 result.id = .StringLiteral;
441 result.tag = .string_literal;
442442 },
443443 '\'' => {
444444 state = .char_literal;
445445 },
446446 'a'...'z', 'A'...'Z', '_' => {
447447 state = .identifier;
448 result.id = .Identifier;
448 result.tag = .identifier;
449449 },
450450 '@' => {
451451 state = .saw_at_sign;
......@@ -460,42 +460,42 @@ pub const Tokenizer = struct {
460460 state = .pipe;
461461 },
462462 '(' => {
463 result.id = .LParen;
463 result.tag = .l_paren;
464464 self.index += 1;
465465 break;
466466 },
467467 ')' => {
468 result.id = .RParen;
468 result.tag = .r_paren;
469469 self.index += 1;
470470 break;
471471 },
472472 '[' => {
473 result.id = .LBracket;
473 result.tag = .l_bracket;
474474 self.index += 1;
475475 break;
476476 },
477477 ']' => {
478 result.id = .RBracket;
478 result.tag = .r_bracket;
479479 self.index += 1;
480480 break;
481481 },
482482 ';' => {
483 result.id = .Semicolon;
483 result.tag = .semicolon;
484484 self.index += 1;
485485 break;
486486 },
487487 ',' => {
488 result.id = .Comma;
488 result.tag = .comma;
489489 self.index += 1;
490490 break;
491491 },
492492 '?' => {
493 result.id = .QuestionMark;
493 result.tag = .question_mark;
494494 self.index += 1;
495495 break;
496496 },
497497 ':' => {
498 result.id = .Colon;
498 result.tag = .colon;
499499 self.index += 1;
500500 break;
501501 },
......@@ -519,20 +519,20 @@ pub const Tokenizer = struct {
519519 },
520520 '\\' => {
521521 state = .backslash;
522 result.id = .MultilineStringLiteralLine;
522 result.tag = .multiline_string_literal_line;
523523 },
524524 '{' => {
525 result.id = .LBrace;
525 result.tag = .l_brace;
526526 self.index += 1;
527527 break;
528528 },
529529 '}' => {
530 result.id = .RBrace;
530 result.tag = .r_brace;
531531 self.index += 1;
532532 break;
533533 },
534534 '~' => {
535 result.id = .Tilde;
535 result.tag = .tilde;
536536 self.index += 1;
537537 break;
538538 },
......@@ -550,14 +550,14 @@ pub const Tokenizer = struct {
550550 },
551551 '0' => {
552552 state = .zero;
553 result.id = .IntegerLiteral;
553 result.tag = .integer_literal;
554554 },
555555 '1'...'9' => {
556556 state = .int_literal_dec;
557 result.id = .IntegerLiteral;
557 result.tag = .integer_literal;
558558 },
559559 else => {
560 result.id = .Invalid;
560 result.tag = .invalid;
561561 self.index += 1;
562562 break;
563563 },
......@@ -565,42 +565,42 @@ pub const Tokenizer = struct {
565565
566566 .saw_at_sign => switch (c) {
567567 '"' => {
568 result.id = .Identifier;
568 result.tag = .identifier;
569569 state = .string_literal;
570570 },
571571 else => {
572572 // reinterpret as a builtin
573573 self.index -= 1;
574574 state = .builtin;
575 result.id = .Builtin;
575 result.tag = .builtin;
576576 },
577577 },
578578
579579 .ampersand => switch (c) {
580580 '&' => {
581 result.id = .Invalid_ampersands;
581 result.tag = .invalid_ampersands;
582582 self.index += 1;
583583 break;
584584 },
585585 '=' => {
586 result.id = .AmpersandEqual;
586 result.tag = .ampersand_equal;
587587 self.index += 1;
588588 break;
589589 },
590590 else => {
591 result.id = .Ampersand;
591 result.tag = .ampersand;
592592 break;
593593 },
594594 },
595595
596596 .asterisk => switch (c) {
597597 '=' => {
598 result.id = .AsteriskEqual;
598 result.tag = .asterisk_equal;
599599 self.index += 1;
600600 break;
601601 },
602602 '*' => {
603 result.id = .AsteriskAsterisk;
603 result.tag = .asterisk_asterisk;
604604 self.index += 1;
605605 break;
606606 },
......@@ -608,43 +608,43 @@ pub const Tokenizer = struct {
608608 state = .asterisk_percent;
609609 },
610610 else => {
611 result.id = .Asterisk;
611 result.tag = .asterisk;
612612 break;
613613 },
614614 },
615615
616616 .asterisk_percent => switch (c) {
617617 '=' => {
618 result.id = .AsteriskPercentEqual;
618 result.tag = .asterisk_percent_equal;
619619 self.index += 1;
620620 break;
621621 },
622622 else => {
623 result.id = .AsteriskPercent;
623 result.tag = .asterisk_percent;
624624 break;
625625 },
626626 },
627627
628628 .percent => switch (c) {
629629 '=' => {
630 result.id = .PercentEqual;
630 result.tag = .percent_equal;
631631 self.index += 1;
632632 break;
633633 },
634634 else => {
635 result.id = .Percent;
635 result.tag = .percent;
636636 break;
637637 },
638638 },
639639
640640 .plus => switch (c) {
641641 '=' => {
642 result.id = .PlusEqual;
642 result.tag = .plus_equal;
643643 self.index += 1;
644644 break;
645645 },
646646 '+' => {
647 result.id = .PlusPlus;
647 result.tag = .plus_plus;
648648 self.index += 1;
649649 break;
650650 },
......@@ -652,31 +652,31 @@ pub const Tokenizer = struct {
652652 state = .plus_percent;
653653 },
654654 else => {
655 result.id = .Plus;
655 result.tag = .plus;
656656 break;
657657 },
658658 },
659659
660660 .plus_percent => switch (c) {
661661 '=' => {
662 result.id = .PlusPercentEqual;
662 result.tag = .plus_percent_equal;
663663 self.index += 1;
664664 break;
665665 },
666666 else => {
667 result.id = .PlusPercent;
667 result.tag = .plus_percent;
668668 break;
669669 },
670670 },
671671
672672 .caret => switch (c) {
673673 '=' => {
674 result.id = .CaretEqual;
674 result.tag = .caret_equal;
675675 self.index += 1;
676676 break;
677677 },
678678 else => {
679 result.id = .Caret;
679 result.tag = .caret;
680680 break;
681681 },
682682 },
......@@ -684,8 +684,8 @@ pub const Tokenizer = struct {
684684 .identifier => switch (c) {
685685 'a'...'z', 'A'...'Z', '_', '0'...'9' => {},
686686 else => {
687 if (Token.getKeyword(self.buffer[result.loc.start..self.index])) |id| {
688 result.id = id;
687 if (Token.getKeyword(self.buffer[result.loc.start..self.index])) |tag| {
688 result.tag = tag;
689689 }
690690 break;
691691 },
......@@ -724,7 +724,7 @@ pub const Tokenizer = struct {
724724 state = .char_literal_backslash;
725725 },
726726 '\'', 0x80...0xbf, 0xf8...0xff => {
727 result.id = .Invalid;
727 result.tag = .invalid;
728728 break;
729729 },
730730 0xc0...0xdf => { // 110xxxxx
......@@ -746,7 +746,7 @@ pub const Tokenizer = struct {
746746
747747 .char_literal_backslash => switch (c) {
748748 '\n' => {
749 result.id = .Invalid;
749 result.tag = .invalid;
750750 break;
751751 },
752752 'x' => {
......@@ -769,7 +769,7 @@ pub const Tokenizer = struct {
769769 }
770770 },
771771 else => {
772 result.id = .Invalid;
772 result.tag = .invalid;
773773 break;
774774 },
775775 },
......@@ -780,7 +780,7 @@ pub const Tokenizer = struct {
780780 seen_escape_digits = 0;
781781 },
782782 else => {
783 result.id = .Invalid;
783 result.tag = .invalid;
784784 state = .char_literal_unicode_invalid;
785785 },
786786 },
......@@ -791,14 +791,14 @@ pub const Tokenizer = struct {
791791 },
792792 '}' => {
793793 if (seen_escape_digits == 0) {
794 result.id = .Invalid;
794 result.tag = .invalid;
795795 state = .char_literal_unicode_invalid;
796796 } else {
797797 state = .char_literal_end;
798798 }
799799 },
800800 else => {
801 result.id = .Invalid;
801 result.tag = .invalid;
802802 state = .char_literal_unicode_invalid;
803803 },
804804 },
......@@ -813,12 +813,12 @@ pub const Tokenizer = struct {
813813
814814 .char_literal_end => switch (c) {
815815 '\'' => {
816 result.id = .CharLiteral;
816 result.tag = .char_literal;
817817 self.index += 1;
818818 break;
819819 },
820820 else => {
821 result.id = .Invalid;
821 result.tag = .invalid;
822822 break;
823823 },
824824 },
......@@ -831,7 +831,7 @@ pub const Tokenizer = struct {
831831 }
832832 },
833833 else => {
834 result.id = .Invalid;
834 result.tag = .invalid;
835835 break;
836836 },
837837 },
......@@ -847,58 +847,58 @@ pub const Tokenizer = struct {
847847
848848 .bang => switch (c) {
849849 '=' => {
850 result.id = .BangEqual;
850 result.tag = .bang_equal;
851851 self.index += 1;
852852 break;
853853 },
854854 else => {
855 result.id = .Bang;
855 result.tag = .bang;
856856 break;
857857 },
858858 },
859859
860860 .pipe => switch (c) {
861861 '=' => {
862 result.id = .PipeEqual;
862 result.tag = .pipe_equal;
863863 self.index += 1;
864864 break;
865865 },
866866 '|' => {
867 result.id = .PipePipe;
867 result.tag = .pipe_pipe;
868868 self.index += 1;
869869 break;
870870 },
871871 else => {
872 result.id = .Pipe;
872 result.tag = .pipe;
873873 break;
874874 },
875875 },
876876
877877 .equal => switch (c) {
878878 '=' => {
879 result.id = .EqualEqual;
879 result.tag = .equal_equal;
880880 self.index += 1;
881881 break;
882882 },
883883 '>' => {
884 result.id = .EqualAngleBracketRight;
884 result.tag = .equal_angle_bracket_right;
885885 self.index += 1;
886886 break;
887887 },
888888 else => {
889 result.id = .Equal;
889 result.tag = .equal;
890890 break;
891891 },
892892 },
893893
894894 .minus => switch (c) {
895895 '>' => {
896 result.id = .Arrow;
896 result.tag = .arrow;
897897 self.index += 1;
898898 break;
899899 },
900900 '=' => {
901 result.id = .MinusEqual;
901 result.tag = .minus_equal;
902902 self.index += 1;
903903 break;
904904 },
......@@ -906,19 +906,19 @@ pub const Tokenizer = struct {
906906 state = .minus_percent;
907907 },
908908 else => {
909 result.id = .Minus;
909 result.tag = .minus;
910910 break;
911911 },
912912 },
913913
914914 .minus_percent => switch (c) {
915915 '=' => {
916 result.id = .MinusPercentEqual;
916 result.tag = .minus_percent_equal;
917917 self.index += 1;
918918 break;
919919 },
920920 else => {
921 result.id = .MinusPercent;
921 result.tag = .minus_percent;
922922 break;
923923 },
924924 },
......@@ -928,24 +928,24 @@ pub const Tokenizer = struct {
928928 state = .angle_bracket_angle_bracket_left;
929929 },
930930 '=' => {
931 result.id = .AngleBracketLeftEqual;
931 result.tag = .angle_bracket_left_equal;
932932 self.index += 1;
933933 break;
934934 },
935935 else => {
936 result.id = .AngleBracketLeft;
936 result.tag = .angle_bracket_left;
937937 break;
938938 },
939939 },
940940
941941 .angle_bracket_angle_bracket_left => switch (c) {
942942 '=' => {
943 result.id = .AngleBracketAngleBracketLeftEqual;
943 result.tag = .angle_bracket_angle_bracket_left_equal;
944944 self.index += 1;
945945 break;
946946 },
947947 else => {
948 result.id = .AngleBracketAngleBracketLeft;
948 result.tag = .angle_bracket_angle_bracket_left;
949949 break;
950950 },
951951 },
......@@ -955,24 +955,24 @@ pub const Tokenizer = struct {
955955 state = .angle_bracket_angle_bracket_right;
956956 },
957957 '=' => {
958 result.id = .AngleBracketRightEqual;
958 result.tag = .angle_bracket_right_equal;
959959 self.index += 1;
960960 break;
961961 },
962962 else => {
963 result.id = .AngleBracketRight;
963 result.tag = .angle_bracket_right;
964964 break;
965965 },
966966 },
967967
968968 .angle_bracket_angle_bracket_right => switch (c) {
969969 '=' => {
970 result.id = .AngleBracketAngleBracketRightEqual;
970 result.tag = .angle_bracket_angle_bracket_right_equal;
971971 self.index += 1;
972972 break;
973973 },
974974 else => {
975 result.id = .AngleBracketAngleBracketRight;
975 result.tag = .angle_bracket_angle_bracket_right;
976976 break;
977977 },
978978 },
......@@ -985,30 +985,30 @@ pub const Tokenizer = struct {
985985 state = .period_asterisk;
986986 },
987987 else => {
988 result.id = .Period;
988 result.tag = .period;
989989 break;
990990 },
991991 },
992992
993993 .period_2 => switch (c) {
994994 '.' => {
995 result.id = .Ellipsis3;
995 result.tag = .ellipsis3;
996996 self.index += 1;
997997 break;
998998 },
999999 else => {
1000 result.id = .Ellipsis2;
1000 result.tag = .ellipsis2;
10011001 break;
10021002 },
10031003 },
10041004
10051005 .period_asterisk => switch (c) {
10061006 '*' => {
1007 result.id = .Invalid_periodasterisks;
1007 result.tag = .invalid_periodasterisks;
10081008 break;
10091009 },
10101010 else => {
1011 result.id = .PeriodAsterisk;
1011 result.tag = .period_asterisk;
10121012 break;
10131013 },
10141014 },
......@@ -1016,15 +1016,14 @@ pub const Tokenizer = struct {
10161016 .slash => switch (c) {
10171017 '/' => {
10181018 state = .line_comment_start;
1019 result.id = .LineComment;
10201019 },
10211020 '=' => {
1022 result.id = .SlashEqual;
1021 result.tag = .slash_equal;
10231022 self.index += 1;
10241023 break;
10251024 },
10261025 else => {
1027 result.id = .Slash;
1026 result.tag = .slash;
10281027 break;
10291028 },
10301029 },
......@@ -1033,10 +1032,13 @@ pub const Tokenizer = struct {
10331032 state = .doc_comment_start;
10341033 },
10351034 '!' => {
1036 result.id = .ContainerDocComment;
1035 result.tag = .container_doc_comment;
10371036 state = .container_doc_comment;
10381037 },
1039 '\n' => break,
1038 '\n' => {
1039 state = .start;
1040 result.loc.start = self.index + 1;
1041 },
10401042 '\t', '\r' => state = .line_comment,
10411043 else => {
10421044 state = .line_comment;
......@@ -1048,20 +1050,28 @@ pub const Tokenizer = struct {
10481050 state = .line_comment;
10491051 },
10501052 '\n' => {
1051 result.id = .DocComment;
1053 result.tag = .doc_comment;
10521054 break;
10531055 },
10541056 '\t', '\r' => {
10551057 state = .doc_comment;
1056 result.id = .DocComment;
1058 result.tag = .doc_comment;
10571059 },
10581060 else => {
10591061 state = .doc_comment;
1060 result.id = .DocComment;
1062 result.tag = .doc_comment;
10611063 self.checkLiteralCharacter();
10621064 },
10631065 },
1064 .line_comment, .doc_comment, .container_doc_comment => switch (c) {
1066 .line_comment => switch (c) {
1067 '\n' => {
1068 state = .start;
1069 result.loc.start = self.index + 1;
1070 },
1071 '\t', '\r' => {},
1072 else => self.checkLiteralCharacter(),
1073 },
1074 .doc_comment, .container_doc_comment => switch (c) {
10651075 '\n' => break,
10661076 '\t', '\r' => {},
10671077 else => self.checkLiteralCharacter(),
......@@ -1083,7 +1093,7 @@ pub const Tokenizer = struct {
10831093 },
10841094 else => {
10851095 if (isIdentifierChar(c)) {
1086 result.id = .Invalid;
1096 result.tag = .invalid;
10871097 }
10881098 break;
10891099 },
......@@ -1093,7 +1103,7 @@ pub const Tokenizer = struct {
10931103 state = .int_literal_bin;
10941104 },
10951105 else => {
1096 result.id = .Invalid;
1106 result.tag = .invalid;
10971107 break;
10981108 },
10991109 },
......@@ -1104,7 +1114,7 @@ pub const Tokenizer = struct {
11041114 '0'...'1' => {},
11051115 else => {
11061116 if (isIdentifierChar(c)) {
1107 result.id = .Invalid;
1117 result.tag = .invalid;
11081118 }
11091119 break;
11101120 },
......@@ -1114,7 +1124,7 @@ pub const Tokenizer = struct {
11141124 state = .int_literal_oct;
11151125 },
11161126 else => {
1117 result.id = .Invalid;
1127 result.tag = .invalid;
11181128 break;
11191129 },
11201130 },
......@@ -1125,7 +1135,7 @@ pub const Tokenizer = struct {
11251135 '0'...'7' => {},
11261136 else => {
11271137 if (isIdentifierChar(c)) {
1128 result.id = .Invalid;
1138 result.tag = .invalid;
11291139 }
11301140 break;
11311141 },
......@@ -1135,7 +1145,7 @@ pub const Tokenizer = struct {
11351145 state = .int_literal_dec;
11361146 },
11371147 else => {
1138 result.id = .Invalid;
1148 result.tag = .invalid;
11391149 break;
11401150 },
11411151 },
......@@ -1145,16 +1155,16 @@ pub const Tokenizer = struct {
11451155 },
11461156 '.' => {
11471157 state = .num_dot_dec;
1148 result.id = .FloatLiteral;
1158 result.tag = .float_literal;
11491159 },
11501160 'e', 'E' => {
11511161 state = .float_exponent_unsigned;
1152 result.id = .FloatLiteral;
1162 result.tag = .float_literal;
11531163 },
11541164 '0'...'9' => {},
11551165 else => {
11561166 if (isIdentifierChar(c)) {
1157 result.id = .Invalid;
1167 result.tag = .invalid;
11581168 }
11591169 break;
11601170 },
......@@ -1164,7 +1174,7 @@ pub const Tokenizer = struct {
11641174 state = .int_literal_hex;
11651175 },
11661176 else => {
1167 result.id = .Invalid;
1177 result.tag = .invalid;
11681178 break;
11691179 },
11701180 },
......@@ -1174,23 +1184,23 @@ pub const Tokenizer = struct {
11741184 },
11751185 '.' => {
11761186 state = .num_dot_hex;
1177 result.id = .FloatLiteral;
1187 result.tag = .float_literal;
11781188 },
11791189 'p', 'P' => {
11801190 state = .float_exponent_unsigned;
1181 result.id = .FloatLiteral;
1191 result.tag = .float_literal;
11821192 },
11831193 '0'...'9', 'a'...'f', 'A'...'F' => {},
11841194 else => {
11851195 if (isIdentifierChar(c)) {
1186 result.id = .Invalid;
1196 result.tag = .invalid;
11871197 }
11881198 break;
11891199 },
11901200 },
11911201 .num_dot_dec => switch (c) {
11921202 '.' => {
1193 result.id = .IntegerLiteral;
1203 result.tag = .integer_literal;
11941204 self.index -= 1;
11951205 state = .start;
11961206 break;
......@@ -1203,14 +1213,14 @@ pub const Tokenizer = struct {
12031213 },
12041214 else => {
12051215 if (isIdentifierChar(c)) {
1206 result.id = .Invalid;
1216 result.tag = .invalid;
12071217 }
12081218 break;
12091219 },
12101220 },
12111221 .num_dot_hex => switch (c) {
12121222 '.' => {
1213 result.id = .IntegerLiteral;
1223 result.tag = .integer_literal;
12141224 self.index -= 1;
12151225 state = .start;
12161226 break;
......@@ -1219,12 +1229,12 @@ pub const Tokenizer = struct {
12191229 state = .float_exponent_unsigned;
12201230 },
12211231 '0'...'9', 'a'...'f', 'A'...'F' => {
1222 result.id = .FloatLiteral;
1232 result.tag = .float_literal;
12231233 state = .float_fraction_hex;
12241234 },
12251235 else => {
12261236 if (isIdentifierChar(c)) {
1227 result.id = .Invalid;
1237 result.tag = .invalid;
12281238 }
12291239 break;
12301240 },
......@@ -1234,7 +1244,7 @@ pub const Tokenizer = struct {
12341244 state = .float_fraction_dec;
12351245 },
12361246 else => {
1237 result.id = .Invalid;
1247 result.tag = .invalid;
12381248 break;
12391249 },
12401250 },
......@@ -1248,7 +1258,7 @@ pub const Tokenizer = struct {
12481258 '0'...'9' => {},
12491259 else => {
12501260 if (isIdentifierChar(c)) {
1251 result.id = .Invalid;
1261 result.tag = .invalid;
12521262 }
12531263 break;
12541264 },
......@@ -1258,7 +1268,7 @@ pub const Tokenizer = struct {
12581268 state = .float_fraction_hex;
12591269 },
12601270 else => {
1261 result.id = .Invalid;
1271 result.tag = .invalid;
12621272 break;
12631273 },
12641274 },
......@@ -1272,7 +1282,7 @@ pub const Tokenizer = struct {
12721282 '0'...'9', 'a'...'f', 'A'...'F' => {},
12731283 else => {
12741284 if (isIdentifierChar(c)) {
1275 result.id = .Invalid;
1285 result.tag = .invalid;
12761286 }
12771287 break;
12781288 },
......@@ -1292,7 +1302,7 @@ pub const Tokenizer = struct {
12921302 state = .float_exponent_num;
12931303 },
12941304 else => {
1295 result.id = .Invalid;
1305 result.tag = .invalid;
12961306 break;
12971307 },
12981308 },
......@@ -1303,7 +1313,7 @@ pub const Tokenizer = struct {
13031313 '0'...'9' => {},
13041314 else => {
13051315 if (isIdentifierChar(c)) {
1306 result.id = .Invalid;
1316 result.tag = .invalid;
13071317 }
13081318 break;
13091319 },
......@@ -1324,21 +1334,20 @@ pub const Tokenizer = struct {
13241334 .string_literal, // find this error later
13251335 .multiline_string_literal_line,
13261336 .builtin,
1337 .line_comment,
1338 .line_comment_start,
13271339 => {},
13281340
13291341 .identifier => {
1330 if (Token.getKeyword(self.buffer[result.loc.start..self.index])) |id| {
1331 result.id = id;
1342 if (Token.getKeyword(self.buffer[result.loc.start..self.index])) |tag| {
1343 result.tag = tag;
13321344 }
13331345 },
1334 .line_comment, .line_comment_start => {
1335 result.id = .LineComment;
1336 },
13371346 .doc_comment, .doc_comment_start => {
1338 result.id = .DocComment;
1347 result.tag = .doc_comment;
13391348 },
13401349 .container_doc_comment => {
1341 result.id = .ContainerDocComment;
1350 result.tag = .container_doc_comment;
13421351 },
13431352
13441353 .int_literal_dec_no_underscore,
......@@ -1361,80 +1370,81 @@ pub const Tokenizer = struct {
13611370 .char_literal_unicode,
13621371 .string_literal_backslash,
13631372 => {
1364 result.id = .Invalid;
1373 result.tag = .invalid;
13651374 },
13661375
13671376 .equal => {
1368 result.id = .Equal;
1377 result.tag = .equal;
13691378 },
13701379 .bang => {
1371 result.id = .Bang;
1380 result.tag = .bang;
13721381 },
13731382 .minus => {
1374 result.id = .Minus;
1383 result.tag = .minus;
13751384 },
13761385 .slash => {
1377 result.id = .Slash;
1386 result.tag = .slash;
13781387 },
13791388 .zero => {
1380 result.id = .IntegerLiteral;
1389 result.tag = .integer_literal;
13811390 },
13821391 .ampersand => {
1383 result.id = .Ampersand;
1392 result.tag = .ampersand;
13841393 },
13851394 .period => {
1386 result.id = .Period;
1395 result.tag = .period;
13871396 },
13881397 .period_2 => {
1389 result.id = .Ellipsis2;
1398 result.tag = .ellipsis2;
13901399 },
13911400 .period_asterisk => {
1392 result.id = .PeriodAsterisk;
1401 result.tag = .period_asterisk;
13931402 },
13941403 .pipe => {
1395 result.id = .Pipe;
1404 result.tag = .pipe;
13961405 },
13971406 .angle_bracket_angle_bracket_right => {
1398 result.id = .AngleBracketAngleBracketRight;
1407 result.tag = .angle_bracket_angle_bracket_right;
13991408 },
14001409 .angle_bracket_right => {
1401 result.id = .AngleBracketRight;
1410 result.tag = .angle_bracket_right;
14021411 },
14031412 .angle_bracket_angle_bracket_left => {
1404 result.id = .AngleBracketAngleBracketLeft;
1413 result.tag = .angle_bracket_angle_bracket_left;
14051414 },
14061415 .angle_bracket_left => {
1407 result.id = .AngleBracketLeft;
1416 result.tag = .angle_bracket_left;
14081417 },
14091418 .plus_percent => {
1410 result.id = .PlusPercent;
1419 result.tag = .plus_percent;
14111420 },
14121421 .plus => {
1413 result.id = .Plus;
1422 result.tag = .plus;
14141423 },
14151424 .percent => {
1416 result.id = .Percent;
1425 result.tag = .percent;
14171426 },
14181427 .caret => {
1419 result.id = .Caret;
1428 result.tag = .caret;
14201429 },
14211430 .asterisk_percent => {
1422 result.id = .AsteriskPercent;
1431 result.tag = .asterisk_percent;
14231432 },
14241433 .asterisk => {
1425 result.id = .Asterisk;
1434 result.tag = .asterisk;
14261435 },
14271436 .minus_percent => {
1428 result.id = .MinusPercent;
1437 result.tag = .minus_percent;
14291438 },
14301439 }
14311440 }
14321441
1433 if (result.id == .Eof) {
1442 if (result.tag == .eof) {
14341443 if (self.pending_invalid_token) |token| {
14351444 self.pending_invalid_token = null;
14361445 return token;
14371446 }
1447 result.loc.start = self.index;
14381448 }
14391449
14401450 result.loc.end = self.index;
......@@ -1446,7 +1456,7 @@ pub const Tokenizer = struct {
14461456 const invalid_length = self.getInvalidCharacterLength();
14471457 if (invalid_length == 0) return;
14481458 self.pending_invalid_token = .{
1449 .id = .Invalid,
1459 .tag = .invalid,
14501460 .loc = .{
14511461 .start = self.index,
14521462 .end = self.index + invalid_length,
......@@ -1493,220 +1503,218 @@ pub const Tokenizer = struct {
14931503};
14941504
14951505test "tokenizer" {
1496 testTokenize("test", &[_]Token.Id{.Keyword_test});
1506 testTokenize("test", &.{.keyword_test});
1507}
1508
1509test "line comment followed by top-level comptime" {
1510 testTokenize(
1511 \\// line comment
1512 \\comptime {}
1513 \\
1514 , &.{
1515 .keyword_comptime,
1516 .l_brace,
1517 .r_brace,
1518 });
14971519}
14981520
14991521test "tokenizer - unknown length pointer and then c pointer" {
15001522 testTokenize(
15011523 \\[*]u8
15021524 \\[*c]u8
1503 , &[_]Token.Id{
1504 .LBracket,
1505 .Asterisk,
1506 .RBracket,
1507 .Identifier,
1508 .LBracket,
1509 .Asterisk,
1510 .Identifier,
1511 .RBracket,
1512 .Identifier,
1525 , &.{
1526 .l_bracket,
1527 .asterisk,
1528 .r_bracket,
1529 .identifier,
1530 .l_bracket,
1531 .asterisk,
1532 .identifier,
1533 .r_bracket,
1534 .identifier,
15131535 });
15141536}
15151537
15161538test "tokenizer - code point literal with hex escape" {
15171539 testTokenize(
15181540 \\'\x1b'
1519 , &[_]Token.Id{.CharLiteral});
1541 , &.{.char_literal});
15201542 testTokenize(
15211543 \\'\x1'
1522 , &[_]Token.Id{ .Invalid, .Invalid });
1544 , &.{ .invalid, .invalid });
15231545}
15241546
15251547test "tokenizer - code point literal with unicode escapes" {
15261548 // Valid unicode escapes
15271549 testTokenize(
15281550 \\'\u{3}'
1529 , &[_]Token.Id{.CharLiteral});
1551 , &.{.char_literal});
15301552 testTokenize(
15311553 \\'\u{01}'
1532 , &[_]Token.Id{.CharLiteral});
1554 , &.{.char_literal});
15331555 testTokenize(
15341556 \\'\u{2a}'
1535 , &[_]Token.Id{.CharLiteral});
1557 , &.{.char_literal});
15361558 testTokenize(
15371559 \\'\u{3f9}'
1538 , &[_]Token.Id{.CharLiteral});
1560 , &.{.char_literal});
15391561 testTokenize(
15401562 \\'\u{6E09aBc1523}'
1541 , &[_]Token.Id{.CharLiteral});
1563 , &.{.char_literal});
15421564 testTokenize(
15431565 \\"\u{440}"
1544 , &[_]Token.Id{.StringLiteral});
1566 , &.{.string_literal});
15451567
15461568 // Invalid unicode escapes
15471569 testTokenize(
15481570 \\'\u'
1549 , &[_]Token.Id{.Invalid});
1571 , &.{.invalid});
15501572 testTokenize(
15511573 \\'\u{{'
1552 , &[_]Token.Id{ .Invalid, .Invalid });
1574 , &.{ .invalid, .invalid });
15531575 testTokenize(
15541576 \\'\u{}'
1555 , &[_]Token.Id{ .Invalid, .Invalid });
1577 , &.{ .invalid, .invalid });
15561578 testTokenize(
15571579 \\'\u{s}'
1558 , &[_]Token.Id{ .Invalid, .Invalid });
1580 , &.{ .invalid, .invalid });
15591581 testTokenize(
15601582 \\'\u{2z}'
1561 , &[_]Token.Id{ .Invalid, .Invalid });
1583 , &.{ .invalid, .invalid });
15621584 testTokenize(
15631585 \\'\u{4a'
1564 , &[_]Token.Id{.Invalid});
1586 , &.{.invalid});
15651587
15661588 // Test old-style unicode literals
15671589 testTokenize(
15681590 \\'\u0333'
1569 , &[_]Token.Id{ .Invalid, .Invalid });
1591 , &.{ .invalid, .invalid });
15701592 testTokenize(
15711593 \\'\U0333'
1572 , &[_]Token.Id{ .Invalid, .IntegerLiteral, .Invalid });
1594 , &.{ .invalid, .integer_literal, .invalid });
15731595}
15741596
15751597test "tokenizer - code point literal with unicode code point" {
15761598 testTokenize(
15771599 \\'💩'
1578 , &[_]Token.Id{.CharLiteral});
1600 , &.{.char_literal});
15791601}
15801602
15811603test "tokenizer - float literal e exponent" {
1582 testTokenize("a = 4.94065645841246544177e-324;\n", &[_]Token.Id{
1583 .Identifier,
1584 .Equal,
1585 .FloatLiteral,
1586 .Semicolon,
1604 testTokenize("a = 4.94065645841246544177e-324;\n", &.{
1605 .identifier,
1606 .equal,
1607 .float_literal,
1608 .semicolon,
15871609 });
15881610}
15891611
15901612test "tokenizer - float literal p exponent" {
1591 testTokenize("a = 0x1.a827999fcef32p+1022;\n", &[_]Token.Id{
1592 .Identifier,
1593 .Equal,
1594 .FloatLiteral,
1595 .Semicolon,
1613 testTokenize("a = 0x1.a827999fcef32p+1022;\n", &.{
1614 .identifier,
1615 .equal,
1616 .float_literal,
1617 .semicolon,
15961618 });
15971619}
15981620
15991621test "tokenizer - chars" {
1600 testTokenize("'c'", &[_]Token.Id{.CharLiteral});
1622 testTokenize("'c'", &.{.char_literal});
16011623}
16021624
16031625test "tokenizer - invalid token characters" {
1604 testTokenize("#", &[_]Token.Id{.Invalid});
1605 testTokenize("`", &[_]Token.Id{.Invalid});
1606 testTokenize("'c", &[_]Token.Id{.Invalid});
1607 testTokenize("'", &[_]Token.Id{.Invalid});
1608 testTokenize("''", &[_]Token.Id{ .Invalid, .Invalid });
1626 testTokenize("#", &.{.invalid});
1627 testTokenize("`", &.{.invalid});
1628 testTokenize("'c", &.{.invalid});
1629 testTokenize("'", &.{.invalid});
1630 testTokenize("''", &.{ .invalid, .invalid });
16091631}
16101632
16111633test "tokenizer - invalid literal/comment characters" {
1612 testTokenize("\"\x00\"", &[_]Token.Id{
1613 .StringLiteral,
1614 .Invalid,
1634 testTokenize("\"\x00\"", &.{
1635 .string_literal,
1636 .invalid,
16151637 });
1616 testTokenize("//\x00", &[_]Token.Id{
1617 .LineComment,
1618 .Invalid,
1638 testTokenize("//\x00", &.{
1639 .invalid,
16191640 });
1620 testTokenize("//\x1f", &[_]Token.Id{
1621 .LineComment,
1622 .Invalid,
1641 testTokenize("//\x1f", &.{
1642 .invalid,
16231643 });
1624 testTokenize("//\x7f", &[_]Token.Id{
1625 .LineComment,
1626 .Invalid,
1644 testTokenize("//\x7f", &.{
1645 .invalid,
16271646 });
16281647}
16291648
16301649test "tokenizer - utf8" {
1631 testTokenize("//\xc2\x80", &[_]Token.Id{.LineComment});
1632 testTokenize("//\xf4\x8f\xbf\xbf", &[_]Token.Id{.LineComment});
1650 testTokenize("//\xc2\x80", &.{});
1651 testTokenize("//\xf4\x8f\xbf\xbf", &.{});
16331652}
16341653
16351654test "tokenizer - invalid utf8" {
1636 testTokenize("//\x80", &[_]Token.Id{
1637 .LineComment,
1638 .Invalid,
1655 testTokenize("//\x80", &.{
1656 .invalid,
16391657 });
1640 testTokenize("//\xbf", &[_]Token.Id{
1641 .LineComment,
1642 .Invalid,
1658 testTokenize("//\xbf", &.{
1659 .invalid,
16431660 });
1644 testTokenize("//\xf8", &[_]Token.Id{
1645 .LineComment,
1646 .Invalid,
1661 testTokenize("//\xf8", &.{
1662 .invalid,
16471663 });
1648 testTokenize("//\xff", &[_]Token.Id{
1649 .LineComment,
1650 .Invalid,
1664 testTokenize("//\xff", &.{
1665 .invalid,
16511666 });
1652 testTokenize("//\xc2\xc0", &[_]Token.Id{
1653 .LineComment,
1654 .Invalid,
1667 testTokenize("//\xc2\xc0", &.{
1668 .invalid,
16551669 });
1656 testTokenize("//\xe0", &[_]Token.Id{
1657 .LineComment,
1658 .Invalid,
1670 testTokenize("//\xe0", &.{
1671 .invalid,
16591672 });
1660 testTokenize("//\xf0", &[_]Token.Id{
1661 .LineComment,
1662 .Invalid,
1673 testTokenize("//\xf0", &.{
1674 .invalid,
16631675 });
1664 testTokenize("//\xf0\x90\x80\xc0", &[_]Token.Id{
1665 .LineComment,
1666 .Invalid,
1676 testTokenize("//\xf0\x90\x80\xc0", &.{
1677 .invalid,
16671678 });
16681679}
16691680
16701681test "tokenizer - illegal unicode codepoints" {
16711682 // unicode newline characters.U+0085, U+2028, U+2029
1672 testTokenize("//\xc2\x84", &[_]Token.Id{.LineComment});
1673 testTokenize("//\xc2\x85", &[_]Token.Id{
1674 .LineComment,
1675 .Invalid,
1683 testTokenize("//\xc2\x84", &.{});
1684 testTokenize("//\xc2\x85", &.{
1685 .invalid,
16761686 });
1677 testTokenize("//\xc2\x86", &[_]Token.Id{.LineComment});
1678 testTokenize("//\xe2\x80\xa7", &[_]Token.Id{.LineComment});
1679 testTokenize("//\xe2\x80\xa8", &[_]Token.Id{
1680 .LineComment,
1681 .Invalid,
1687 testTokenize("//\xc2\x86", &.{});
1688 testTokenize("//\xe2\x80\xa7", &.{});
1689 testTokenize("//\xe2\x80\xa8", &.{
1690 .invalid,
16821691 });
1683 testTokenize("//\xe2\x80\xa9", &[_]Token.Id{
1684 .LineComment,
1685 .Invalid,
1692 testTokenize("//\xe2\x80\xa9", &.{
1693 .invalid,
16861694 });
1687 testTokenize("//\xe2\x80\xaa", &[_]Token.Id{.LineComment});
1695 testTokenize("//\xe2\x80\xaa", &.{});
16881696}
16891697
16901698test "tokenizer - string identifier and builtin fns" {
16911699 testTokenize(
16921700 \\const @"if" = @import("std");
1693 , &[_]Token.Id{
1694 .Keyword_const,
1695 .Identifier,
1696 .Equal,
1697 .Builtin,
1698 .LParen,
1699 .StringLiteral,
1700 .RParen,
1701 .Semicolon,
1701 , &.{
1702 .keyword_const,
1703 .identifier,
1704 .equal,
1705 .builtin,
1706 .l_paren,
1707 .string_literal,
1708 .r_paren,
1709 .semicolon,
17021710 });
17031711}
17041712
17051713test "tokenizer - multiline string literal with literal tab" {
17061714 testTokenize(
17071715 \\\\foo bar
1708 , &[_]Token.Id{
1709 .MultilineStringLiteralLine,
1716 , &.{
1717 .multiline_string_literal_line,
17101718 });
17111719}
17121720
......@@ -1718,32 +1726,30 @@ test "tokenizer - comments with literal tab" {
17181726 \\// foo
17191727 \\/// foo
17201728 \\/// /foo
1721 , &[_]Token.Id{
1722 .LineComment,
1723 .ContainerDocComment,
1724 .DocComment,
1725 .LineComment,
1726 .DocComment,
1727 .DocComment,
1729 , &.{
1730 .container_doc_comment,
1731 .doc_comment,
1732 .doc_comment,
1733 .doc_comment,
17281734 });
17291735}
17301736
17311737test "tokenizer - pipe and then invalid" {
1732 testTokenize("||=", &[_]Token.Id{
1733 .PipePipe,
1734 .Equal,
1738 testTokenize("||=", &.{
1739 .pipe_pipe,
1740 .equal,
17351741 });
17361742}
17371743
17381744test "tokenizer - line comment and doc comment" {
1739 testTokenize("//", &[_]Token.Id{.LineComment});
1740 testTokenize("// a / b", &[_]Token.Id{.LineComment});
1741 testTokenize("// /", &[_]Token.Id{.LineComment});
1742 testTokenize("/// a", &[_]Token.Id{.DocComment});
1743 testTokenize("///", &[_]Token.Id{.DocComment});
1744 testTokenize("////", &[_]Token.Id{.LineComment});
1745 testTokenize("//!", &[_]Token.Id{.ContainerDocComment});
1746 testTokenize("//!!", &[_]Token.Id{.ContainerDocComment});
1745 testTokenize("//", &.{});
1746 testTokenize("// a / b", &.{});
1747 testTokenize("// /", &.{});
1748 testTokenize("/// a", &.{.doc_comment});
1749 testTokenize("///", &.{.doc_comment});
1750 testTokenize("////", &.{});
1751 testTokenize("//!", &.{.container_doc_comment});
1752 testTokenize("//!!", &.{.container_doc_comment});
17471753}
17481754
17491755test "tokenizer - line comment followed by identifier" {
......@@ -1751,304 +1757,304 @@ test "tokenizer - line comment followed by identifier" {
17511757 \\ Unexpected,
17521758 \\ // another
17531759 \\ Another,
1754 , &[_]Token.Id{
1755 .Identifier,
1756 .Comma,
1757 .LineComment,
1758 .Identifier,
1759 .Comma,
1760 , &.{
1761 .identifier,
1762 .comma,
1763 .identifier,
1764 .comma,
17601765 });
17611766}
17621767
17631768test "tokenizer - UTF-8 BOM is recognized and skipped" {
1764 testTokenize("\xEF\xBB\xBFa;\n", &[_]Token.Id{
1765 .Identifier,
1766 .Semicolon,
1769 testTokenize("\xEF\xBB\xBFa;\n", &.{
1770 .identifier,
1771 .semicolon,
17671772 });
17681773}
17691774
17701775test "correctly parse pointer assignment" {
1771 testTokenize("b.*=3;\n", &[_]Token.Id{
1772 .Identifier,
1773 .PeriodAsterisk,
1774 .Equal,
1775 .IntegerLiteral,
1776 .Semicolon,
1776 testTokenize("b.*=3;\n", &.{
1777 .identifier,
1778 .period_asterisk,
1779 .equal,
1780 .integer_literal,
1781 .semicolon,
17771782 });
17781783}
17791784
17801785test "correctly parse pointer dereference followed by asterisk" {
1781 testTokenize("\"b\".* ** 10", &[_]Token.Id{
1782 .StringLiteral,
1783 .PeriodAsterisk,
1784 .AsteriskAsterisk,
1785 .IntegerLiteral,
1786 testTokenize("\"b\".* ** 10", &.{
1787 .string_literal,
1788 .period_asterisk,
1789 .asterisk_asterisk,
1790 .integer_literal,
17861791 });
17871792
1788 testTokenize("(\"b\".*)** 10", &[_]Token.Id{
1789 .LParen,
1790 .StringLiteral,
1791 .PeriodAsterisk,
1792 .RParen,
1793 .AsteriskAsterisk,
1794 .IntegerLiteral,
1793 testTokenize("(\"b\".*)** 10", &.{
1794 .l_paren,
1795 .string_literal,
1796 .period_asterisk,
1797 .r_paren,
1798 .asterisk_asterisk,
1799 .integer_literal,
17951800 });
17961801
1797 testTokenize("\"b\".*** 10", &[_]Token.Id{
1798 .StringLiteral,
1799 .Invalid_periodasterisks,
1800 .AsteriskAsterisk,
1801 .IntegerLiteral,
1802 testTokenize("\"b\".*** 10", &.{
1803 .string_literal,
1804 .invalid_periodasterisks,
1805 .asterisk_asterisk,
1806 .integer_literal,
18021807 });
18031808}
18041809
18051810test "tokenizer - range literals" {
1806 testTokenize("0...9", &[_]Token.Id{ .IntegerLiteral, .Ellipsis3, .IntegerLiteral });
1807 testTokenize("'0'...'9'", &[_]Token.Id{ .CharLiteral, .Ellipsis3, .CharLiteral });
1808 testTokenize("0x00...0x09", &[_]Token.Id{ .IntegerLiteral, .Ellipsis3, .IntegerLiteral });
1809 testTokenize("0b00...0b11", &[_]Token.Id{ .IntegerLiteral, .Ellipsis3, .IntegerLiteral });
1810 testTokenize("0o00...0o11", &[_]Token.Id{ .IntegerLiteral, .Ellipsis3, .IntegerLiteral });
1811 testTokenize("0...9", &.{ .integer_literal, .ellipsis3, .integer_literal });
1812 testTokenize("'0'...'9'", &.{ .char_literal, .ellipsis3, .char_literal });
1813 testTokenize("0x00...0x09", &.{ .integer_literal, .ellipsis3, .integer_literal });
1814 testTokenize("0b00...0b11", &.{ .integer_literal, .ellipsis3, .integer_literal });
1815 testTokenize("0o00...0o11", &.{ .integer_literal, .ellipsis3, .integer_literal });
18111816}
18121817
18131818test "tokenizer - number literals decimal" {
1814 testTokenize("0", &[_]Token.Id{.IntegerLiteral});
1815 testTokenize("1", &[_]Token.Id{.IntegerLiteral});
1816 testTokenize("2", &[_]Token.Id{.IntegerLiteral});
1817 testTokenize("3", &[_]Token.Id{.IntegerLiteral});
1818 testTokenize("4", &[_]Token.Id{.IntegerLiteral});
1819 testTokenize("5", &[_]Token.Id{.IntegerLiteral});
1820 testTokenize("6", &[_]Token.Id{.IntegerLiteral});
1821 testTokenize("7", &[_]Token.Id{.IntegerLiteral});
1822 testTokenize("8", &[_]Token.Id{.IntegerLiteral});
1823 testTokenize("9", &[_]Token.Id{.IntegerLiteral});
1824 testTokenize("1..", &[_]Token.Id{ .IntegerLiteral, .Ellipsis2 });
1825 testTokenize("0a", &[_]Token.Id{ .Invalid, .Identifier });
1826 testTokenize("9b", &[_]Token.Id{ .Invalid, .Identifier });
1827 testTokenize("1z", &[_]Token.Id{ .Invalid, .Identifier });
1828 testTokenize("1z_1", &[_]Token.Id{ .Invalid, .Identifier });
1829 testTokenize("9z3", &[_]Token.Id{ .Invalid, .Identifier });
1830
1831 testTokenize("0_0", &[_]Token.Id{.IntegerLiteral});
1832 testTokenize("0001", &[_]Token.Id{.IntegerLiteral});
1833 testTokenize("01234567890", &[_]Token.Id{.IntegerLiteral});
1834 testTokenize("012_345_6789_0", &[_]Token.Id{.IntegerLiteral});
1835 testTokenize("0_1_2_3_4_5_6_7_8_9_0", &[_]Token.Id{.IntegerLiteral});
1836
1837 testTokenize("00_", &[_]Token.Id{.Invalid});
1838 testTokenize("0_0_", &[_]Token.Id{.Invalid});
1839 testTokenize("0__0", &[_]Token.Id{ .Invalid, .Identifier });
1840 testTokenize("0_0f", &[_]Token.Id{ .Invalid, .Identifier });
1841 testTokenize("0_0_f", &[_]Token.Id{ .Invalid, .Identifier });
1842 testTokenize("0_0_f_00", &[_]Token.Id{ .Invalid, .Identifier });
1843 testTokenize("1_,", &[_]Token.Id{ .Invalid, .Comma });
1844
1845 testTokenize("1.", &[_]Token.Id{.FloatLiteral});
1846 testTokenize("0.0", &[_]Token.Id{.FloatLiteral});
1847 testTokenize("1.0", &[_]Token.Id{.FloatLiteral});
1848 testTokenize("10.0", &[_]Token.Id{.FloatLiteral});
1849 testTokenize("0e0", &[_]Token.Id{.FloatLiteral});
1850 testTokenize("1e0", &[_]Token.Id{.FloatLiteral});
1851 testTokenize("1e100", &[_]Token.Id{.FloatLiteral});
1852 testTokenize("1.e100", &[_]Token.Id{.FloatLiteral});
1853 testTokenize("1.0e100", &[_]Token.Id{.FloatLiteral});
1854 testTokenize("1.0e+100", &[_]Token.Id{.FloatLiteral});
1855 testTokenize("1.0e-100", &[_]Token.Id{.FloatLiteral});
1856 testTokenize("1_0_0_0.0_0_0_0_0_1e1_0_0_0", &[_]Token.Id{.FloatLiteral});
1857 testTokenize("1.+", &[_]Token.Id{ .FloatLiteral, .Plus });
1858
1859 testTokenize("1e", &[_]Token.Id{.Invalid});
1860 testTokenize("1.0e1f0", &[_]Token.Id{ .Invalid, .Identifier });
1861 testTokenize("1.0p100", &[_]Token.Id{ .Invalid, .Identifier });
1862 testTokenize("1.0p-100", &[_]Token.Id{ .Invalid, .Identifier, .Minus, .IntegerLiteral });
1863 testTokenize("1.0p1f0", &[_]Token.Id{ .Invalid, .Identifier });
1864 testTokenize("1.0_,", &[_]Token.Id{ .Invalid, .Comma });
1865 testTokenize("1_.0", &[_]Token.Id{ .Invalid, .Period, .IntegerLiteral });
1866 testTokenize("1._", &[_]Token.Id{ .Invalid, .Identifier });
1867 testTokenize("1.a", &[_]Token.Id{ .Invalid, .Identifier });
1868 testTokenize("1.z", &[_]Token.Id{ .Invalid, .Identifier });
1869 testTokenize("1._0", &[_]Token.Id{ .Invalid, .Identifier });
1870 testTokenize("1._+", &[_]Token.Id{ .Invalid, .Identifier, .Plus });
1871 testTokenize("1._e", &[_]Token.Id{ .Invalid, .Identifier });
1872 testTokenize("1.0e", &[_]Token.Id{.Invalid});
1873 testTokenize("1.0e,", &[_]Token.Id{ .Invalid, .Comma });
1874 testTokenize("1.0e_", &[_]Token.Id{ .Invalid, .Identifier });
1875 testTokenize("1.0e+_", &[_]Token.Id{ .Invalid, .Identifier });
1876 testTokenize("1.0e-_", &[_]Token.Id{ .Invalid, .Identifier });
1877 testTokenize("1.0e0_+", &[_]Token.Id{ .Invalid, .Plus });
1819 testTokenize("0", &.{.integer_literal});
1820 testTokenize("1", &.{.integer_literal});
1821 testTokenize("2", &.{.integer_literal});
1822 testTokenize("3", &.{.integer_literal});
1823 testTokenize("4", &.{.integer_literal});
1824 testTokenize("5", &.{.integer_literal});
1825 testTokenize("6", &.{.integer_literal});
1826 testTokenize("7", &.{.integer_literal});
1827 testTokenize("8", &.{.integer_literal});
1828 testTokenize("9", &.{.integer_literal});
1829 testTokenize("1..", &.{ .integer_literal, .ellipsis2 });
1830 testTokenize("0a", &.{ .invalid, .identifier });
1831 testTokenize("9b", &.{ .invalid, .identifier });
1832 testTokenize("1z", &.{ .invalid, .identifier });
1833 testTokenize("1z_1", &.{ .invalid, .identifier });
1834 testTokenize("9z3", &.{ .invalid, .identifier });
1835
1836 testTokenize("0_0", &.{.integer_literal});
1837 testTokenize("0001", &.{.integer_literal});
1838 testTokenize("01234567890", &.{.integer_literal});
1839 testTokenize("012_345_6789_0", &.{.integer_literal});
1840 testTokenize("0_1_2_3_4_5_6_7_8_9_0", &.{.integer_literal});
1841
1842 testTokenize("00_", &.{.invalid});
1843 testTokenize("0_0_", &.{.invalid});
1844 testTokenize("0__0", &.{ .invalid, .identifier });
1845 testTokenize("0_0f", &.{ .invalid, .identifier });
1846 testTokenize("0_0_f", &.{ .invalid, .identifier });
1847 testTokenize("0_0_f_00", &.{ .invalid, .identifier });
1848 testTokenize("1_,", &.{ .invalid, .comma });
1849
1850 testTokenize("1.", &.{.float_literal});
1851 testTokenize("0.0", &.{.float_literal});
1852 testTokenize("1.0", &.{.float_literal});
1853 testTokenize("10.0", &.{.float_literal});
1854 testTokenize("0e0", &.{.float_literal});
1855 testTokenize("1e0", &.{.float_literal});
1856 testTokenize("1e100", &.{.float_literal});
1857 testTokenize("1.e100", &.{.float_literal});
1858 testTokenize("1.0e100", &.{.float_literal});
1859 testTokenize("1.0e+100", &.{.float_literal});
1860 testTokenize("1.0e-100", &.{.float_literal});
1861 testTokenize("1_0_0_0.0_0_0_0_0_1e1_0_0_0", &.{.float_literal});
1862 testTokenize("1.+", &.{ .float_literal, .plus });
1863
1864 testTokenize("1e", &.{.invalid});
1865 testTokenize("1.0e1f0", &.{ .invalid, .identifier });
1866 testTokenize("1.0p100", &.{ .invalid, .identifier });
1867 testTokenize("1.0p-100", &.{ .invalid, .identifier, .minus, .integer_literal });
1868 testTokenize("1.0p1f0", &.{ .invalid, .identifier });
1869 testTokenize("1.0_,", &.{ .invalid, .comma });
1870 testTokenize("1_.0", &.{ .invalid, .period, .integer_literal });
1871 testTokenize("1._", &.{ .invalid, .identifier });
1872 testTokenize("1.a", &.{ .invalid, .identifier });
1873 testTokenize("1.z", &.{ .invalid, .identifier });
1874 testTokenize("1._0", &.{ .invalid, .identifier });
1875 testTokenize("1._+", &.{ .invalid, .identifier, .plus });
1876 testTokenize("1._e", &.{ .invalid, .identifier });
1877 testTokenize("1.0e", &.{.invalid});
1878 testTokenize("1.0e,", &.{ .invalid, .comma });
1879 testTokenize("1.0e_", &.{ .invalid, .identifier });
1880 testTokenize("1.0e+_", &.{ .invalid, .identifier });
1881 testTokenize("1.0e-_", &.{ .invalid, .identifier });
1882 testTokenize("1.0e0_+", &.{ .invalid, .plus });
18781883}
18791884
18801885test "tokenizer - number literals binary" {
1881 testTokenize("0b0", &[_]Token.Id{.IntegerLiteral});
1882 testTokenize("0b1", &[_]Token.Id{.IntegerLiteral});
1883 testTokenize("0b2", &[_]Token.Id{ .Invalid, .IntegerLiteral });
1884 testTokenize("0b3", &[_]Token.Id{ .Invalid, .IntegerLiteral });
1885 testTokenize("0b4", &[_]Token.Id{ .Invalid, .IntegerLiteral });
1886 testTokenize("0b5", &[_]Token.Id{ .Invalid, .IntegerLiteral });
1887 testTokenize("0b6", &[_]Token.Id{ .Invalid, .IntegerLiteral });
1888 testTokenize("0b7", &[_]Token.Id{ .Invalid, .IntegerLiteral });
1889 testTokenize("0b8", &[_]Token.Id{ .Invalid, .IntegerLiteral });
1890 testTokenize("0b9", &[_]Token.Id{ .Invalid, .IntegerLiteral });
1891 testTokenize("0ba", &[_]Token.Id{ .Invalid, .Identifier });
1892 testTokenize("0bb", &[_]Token.Id{ .Invalid, .Identifier });
1893 testTokenize("0bc", &[_]Token.Id{ .Invalid, .Identifier });
1894 testTokenize("0bd", &[_]Token.Id{ .Invalid, .Identifier });
1895 testTokenize("0be", &[_]Token.Id{ .Invalid, .Identifier });
1896 testTokenize("0bf", &[_]Token.Id{ .Invalid, .Identifier });
1897 testTokenize("0bz", &[_]Token.Id{ .Invalid, .Identifier });
1898
1899 testTokenize("0b0000_0000", &[_]Token.Id{.IntegerLiteral});
1900 testTokenize("0b1111_1111", &[_]Token.Id{.IntegerLiteral});
1901 testTokenize("0b10_10_10_10", &[_]Token.Id{.IntegerLiteral});
1902 testTokenize("0b0_1_0_1_0_1_0_1", &[_]Token.Id{.IntegerLiteral});
1903 testTokenize("0b1.", &[_]Token.Id{ .IntegerLiteral, .Period });
1904 testTokenize("0b1.0", &[_]Token.Id{ .IntegerLiteral, .Period, .IntegerLiteral });
1905
1906 testTokenize("0B0", &[_]Token.Id{ .Invalid, .Identifier });
1907 testTokenize("0b_", &[_]Token.Id{ .Invalid, .Identifier });
1908 testTokenize("0b_0", &[_]Token.Id{ .Invalid, .Identifier });
1909 testTokenize("0b1_", &[_]Token.Id{.Invalid});
1910 testTokenize("0b0__1", &[_]Token.Id{ .Invalid, .Identifier });
1911 testTokenize("0b0_1_", &[_]Token.Id{.Invalid});
1912 testTokenize("0b1e", &[_]Token.Id{ .Invalid, .Identifier });
1913 testTokenize("0b1p", &[_]Token.Id{ .Invalid, .Identifier });
1914 testTokenize("0b1e0", &[_]Token.Id{ .Invalid, .Identifier });
1915 testTokenize("0b1p0", &[_]Token.Id{ .Invalid, .Identifier });
1916 testTokenize("0b1_,", &[_]Token.Id{ .Invalid, .Comma });
1886 testTokenize("0b0", &.{.integer_literal});
1887 testTokenize("0b1", &.{.integer_literal});
1888 testTokenize("0b2", &.{ .invalid, .integer_literal });
1889 testTokenize("0b3", &.{ .invalid, .integer_literal });
1890 testTokenize("0b4", &.{ .invalid, .integer_literal });
1891 testTokenize("0b5", &.{ .invalid, .integer_literal });
1892 testTokenize("0b6", &.{ .invalid, .integer_literal });
1893 testTokenize("0b7", &.{ .invalid, .integer_literal });
1894 testTokenize("0b8", &.{ .invalid, .integer_literal });
1895 testTokenize("0b9", &.{ .invalid, .integer_literal });
1896 testTokenize("0ba", &.{ .invalid, .identifier });
1897 testTokenize("0bb", &.{ .invalid, .identifier });
1898 testTokenize("0bc", &.{ .invalid, .identifier });
1899 testTokenize("0bd", &.{ .invalid, .identifier });
1900 testTokenize("0be", &.{ .invalid, .identifier });
1901 testTokenize("0bf", &.{ .invalid, .identifier });
1902 testTokenize("0bz", &.{ .invalid, .identifier });
1903
1904 testTokenize("0b0000_0000", &.{.integer_literal});
1905 testTokenize("0b1111_1111", &.{.integer_literal});
1906 testTokenize("0b10_10_10_10", &.{.integer_literal});
1907 testTokenize("0b0_1_0_1_0_1_0_1", &.{.integer_literal});
1908 testTokenize("0b1.", &.{ .integer_literal, .period });
1909 testTokenize("0b1.0", &.{ .integer_literal, .period, .integer_literal });
1910
1911 testTokenize("0B0", &.{ .invalid, .identifier });
1912 testTokenize("0b_", &.{ .invalid, .identifier });
1913 testTokenize("0b_0", &.{ .invalid, .identifier });
1914 testTokenize("0b1_", &.{.invalid});
1915 testTokenize("0b0__1", &.{ .invalid, .identifier });
1916 testTokenize("0b0_1_", &.{.invalid});
1917 testTokenize("0b1e", &.{ .invalid, .identifier });
1918 testTokenize("0b1p", &.{ .invalid, .identifier });
1919 testTokenize("0b1e0", &.{ .invalid, .identifier });
1920 testTokenize("0b1p0", &.{ .invalid, .identifier });
1921 testTokenize("0b1_,", &.{ .invalid, .comma });
19171922}
19181923
19191924test "tokenizer - number literals octal" {
1920 testTokenize("0o0", &[_]Token.Id{.IntegerLiteral});
1921 testTokenize("0o1", &[_]Token.Id{.IntegerLiteral});
1922 testTokenize("0o2", &[_]Token.Id{.IntegerLiteral});
1923 testTokenize("0o3", &[_]Token.Id{.IntegerLiteral});
1924 testTokenize("0o4", &[_]Token.Id{.IntegerLiteral});
1925 testTokenize("0o5", &[_]Token.Id{.IntegerLiteral});
1926 testTokenize("0o6", &[_]Token.Id{.IntegerLiteral});
1927 testTokenize("0o7", &[_]Token.Id{.IntegerLiteral});
1928 testTokenize("0o8", &[_]Token.Id{ .Invalid, .IntegerLiteral });
1929 testTokenize("0o9", &[_]Token.Id{ .Invalid, .IntegerLiteral });
1930 testTokenize("0oa", &[_]Token.Id{ .Invalid, .Identifier });
1931 testTokenize("0ob", &[_]Token.Id{ .Invalid, .Identifier });
1932 testTokenize("0oc", &[_]Token.Id{ .Invalid, .Identifier });
1933 testTokenize("0od", &[_]Token.Id{ .Invalid, .Identifier });
1934 testTokenize("0oe", &[_]Token.Id{ .Invalid, .Identifier });
1935 testTokenize("0of", &[_]Token.Id{ .Invalid, .Identifier });
1936 testTokenize("0oz", &[_]Token.Id{ .Invalid, .Identifier });
1937
1938 testTokenize("0o01234567", &[_]Token.Id{.IntegerLiteral});
1939 testTokenize("0o0123_4567", &[_]Token.Id{.IntegerLiteral});
1940 testTokenize("0o01_23_45_67", &[_]Token.Id{.IntegerLiteral});
1941 testTokenize("0o0_1_2_3_4_5_6_7", &[_]Token.Id{.IntegerLiteral});
1942 testTokenize("0o7.", &[_]Token.Id{ .IntegerLiteral, .Period });
1943 testTokenize("0o7.0", &[_]Token.Id{ .IntegerLiteral, .Period, .IntegerLiteral });
1944
1945 testTokenize("0O0", &[_]Token.Id{ .Invalid, .Identifier });
1946 testTokenize("0o_", &[_]Token.Id{ .Invalid, .Identifier });
1947 testTokenize("0o_0", &[_]Token.Id{ .Invalid, .Identifier });
1948 testTokenize("0o1_", &[_]Token.Id{.Invalid});
1949 testTokenize("0o0__1", &[_]Token.Id{ .Invalid, .Identifier });
1950 testTokenize("0o0_1_", &[_]Token.Id{.Invalid});
1951 testTokenize("0o1e", &[_]Token.Id{ .Invalid, .Identifier });
1952 testTokenize("0o1p", &[_]Token.Id{ .Invalid, .Identifier });
1953 testTokenize("0o1e0", &[_]Token.Id{ .Invalid, .Identifier });
1954 testTokenize("0o1p0", &[_]Token.Id{ .Invalid, .Identifier });
1955 testTokenize("0o_,", &[_]Token.Id{ .Invalid, .Identifier, .Comma });
1925 testTokenize("0o0", &.{.integer_literal});
1926 testTokenize("0o1", &.{.integer_literal});
1927 testTokenize("0o2", &.{.integer_literal});
1928 testTokenize("0o3", &.{.integer_literal});
1929 testTokenize("0o4", &.{.integer_literal});
1930 testTokenize("0o5", &.{.integer_literal});
1931 testTokenize("0o6", &.{.integer_literal});
1932 testTokenize("0o7", &.{.integer_literal});
1933 testTokenize("0o8", &.{ .invalid, .integer_literal });
1934 testTokenize("0o9", &.{ .invalid, .integer_literal });
1935 testTokenize("0oa", &.{ .invalid, .identifier });
1936 testTokenize("0ob", &.{ .invalid, .identifier });
1937 testTokenize("0oc", &.{ .invalid, .identifier });
1938 testTokenize("0od", &.{ .invalid, .identifier });
1939 testTokenize("0oe", &.{ .invalid, .identifier });
1940 testTokenize("0of", &.{ .invalid, .identifier });
1941 testTokenize("0oz", &.{ .invalid, .identifier });
1942
1943 testTokenize("0o01234567", &.{.integer_literal});
1944 testTokenize("0o0123_4567", &.{.integer_literal});
1945 testTokenize("0o01_23_45_67", &.{.integer_literal});
1946 testTokenize("0o0_1_2_3_4_5_6_7", &.{.integer_literal});
1947 testTokenize("0o7.", &.{ .integer_literal, .period });
1948 testTokenize("0o7.0", &.{ .integer_literal, .period, .integer_literal });
1949
1950 testTokenize("0O0", &.{ .invalid, .identifier });
1951 testTokenize("0o_", &.{ .invalid, .identifier });
1952 testTokenize("0o_0", &.{ .invalid, .identifier });
1953 testTokenize("0o1_", &.{.invalid});
1954 testTokenize("0o0__1", &.{ .invalid, .identifier });
1955 testTokenize("0o0_1_", &.{.invalid});
1956 testTokenize("0o1e", &.{ .invalid, .identifier });
1957 testTokenize("0o1p", &.{ .invalid, .identifier });
1958 testTokenize("0o1e0", &.{ .invalid, .identifier });
1959 testTokenize("0o1p0", &.{ .invalid, .identifier });
1960 testTokenize("0o_,", &.{ .invalid, .identifier, .comma });
19561961}
19571962
19581963test "tokenizer - number literals hexadeciaml" {
1959 testTokenize("0x0", &[_]Token.Id{.IntegerLiteral});
1960 testTokenize("0x1", &[_]Token.Id{.IntegerLiteral});
1961 testTokenize("0x2", &[_]Token.Id{.IntegerLiteral});
1962 testTokenize("0x3", &[_]Token.Id{.IntegerLiteral});
1963 testTokenize("0x4", &[_]Token.Id{.IntegerLiteral});
1964 testTokenize("0x5", &[_]Token.Id{.IntegerLiteral});
1965 testTokenize("0x6", &[_]Token.Id{.IntegerLiteral});
1966 testTokenize("0x7", &[_]Token.Id{.IntegerLiteral});
1967 testTokenize("0x8", &[_]Token.Id{.IntegerLiteral});
1968 testTokenize("0x9", &[_]Token.Id{.IntegerLiteral});
1969 testTokenize("0xa", &[_]Token.Id{.IntegerLiteral});
1970 testTokenize("0xb", &[_]Token.Id{.IntegerLiteral});
1971 testTokenize("0xc", &[_]Token.Id{.IntegerLiteral});
1972 testTokenize("0xd", &[_]Token.Id{.IntegerLiteral});
1973 testTokenize("0xe", &[_]Token.Id{.IntegerLiteral});
1974 testTokenize("0xf", &[_]Token.Id{.IntegerLiteral});
1975 testTokenize("0xA", &[_]Token.Id{.IntegerLiteral});
1976 testTokenize("0xB", &[_]Token.Id{.IntegerLiteral});
1977 testTokenize("0xC", &[_]Token.Id{.IntegerLiteral});
1978 testTokenize("0xD", &[_]Token.Id{.IntegerLiteral});
1979 testTokenize("0xE", &[_]Token.Id{.IntegerLiteral});
1980 testTokenize("0xF", &[_]Token.Id{.IntegerLiteral});
1981 testTokenize("0x0z", &[_]Token.Id{ .Invalid, .Identifier });
1982 testTokenize("0xz", &[_]Token.Id{ .Invalid, .Identifier });
1983
1984 testTokenize("0x0123456789ABCDEF", &[_]Token.Id{.IntegerLiteral});
1985 testTokenize("0x0123_4567_89AB_CDEF", &[_]Token.Id{.IntegerLiteral});
1986 testTokenize("0x01_23_45_67_89AB_CDE_F", &[_]Token.Id{.IntegerLiteral});
1987 testTokenize("0x0_1_2_3_4_5_6_7_8_9_A_B_C_D_E_F", &[_]Token.Id{.IntegerLiteral});
1988
1989 testTokenize("0X0", &[_]Token.Id{ .Invalid, .Identifier });
1990 testTokenize("0x_", &[_]Token.Id{ .Invalid, .Identifier });
1991 testTokenize("0x_1", &[_]Token.Id{ .Invalid, .Identifier });
1992 testTokenize("0x1_", &[_]Token.Id{.Invalid});
1993 testTokenize("0x0__1", &[_]Token.Id{ .Invalid, .Identifier });
1994 testTokenize("0x0_1_", &[_]Token.Id{.Invalid});
1995 testTokenize("0x_,", &[_]Token.Id{ .Invalid, .Identifier, .Comma });
1996
1997 testTokenize("0x1.", &[_]Token.Id{.FloatLiteral});
1998 testTokenize("0x1.0", &[_]Token.Id{.FloatLiteral});
1999 testTokenize("0xF.", &[_]Token.Id{.FloatLiteral});
2000 testTokenize("0xF.0", &[_]Token.Id{.FloatLiteral});
2001 testTokenize("0xF.F", &[_]Token.Id{.FloatLiteral});
2002 testTokenize("0xF.Fp0", &[_]Token.Id{.FloatLiteral});
2003 testTokenize("0xF.FP0", &[_]Token.Id{.FloatLiteral});
2004 testTokenize("0x1p0", &[_]Token.Id{.FloatLiteral});
2005 testTokenize("0xfp0", &[_]Token.Id{.FloatLiteral});
2006 testTokenize("0x1.+0xF.", &[_]Token.Id{ .FloatLiteral, .Plus, .FloatLiteral });
2007
2008 testTokenize("0x0123456.789ABCDEF", &[_]Token.Id{.FloatLiteral});
2009 testTokenize("0x0_123_456.789_ABC_DEF", &[_]Token.Id{.FloatLiteral});
2010 testTokenize("0x0_1_2_3_4_5_6.7_8_9_A_B_C_D_E_F", &[_]Token.Id{.FloatLiteral});
2011 testTokenize("0x0p0", &[_]Token.Id{.FloatLiteral});
2012 testTokenize("0x0.0p0", &[_]Token.Id{.FloatLiteral});
2013 testTokenize("0xff.ffp10", &[_]Token.Id{.FloatLiteral});
2014 testTokenize("0xff.ffP10", &[_]Token.Id{.FloatLiteral});
2015 testTokenize("0xff.p10", &[_]Token.Id{.FloatLiteral});
2016 testTokenize("0xffp10", &[_]Token.Id{.FloatLiteral});
2017 testTokenize("0xff_ff.ff_ffp1_0_0_0", &[_]Token.Id{.FloatLiteral});
2018 testTokenize("0xf_f_f_f.f_f_f_fp+1_000", &[_]Token.Id{.FloatLiteral});
2019 testTokenize("0xf_f_f_f.f_f_f_fp-1_00_0", &[_]Token.Id{.FloatLiteral});
2020
2021 testTokenize("0x1e", &[_]Token.Id{.IntegerLiteral});
2022 testTokenize("0x1e0", &[_]Token.Id{.IntegerLiteral});
2023 testTokenize("0x1p", &[_]Token.Id{.Invalid});
2024 testTokenize("0xfp0z1", &[_]Token.Id{ .Invalid, .Identifier });
2025 testTokenize("0xff.ffpff", &[_]Token.Id{ .Invalid, .Identifier });
2026 testTokenize("0x0.p", &[_]Token.Id{.Invalid});
2027 testTokenize("0x0.z", &[_]Token.Id{ .Invalid, .Identifier });
2028 testTokenize("0x0._", &[_]Token.Id{ .Invalid, .Identifier });
2029 testTokenize("0x0_.0", &[_]Token.Id{ .Invalid, .Period, .IntegerLiteral });
2030 testTokenize("0x0_.0.0", &[_]Token.Id{ .Invalid, .Period, .FloatLiteral });
2031 testTokenize("0x0._0", &[_]Token.Id{ .Invalid, .Identifier });
2032 testTokenize("0x0.0_", &[_]Token.Id{.Invalid});
2033 testTokenize("0x0_p0", &[_]Token.Id{ .Invalid, .Identifier });
2034 testTokenize("0x0_.p0", &[_]Token.Id{ .Invalid, .Period, .Identifier });
2035 testTokenize("0x0._p0", &[_]Token.Id{ .Invalid, .Identifier });
2036 testTokenize("0x0.0_p0", &[_]Token.Id{ .Invalid, .Identifier });
2037 testTokenize("0x0._0p0", &[_]Token.Id{ .Invalid, .Identifier });
2038 testTokenize("0x0.0p_0", &[_]Token.Id{ .Invalid, .Identifier });
2039 testTokenize("0x0.0p+_0", &[_]Token.Id{ .Invalid, .Identifier });
2040 testTokenize("0x0.0p-_0", &[_]Token.Id{ .Invalid, .Identifier });
2041 testTokenize("0x0.0p0_", &[_]Token.Id{ .Invalid, .Eof });
1964 testTokenize("0x0", &.{.integer_literal});
1965 testTokenize("0x1", &.{.integer_literal});
1966 testTokenize("0x2", &.{.integer_literal});
1967 testTokenize("0x3", &.{.integer_literal});
1968 testTokenize("0x4", &.{.integer_literal});
1969 testTokenize("0x5", &.{.integer_literal});
1970 testTokenize("0x6", &.{.integer_literal});
1971 testTokenize("0x7", &.{.integer_literal});
1972 testTokenize("0x8", &.{.integer_literal});
1973 testTokenize("0x9", &.{.integer_literal});
1974 testTokenize("0xa", &.{.integer_literal});
1975 testTokenize("0xb", &.{.integer_literal});
1976 testTokenize("0xc", &.{.integer_literal});
1977 testTokenize("0xd", &.{.integer_literal});
1978 testTokenize("0xe", &.{.integer_literal});
1979 testTokenize("0xf", &.{.integer_literal});
1980 testTokenize("0xA", &.{.integer_literal});
1981 testTokenize("0xB", &.{.integer_literal});
1982 testTokenize("0xC", &.{.integer_literal});
1983 testTokenize("0xD", &.{.integer_literal});
1984 testTokenize("0xE", &.{.integer_literal});
1985 testTokenize("0xF", &.{.integer_literal});
1986 testTokenize("0x0z", &.{ .invalid, .identifier });
1987 testTokenize("0xz", &.{ .invalid, .identifier });
1988
1989 testTokenize("0x0123456789ABCDEF", &.{.integer_literal});
1990 testTokenize("0x0123_4567_89AB_CDEF", &.{.integer_literal});
1991 testTokenize("0x01_23_45_67_89AB_CDE_F", &.{.integer_literal});
1992 testTokenize("0x0_1_2_3_4_5_6_7_8_9_A_B_C_D_E_F", &.{.integer_literal});
1993
1994 testTokenize("0X0", &.{ .invalid, .identifier });
1995 testTokenize("0x_", &.{ .invalid, .identifier });
1996 testTokenize("0x_1", &.{ .invalid, .identifier });
1997 testTokenize("0x1_", &.{.invalid});
1998 testTokenize("0x0__1", &.{ .invalid, .identifier });
1999 testTokenize("0x0_1_", &.{.invalid});
2000 testTokenize("0x_,", &.{ .invalid, .identifier, .comma });
2001
2002 testTokenize("0x1.", &.{.float_literal});
2003 testTokenize("0x1.0", &.{.float_literal});
2004 testTokenize("0xF.", &.{.float_literal});
2005 testTokenize("0xF.0", &.{.float_literal});
2006 testTokenize("0xF.F", &.{.float_literal});
2007 testTokenize("0xF.Fp0", &.{.float_literal});
2008 testTokenize("0xF.FP0", &.{.float_literal});
2009 testTokenize("0x1p0", &.{.float_literal});
2010 testTokenize("0xfp0", &.{.float_literal});
2011 testTokenize("0x1.+0xF.", &.{ .float_literal, .plus, .float_literal });
2012
2013 testTokenize("0x0123456.789ABCDEF", &.{.float_literal});
2014 testTokenize("0x0_123_456.789_ABC_DEF", &.{.float_literal});
2015 testTokenize("0x0_1_2_3_4_5_6.7_8_9_A_B_C_D_E_F", &.{.float_literal});
2016 testTokenize("0x0p0", &.{.float_literal});
2017 testTokenize("0x0.0p0", &.{.float_literal});
2018 testTokenize("0xff.ffp10", &.{.float_literal});
2019 testTokenize("0xff.ffP10", &.{.float_literal});
2020 testTokenize("0xff.p10", &.{.float_literal});
2021 testTokenize("0xffp10", &.{.float_literal});
2022 testTokenize("0xff_ff.ff_ffp1_0_0_0", &.{.float_literal});
2023 testTokenize("0xf_f_f_f.f_f_f_fp+1_000", &.{.float_literal});
2024 testTokenize("0xf_f_f_f.f_f_f_fp-1_00_0", &.{.float_literal});
2025
2026 testTokenize("0x1e", &.{.integer_literal});
2027 testTokenize("0x1e0", &.{.integer_literal});
2028 testTokenize("0x1p", &.{.invalid});
2029 testTokenize("0xfp0z1", &.{ .invalid, .identifier });
2030 testTokenize("0xff.ffpff", &.{ .invalid, .identifier });
2031 testTokenize("0x0.p", &.{.invalid});
2032 testTokenize("0x0.z", &.{ .invalid, .identifier });
2033 testTokenize("0x0._", &.{ .invalid, .identifier });
2034 testTokenize("0x0_.0", &.{ .invalid, .period, .integer_literal });
2035 testTokenize("0x0_.0.0", &.{ .invalid, .period, .float_literal });
2036 testTokenize("0x0._0", &.{ .invalid, .identifier });
2037 testTokenize("0x0.0_", &.{.invalid});
2038 testTokenize("0x0_p0", &.{ .invalid, .identifier });
2039 testTokenize("0x0_.p0", &.{ .invalid, .period, .identifier });
2040 testTokenize("0x0._p0", &.{ .invalid, .identifier });
2041 testTokenize("0x0.0_p0", &.{ .invalid, .identifier });
2042 testTokenize("0x0._0p0", &.{ .invalid, .identifier });
2043 testTokenize("0x0.0p_0", &.{ .invalid, .identifier });
2044 testTokenize("0x0.0p+_0", &.{ .invalid, .identifier });
2045 testTokenize("0x0.0p-_0", &.{ .invalid, .identifier });
2046 testTokenize("0x0.0p0_", &.{ .invalid, .eof });
20422047}
20432048
2044fn testTokenize(source: []const u8, expected_tokens: []const Token.Id) void {
2049fn testTokenize(source: []const u8, expected_tokens: []const Token.Tag) void {
20452050 var tokenizer = Tokenizer.init(source);
20462051 for (expected_tokens) |expected_token_id| {
20472052 const token = tokenizer.next();
2048 if (token.id != expected_token_id) {
2049 std.debug.panic("expected {s}, found {s}\n", .{ @tagName(expected_token_id), @tagName(token.id) });
2053 if (token.tag != expected_token_id) {
2054 std.debug.panic("expected {s}, found {s}\n", .{ @tagName(expected_token_id), @tagName(token.tag) });
20502055 }
20512056 }
20522057 const last_token = tokenizer.next();
2053 std.testing.expect(last_token.id == .Eof);
2058 std.testing.expect(last_token.tag == .eof);
2059 std.testing.expect(last_token.loc.start == source.len);
20542060}
src/BuiltinFn.zig created+844
......@@ -0,0 +1,844 @@
1const std = @import("std");
2
3pub const Tag = enum {
4 add_with_overflow,
5 align_cast,
6 align_of,
7 as,
8 async_call,
9 atomic_load,
10 atomic_rmw,
11 atomic_store,
12 bit_cast,
13 bit_offset_of,
14 bool_to_int,
15 bit_size_of,
16 breakpoint,
17 mul_add,
18 byte_swap,
19 bit_reverse,
20 byte_offset_of,
21 call,
22 c_define,
23 c_import,
24 c_include,
25 clz,
26 cmpxchg_strong,
27 cmpxchg_weak,
28 compile_error,
29 compile_log,
30 ctz,
31 c_undef,
32 div_exact,
33 div_floor,
34 div_trunc,
35 embed_file,
36 enum_to_int,
37 error_name,
38 error_return_trace,
39 error_to_int,
40 err_set_cast,
41 @"export",
42 fence,
43 field,
44 field_parent_ptr,
45 float_cast,
46 float_to_int,
47 frame,
48 Frame,
49 frame_address,
50 frame_size,
51 has_decl,
52 has_field,
53 import,
54 int_cast,
55 int_to_enum,
56 int_to_error,
57 int_to_float,
58 int_to_ptr,
59 memcpy,
60 memset,
61 wasm_memory_size,
62 wasm_memory_grow,
63 mod,
64 mul_with_overflow,
65 panic,
66 pop_count,
67 ptr_cast,
68 ptr_to_int,
69 rem,
70 return_address,
71 set_align_stack,
72 set_cold,
73 set_eval_branch_quota,
74 set_float_mode,
75 set_runtime_safety,
76 shl_exact,
77 shl_with_overflow,
78 shr_exact,
79 shuffle,
80 size_of,
81 splat,
82 reduce,
83 src,
84 sqrt,
85 sin,
86 cos,
87 exp,
88 exp2,
89 log,
90 log2,
91 log10,
92 fabs,
93 floor,
94 ceil,
95 trunc,
96 round,
97 sub_with_overflow,
98 tag_name,
99 This,
100 truncate,
101 Type,
102 type_info,
103 type_name,
104 TypeOf,
105 union_init,
106};
107
108tag: Tag,
109
110/// `true` if the builtin call can take advantage of a result location pointer.
111needs_mem_loc: bool = false,
112/// `true` if the builtin call can be the left-hand side of an expression (assigned to).
113allows_lvalue: bool = false,
114/// The number of parameters to this builtin function. `null` means variable number
115/// of parameters.
116param_count: ?u8,
117
118pub const list = list: {
119 @setEvalBranchQuota(3000);
120 break :list std.ComptimeStringMap(@This(), .{
121 .{
122 "@addWithOverflow",
123 .{
124 .tag = .add_with_overflow,
125 .param_count = 4,
126 },
127 },
128 .{
129 "@alignCast",
130 .{
131 .tag = .align_cast,
132 .param_count = 1,
133 },
134 },
135 .{
136 "@alignOf",
137 .{
138 .tag = .align_of,
139 .param_count = 1,
140 },
141 },
142 .{
143 "@as",
144 .{
145 .tag = .as,
146 .needs_mem_loc = true,
147 .param_count = 2,
148 },
149 },
150 .{
151 "@asyncCall",
152 .{
153 .tag = .async_call,
154 .param_count = null,
155 },
156 },
157 .{
158 "@atomicLoad",
159 .{
160 .tag = .atomic_load,
161 .param_count = 3,
162 },
163 },
164 .{
165 "@atomicRmw",
166 .{
167 .tag = .atomic_rmw,
168 .param_count = 5,
169 },
170 },
171 .{
172 "@atomicStore",
173 .{
174 .tag = .atomic_store,
175 .param_count = 4,
176 },
177 },
178 .{
179 "@bitCast",
180 .{
181 .tag = .bit_cast,
182 .needs_mem_loc = true,
183 .param_count = 2,
184 },
185 },
186 .{
187 "@bitOffsetOf",
188 .{
189 .tag = .bit_offset_of,
190 .param_count = 2,
191 },
192 },
193 .{
194 "@boolToInt",
195 .{
196 .tag = .bool_to_int,
197 .param_count = 1,
198 },
199 },
200 .{
201 "@bitSizeOf",
202 .{
203 .tag = .bit_size_of,
204 .param_count = 1,
205 },
206 },
207 .{
208 "@breakpoint",
209 .{
210 .tag = .breakpoint,
211 .param_count = 0,
212 },
213 },
214 .{
215 "@mulAdd",
216 .{
217 .tag = .mul_add,
218 .param_count = 4,
219 },
220 },
221 .{
222 "@byteSwap",
223 .{
224 .tag = .byte_swap,
225 .param_count = 2,
226 },
227 },
228 .{
229 "@bitReverse",
230 .{
231 .tag = .bit_reverse,
232 .param_count = 2,
233 },
234 },
235 .{
236 "@byteOffsetOf",
237 .{
238 .tag = .byte_offset_of,
239 .param_count = 2,
240 },
241 },
242 .{
243 "@call",
244 .{
245 .tag = .call,
246 .needs_mem_loc = true,
247 .param_count = 3,
248 },
249 },
250 .{
251 "@cDefine",
252 .{
253 .tag = .c_define,
254 .param_count = 2,
255 },
256 },
257 .{
258 "@cImport",
259 .{
260 .tag = .c_import,
261 .param_count = 1,
262 },
263 },
264 .{
265 "@cInclude",
266 .{
267 .tag = .c_include,
268 .param_count = 1,
269 },
270 },
271 .{
272 "@clz",
273 .{
274 .tag = .clz,
275 .param_count = 2,
276 },
277 },
278 .{
279 "@cmpxchgStrong",
280 .{
281 .tag = .cmpxchg_strong,
282 .param_count = 6,
283 },
284 },
285 .{
286 "@cmpxchgWeak",
287 .{
288 .tag = .cmpxchg_weak,
289 .param_count = 6,
290 },
291 },
292 .{
293 "@compileError",
294 .{
295 .tag = .compile_error,
296 .param_count = 1,
297 },
298 },
299 .{
300 "@compileLog",
301 .{
302 .tag = .compile_log,
303 .param_count = null,
304 },
305 },
306 .{
307 "@ctz",
308 .{
309 .tag = .ctz,
310 .param_count = 2,
311 },
312 },
313 .{
314 "@cUndef",
315 .{
316 .tag = .c_undef,
317 .param_count = 1,
318 },
319 },
320 .{
321 "@divExact",
322 .{
323 .tag = .div_exact,
324 .param_count = 2,
325 },
326 },
327 .{
328 "@divFloor",
329 .{
330 .tag = .div_floor,
331 .param_count = 2,
332 },
333 },
334 .{
335 "@divTrunc",
336 .{
337 .tag = .div_trunc,
338 .param_count = 2,
339 },
340 },
341 .{
342 "@embedFile",
343 .{
344 .tag = .embed_file,
345 .param_count = 1,
346 },
347 },
348 .{
349 "@enumToInt",
350 .{
351 .tag = .enum_to_int,
352 .param_count = 1,
353 },
354 },
355 .{
356 "@errorName",
357 .{
358 .tag = .error_name,
359 .param_count = 1,
360 },
361 },
362 .{
363 "@errorReturnTrace",
364 .{
365 .tag = .error_return_trace,
366 .param_count = 0,
367 },
368 },
369 .{
370 "@errorToInt",
371 .{
372 .tag = .error_to_int,
373 .param_count = 1,
374 },
375 },
376 .{
377 "@errSetCast",
378 .{
379 .tag = .err_set_cast,
380 .param_count = 2,
381 },
382 },
383 .{
384 "@export",
385 .{
386 .tag = .@"export",
387 .param_count = 2,
388 },
389 },
390 .{
391 "@fence",
392 .{
393 .tag = .fence,
394 .param_count = 0,
395 },
396 },
397 .{
398 "@field",
399 .{
400 .tag = .field,
401 .needs_mem_loc = true,
402 .param_count = 2,
403 .allows_lvalue = true,
404 },
405 },
406 .{
407 "@fieldParentPtr",
408 .{
409 .tag = .field_parent_ptr,
410 .param_count = 3,
411 },
412 },
413 .{
414 "@floatCast",
415 .{
416 .tag = .float_cast,
417 .param_count = 1,
418 },
419 },
420 .{
421 "@floatToInt",
422 .{
423 .tag = .float_to_int,
424 .param_count = 1,
425 },
426 },
427 .{
428 "@frame",
429 .{
430 .tag = .frame,
431 .param_count = 0,
432 },
433 },
434 .{
435 "@Frame",
436 .{
437 .tag = .Frame,
438 .param_count = 1,
439 },
440 },
441 .{
442 "@frameAddress",
443 .{
444 .tag = .frame_address,
445 .param_count = 0,
446 },
447 },
448 .{
449 "@frameSize",
450 .{
451 .tag = .frame_size,
452 .param_count = 1,
453 },
454 },
455 .{
456 "@hasDecl",
457 .{
458 .tag = .has_decl,
459 .param_count = 2,
460 },
461 },
462 .{
463 "@hasField",
464 .{
465 .tag = .has_field,
466 .param_count = 2,
467 },
468 },
469 .{
470 "@import",
471 .{
472 .tag = .import,
473 .param_count = 1,
474 },
475 },
476 .{
477 "@intCast",
478 .{
479 .tag = .int_cast,
480 .param_count = 1,
481 },
482 },
483 .{
484 "@intToEnum",
485 .{
486 .tag = .int_to_enum,
487 .param_count = 1,
488 },
489 },
490 .{
491 "@intToError",
492 .{
493 .tag = .int_to_error,
494 .param_count = 1,
495 },
496 },
497 .{
498 "@intToFloat",
499 .{
500 .tag = .int_to_float,
501 .param_count = 1,
502 },
503 },
504 .{
505 "@intToPtr",
506 .{
507 .tag = .int_to_ptr,
508 .param_count = 2,
509 },
510 },
511 .{
512 "@memcpy",
513 .{
514 .tag = .memcpy,
515 .param_count = 3,
516 },
517 },
518 .{
519 "@memset",
520 .{
521 .tag = .memset,
522 .param_count = 3,
523 },
524 },
525 .{
526 "@wasmMemorySize",
527 .{
528 .tag = .wasm_memory_size,
529 .param_count = 1,
530 },
531 },
532 .{
533 "@wasmMemoryGrow",
534 .{
535 .tag = .wasm_memory_grow,
536 .param_count = 2,
537 },
538 },
539 .{
540 "@mod",
541 .{
542 .tag = .mod,
543 .param_count = 2,
544 },
545 },
546 .{
547 "@mulWithOverflow",
548 .{
549 .tag = .mul_with_overflow,
550 .param_count = 4,
551 },
552 },
553 .{
554 "@panic",
555 .{
556 .tag = .panic,
557 .param_count = 1,
558 },
559 },
560 .{
561 "@popCount",
562 .{
563 .tag = .pop_count,
564 .param_count = 2,
565 },
566 },
567 .{
568 "@ptrCast",
569 .{
570 .tag = .ptr_cast,
571 .param_count = 2,
572 },
573 },
574 .{
575 "@ptrToInt",
576 .{
577 .tag = .ptr_to_int,
578 .param_count = 1,
579 },
580 },
581 .{
582 "@rem",
583 .{
584 .tag = .rem,
585 .param_count = 2,
586 },
587 },
588 .{
589 "@returnAddress",
590 .{
591 .tag = .return_address,
592 .param_count = 0,
593 },
594 },
595 .{
596 "@setAlignStack",
597 .{
598 .tag = .set_align_stack,
599 .param_count = 1,
600 },
601 },
602 .{
603 "@setCold",
604 .{
605 .tag = .set_cold,
606 .param_count = 1,
607 },
608 },
609 .{
610 "@setEvalBranchQuota",
611 .{
612 .tag = .set_eval_branch_quota,
613 .param_count = 1,
614 },
615 },
616 .{
617 "@setFloatMode",
618 .{
619 .tag = .set_float_mode,
620 .param_count = 1,
621 },
622 },
623 .{
624 "@setRuntimeSafety",
625 .{
626 .tag = .set_runtime_safety,
627 .param_count = 1,
628 },
629 },
630 .{
631 "@shlExact",
632 .{
633 .tag = .shl_exact,
634 .param_count = 2,
635 },
636 },
637 .{
638 "@shlWithOverflow",
639 .{
640 .tag = .shl_with_overflow,
641 .param_count = 4,
642 },
643 },
644 .{
645 "@shrExact",
646 .{
647 .tag = .shr_exact,
648 .param_count = 2,
649 },
650 },
651 .{
652 "@shuffle",
653 .{
654 .tag = .shuffle,
655 .param_count = 4,
656 },
657 },
658 .{
659 "@sizeOf",
660 .{
661 .tag = .size_of,
662 .param_count = 1,
663 },
664 },
665 .{
666 "@splat",
667 .{
668 .tag = .splat,
669 .needs_mem_loc = true,
670 .param_count = 2,
671 },
672 },
673 .{
674 "@reduce",
675 .{
676 .tag = .reduce,
677 .param_count = 2,
678 },
679 },
680 .{
681 "@src",
682 .{
683 .tag = .src,
684 .needs_mem_loc = true,
685 .param_count = 0,
686 },
687 },
688 .{
689 "@sqrt",
690 .{
691 .tag = .sqrt,
692 .param_count = 1,
693 },
694 },
695 .{
696 "@sin",
697 .{
698 .tag = .sin,
699 .param_count = 1,
700 },
701 },
702 .{
703 "@cos",
704 .{
705 .tag = .cos,
706 .param_count = 1,
707 },
708 },
709 .{
710 "@exp",
711 .{
712 .tag = .exp,
713 .param_count = 1,
714 },
715 },
716 .{
717 "@exp2",
718 .{
719 .tag = .exp2,
720 .param_count = 1,
721 },
722 },
723 .{
724 "@log",
725 .{
726 .tag = .log,
727 .param_count = 1,
728 },
729 },
730 .{
731 "@log2",
732 .{
733 .tag = .log2,
734 .param_count = 1,
735 },
736 },
737 .{
738 "@log10",
739 .{
740 .tag = .log10,
741 .param_count = 1,
742 },
743 },
744 .{
745 "@fabs",
746 .{
747 .tag = .fabs,
748 .param_count = 1,
749 },
750 },
751 .{
752 "@floor",
753 .{
754 .tag = .floor,
755 .param_count = 1,
756 },
757 },
758 .{
759 "@ceil",
760 .{
761 .tag = .ceil,
762 .param_count = 1,
763 },
764 },
765 .{
766 "@trunc",
767 .{
768 .tag = .trunc,
769 .param_count = 1,
770 },
771 },
772 .{
773 "@round",
774 .{
775 .tag = .round,
776 .param_count = 1,
777 },
778 },
779 .{
780 "@subWithOverflow",
781 .{
782 .tag = .sub_with_overflow,
783 .param_count = 4,
784 },
785 },
786 .{
787 "@tagName",
788 .{
789 .tag = .tag_name,
790 .param_count = 1,
791 },
792 },
793 .{
794 "@This",
795 .{
796 .tag = .This,
797 .param_count = 0,
798 },
799 },
800 .{
801 "@truncate",
802 .{
803 .tag = .truncate,
804 .param_count = 2,
805 },
806 },
807 .{
808 "@Type",
809 .{
810 .tag = .Type,
811 .param_count = 1,
812 },
813 },
814 .{
815 "@typeInfo",
816 .{
817 .tag = .type_info,
818 .param_count = 1,
819 },
820 },
821 .{
822 "@typeName",
823 .{
824 .tag = .type_name,
825 .param_count = 1,
826 },
827 },
828 .{
829 "@TypeOf",
830 .{
831 .tag = .TypeOf,
832 .param_count = null,
833 },
834 },
835 .{
836 "@unionInit",
837 .{
838 .tag = .union_init,
839 .needs_mem_loc = true,
840 .param_count = 3,
841 },
842 },
843 });
844};
src/Compilation.zig+10-9
......@@ -921,7 +921,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
921921 // TODO this is duped so it can be freed in Container.deinit
922922 .sub_file_path = try gpa.dupe(u8, root_pkg.root_src_path),
923923 .source = .{ .unloaded = {} },
924 .contents = .{ .not_available = {} },
924 .tree = undefined,
925925 .status = .never_loaded,
926926 .pkg = root_pkg,
927927 .root_container = .{
......@@ -1334,7 +1334,7 @@ pub fn update(self: *Compilation) !void {
13341334 self.c_object_work_queue.writeItemAssumeCapacity(entry.key);
13351335 }
13361336
1337 const use_stage1 = build_options.is_stage1 and self.bin_file.options.use_llvm;
1337 const use_stage1 = build_options.omit_stage2 or build_options.is_stage1 and self.bin_file.options.use_llvm;
13381338 if (!use_stage1) {
13391339 if (self.bin_file.options.module) |module| {
13401340 module.compile_log_text.shrinkAndFree(module.gpa, 0);
......@@ -1884,7 +1884,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {
18841884 const c_headers_dir_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{"include"});
18851885 const c_headers_dir_path_z = try arena.dupeZ(u8, c_headers_dir_path);
18861886 var clang_errors: []translate_c.ClangErrMsg = &[0]translate_c.ClangErrMsg{};
1887 const tree = translate_c.translate(
1887 var tree = translate_c.translate(
18881888 comp.gpa,
18891889 new_argv.ptr,
18901890 new_argv.ptr + new_argv.len,
......@@ -1903,7 +1903,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {
19031903 };
19041904 },
19051905 };
1906 defer tree.deinit();
1906 defer tree.deinit(comp.gpa);
19071907
19081908 if (comp.verbose_cimport) {
19091909 log.info("C import .d file: {s}", .{out_dep_path});
......@@ -1921,9 +1921,10 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {
19211921 var out_zig_file = try o_dir.createFile(cimport_zig_basename, .{});
19221922 defer out_zig_file.close();
19231923
1924 var bos = std.io.bufferedWriter(out_zig_file.writer());
1925 _ = try std.zig.render(comp.gpa, bos.writer(), tree);
1926 try bos.flush();
1924 const formatted = try tree.render(comp.gpa);
1925 defer comp.gpa.free(formatted);
1926
1927 try out_zig_file.writeAll(formatted);
19271928
19281929 man.writeManifest() catch |err| {
19291930 log.warn("failed to write cache manifest for C import: {s}", .{@errorName(err)});
......@@ -1936,7 +1937,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {
19361937 "o", &digest, cimport_zig_basename,
19371938 });
19381939 if (comp.verbose_cimport) {
1939 log.info("C import output: {s}\n", .{out_zig_path});
1940 log.info("C import output: {s}", .{out_zig_path});
19401941 }
19411942 return CImportResult{
19421943 .out_zig_path = out_zig_path,
......@@ -3000,7 +3001,7 @@ pub fn updateSubCompilation(sub_compilation: *Compilation) !void {
30003001 for (errors.list) |full_err_msg| {
30013002 switch (full_err_msg) {
30023003 .src => |src| {
3003 log.err("{s}:{d}:{d}: {s}\n", .{
3004 log.err("{s}:{d}:{d}: {s}", .{
30043005 src.src_path,
30053006 src.line + 1,
30063007 src.column + 1,
src/Module.zig+977-637
......@@ -244,9 +244,9 @@ pub const Decl = struct {
244244 }
245245
246246 pub fn src(self: Decl) usize {
247 const tree = self.container.file_scope.contents.tree;
248 const decl_node = tree.root_node.decls()[self.src_index];
249 return tree.token_locs[decl_node.firstToken()].start;
247 const tree = &self.container.file_scope.tree;
248 const decl_node = tree.rootDecls()[self.src_index];
249 return tree.tokens.items(.start)[tree.firstToken(decl_node)];
250250 }
251251
252252 pub fn fullyQualifiedNameHash(self: Decl) Scope.NameHash {
......@@ -428,14 +428,14 @@ pub const Scope = struct {
428428 }
429429
430430 /// Asserts the scope is a child of a File and has an AST tree and returns the tree.
431 pub fn tree(self: *Scope) *ast.Tree {
431 pub fn tree(self: *Scope) *const ast.Tree {
432432 switch (self.tag) {
433 .file => return self.cast(File).?.contents.tree,
434 .block => return self.cast(Block).?.src_decl.container.file_scope.contents.tree,
435 .gen_zir => return self.cast(GenZIR).?.decl.container.file_scope.contents.tree,
436 .local_val => return self.cast(LocalVal).?.gen_zir.decl.container.file_scope.contents.tree,
437 .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.container.file_scope.contents.tree,
438 .container => return self.cast(Container).?.file_scope.contents.tree,
433 .file => return &self.cast(File).?.tree,
434 .block => return &self.cast(Block).?.src_decl.container.file_scope.tree,
435 .gen_zir => return &self.cast(GenZIR).?.decl.container.file_scope.tree,
436 .local_val => return &self.cast(LocalVal).?.gen_zir.decl.container.file_scope.tree,
437 .local_ptr => return &self.cast(LocalPtr).?.gen_zir.decl.container.file_scope.tree,
438 .container => return &self.cast(Container).?.file_scope.tree,
439439 }
440440 }
441441
......@@ -540,6 +540,12 @@ pub const Scope = struct {
540540 pub const File = struct {
541541 pub const base_tag: Tag = .file;
542542 base: Scope = Scope{ .tag = base_tag },
543 status: enum {
544 never_loaded,
545 unloaded_success,
546 unloaded_parse_failure,
547 loaded_success,
548 },
543549
544550 /// Relative to the owning package's root_src_dir.
545551 /// Reference to external memory, not owned by File.
......@@ -548,16 +554,8 @@ pub const Scope = struct {
548554 unloaded: void,
549555 bytes: [:0]const u8,
550556 },
551 contents: union {
552 not_available: void,
553 tree: *ast.Tree,
554 },
555 status: enum {
556 never_loaded,
557 unloaded_success,
558 unloaded_parse_failure,
559 loaded_success,
560 },
557 /// Whether this is populated or not depends on `status`.
558 tree: ast.Tree,
561559 /// Package that this file is a part of, managed externally.
562560 pkg: *Package,
563561
......@@ -571,7 +569,7 @@ pub const Scope = struct {
571569 => {},
572570
573571 .loaded_success => {
574 self.contents.tree.deinit();
572 self.tree.deinit(gpa);
575573 self.status = .unloaded_success;
576574 },
577575 }
......@@ -926,7 +924,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void {
926924 .complete => return,
927925
928926 .outdated => blk: {
929 log.debug("re-analyzing {s}\n", .{decl.name});
927 log.debug("re-analyzing {s}", .{decl.name});
930928
931929 // The exports this Decl performs will be re-discovered, so we remove them here
932930 // prior to re-analysis.
......@@ -950,7 +948,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void {
950948 .unreferenced => false,
951949 };
952950
953 const type_changed = mod.astGenAndAnalyzeDecl(decl) catch |err| switch (err) {
951 const type_changed = mod.astgenAndSemaDecl(decl) catch |err| switch (err) {
954952 error.OutOfMemory => return error.OutOfMemory,
955953 error.AnalysisFail => return error.AnalysisFail,
956954 else => {
......@@ -992,141 +990,72 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void {
992990 }
993991}
994992
995fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
993/// Returns `true` if the Decl type changed.
994/// Returns `true` if this is the first time analyzing the Decl.
995/// Returns `false` otherwise.
996fn astgenAndSemaDecl(mod: *Module, decl: *Decl) !bool {
996997 const tracy = trace(@src());
997998 defer tracy.end();
998999
999 const tree = try self.getAstTree(decl.container.file_scope);
1000 const ast_node = tree.root_node.decls()[decl.src_index];
1001 switch (ast_node.tag) {
1002 .FnProto => {
1003 const fn_proto = ast_node.castTag(.FnProto).?;
1000 const tree = try mod.getAstTree(decl.container.file_scope);
1001 const node_tags = tree.nodes.items(.tag);
1002 const node_datas = tree.nodes.items(.data);
1003 const decl_node = tree.rootDecls()[decl.src_index];
1004 switch (node_tags[decl_node]) {
1005 .fn_decl => {
1006 const fn_proto = node_datas[decl_node].lhs;
1007 const body = node_datas[decl_node].rhs;
1008 switch (node_tags[fn_proto]) {
1009 .fn_proto_simple => {
1010 var params: [1]ast.Node.Index = undefined;
1011 return mod.astgenAndSemaFn(decl, tree.*, body, tree.fnProtoSimple(&params, fn_proto));
1012 },
1013 .fn_proto_multi => return mod.astgenAndSemaFn(decl, tree.*, body, tree.fnProtoMulti(fn_proto)),
1014 .fn_proto_one => {
1015 var params: [1]ast.Node.Index = undefined;
1016 return mod.astgenAndSemaFn(decl, tree.*, body, tree.fnProtoOne(&params, fn_proto));
1017 },
1018 .fn_proto => return mod.astgenAndSemaFn(decl, tree.*, body, tree.fnProto(fn_proto)),
1019 else => unreachable,
1020 }
1021 },
1022 .fn_proto_simple => {
1023 var params: [1]ast.Node.Index = undefined;
1024 return mod.astgenAndSemaFn(decl, tree.*, 0, tree.fnProtoSimple(&params, decl_node));
1025 },
1026 .fn_proto_multi => return mod.astgenAndSemaFn(decl, tree.*, 0, tree.fnProtoMulti(decl_node)),
1027 .fn_proto_one => {
1028 var params: [1]ast.Node.Index = undefined;
1029 return mod.astgenAndSemaFn(decl, tree.*, 0, tree.fnProtoOne(&params, decl_node));
1030 },
1031 .fn_proto => return mod.astgenAndSemaFn(decl, tree.*, 0, tree.fnProto(decl_node)),
10041032
1033 .global_var_decl => return mod.astgenAndSemaVarDecl(decl, tree.*, tree.globalVarDecl(decl_node)),
1034 .local_var_decl => return mod.astgenAndSemaVarDecl(decl, tree.*, tree.localVarDecl(decl_node)),
1035 .simple_var_decl => return mod.astgenAndSemaVarDecl(decl, tree.*, tree.simpleVarDecl(decl_node)),
1036 .aligned_var_decl => return mod.astgenAndSemaVarDecl(decl, tree.*, tree.alignedVarDecl(decl_node)),
1037
1038 .@"comptime" => {
10051039 decl.analysis = .in_progress;
10061040
1007 // This arena allocator's memory is discarded at the end of this function. It is used
1008 // to determine the type of the function, and hence the type of the decl, which is needed
1009 // to complete the Decl analysis.
1010 var fn_type_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
1011 defer fn_type_scope_arena.deinit();
1012 var fn_type_scope: Scope.GenZIR = .{
1041 // A comptime decl does not store any value so we can just deinit this arena after analysis is done.
1042 var analysis_arena = std.heap.ArenaAllocator.init(mod.gpa);
1043 defer analysis_arena.deinit();
1044 var gen_scope: Scope.GenZIR = .{
10131045 .decl = decl,
1014 .arena = &fn_type_scope_arena.allocator,
1046 .arena = &analysis_arena.allocator,
10151047 .parent = &decl.container.base,
10161048 .force_comptime = true,
10171049 };
1018 defer fn_type_scope.instructions.deinit(self.gpa);
1019
1020 decl.is_pub = fn_proto.getVisibToken() != null;
1021
1022 const param_decls = fn_proto.params();
1023 const param_types = try fn_type_scope.arena.alloc(*zir.Inst, param_decls.len);
1050 defer gen_scope.instructions.deinit(mod.gpa);
10241051
1025 const fn_src = tree.token_locs[fn_proto.fn_token].start;
1026 const type_type = try astgen.addZIRInstConst(self, &fn_type_scope.base, fn_src, .{
1027 .ty = Type.initTag(.type),
1028 .val = Value.initTag(.type_type),
1029 });
1030 const type_type_rl: astgen.ResultLoc = .{ .ty = type_type };
1031 for (param_decls) |param_decl, i| {
1032 const param_type_node = switch (param_decl.param_type) {
1033 .any_type => |node| return self.failNode(&fn_type_scope.base, node, "TODO implement anytype parameter", .{}),
1034 .type_expr => |node| node,
1035 };
1036 param_types[i] = try astgen.expr(self, &fn_type_scope.base, type_type_rl, param_type_node);
1037 }
1038 if (fn_proto.getVarArgsToken()) |var_args_token| {
1039 return self.failTok(&fn_type_scope.base, var_args_token, "TODO implement var args", .{});
1040 }
1041 if (fn_proto.getLibName()) |lib_name| blk: {
1042 const lib_name_str = mem.trim(u8, tree.tokenSlice(lib_name.firstToken()), "\""); // TODO: call identifierTokenString
1043 log.debug("extern fn symbol expected in lib '{s}'", .{lib_name_str});
1044 const target = self.comp.getTarget();
1045 if (target_util.is_libc_lib_name(target, lib_name_str)) {
1046 if (!self.comp.bin_file.options.link_libc) {
1047 return self.failNode(
1048 &fn_type_scope.base,
1049 lib_name,
1050 "dependency on libc must be explicitly specified in the build command",
1051 .{},
1052 );
1053 }
1054 break :blk;
1055 }
1056 if (target_util.is_libcpp_lib_name(target, lib_name_str)) {
1057 if (!self.comp.bin_file.options.link_libcpp) {
1058 return self.failNode(
1059 &fn_type_scope.base,
1060 lib_name,
1061 "dependency on libc++ must be explicitly specified in the build command",
1062 .{},
1063 );
1064 }
1065 break :blk;
1066 }
1067 if (!target.isWasm() and !self.comp.bin_file.options.pic) {
1068 return self.failNode(
1069 &fn_type_scope.base,
1070 lib_name,
1071 "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by `-l{s}` or `-fPIC`.",
1072 .{ lib_name, lib_name },
1073 );
1074 }
1075 self.comp.stage1AddLinkLib(lib_name_str) catch |err| {
1076 return self.failNode(
1077 &fn_type_scope.base,
1078 lib_name,
1079 "unable to add link lib '{s}': {s}",
1080 .{ lib_name, @errorName(err) },
1081 );
1082 };
1083 }
1084 if (fn_proto.getAlignExpr()) |align_expr| {
1085 return self.failNode(&fn_type_scope.base, align_expr, "TODO implement function align expression", .{});
1086 }
1087 if (fn_proto.getSectionExpr()) |sect_expr| {
1088 return self.failNode(&fn_type_scope.base, sect_expr, "TODO implement function section expression", .{});
1052 const block_expr = node_datas[decl_node].lhs;
1053 _ = try astgen.comptimeExpr(mod, &gen_scope.base, .none, block_expr);
1054 if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
1055 zir.dumpZir(mod.gpa, "comptime_block", decl.name, gen_scope.instructions.items) catch {};
10891056 }
10901057
1091 const enum_literal_type = try astgen.addZIRInstConst(self, &fn_type_scope.base, fn_src, .{
1092 .ty = Type.initTag(.type),
1093 .val = Value.initTag(.enum_literal_type),
1094 });
1095 const enum_literal_type_rl: astgen.ResultLoc = .{ .ty = enum_literal_type };
1096 const cc = if (fn_proto.getCallconvExpr()) |callconv_expr|
1097 try astgen.expr(self, &fn_type_scope.base, enum_literal_type_rl, callconv_expr)
1098 else
1099 try astgen.addZIRInstConst(self, &fn_type_scope.base, fn_src, .{
1100 .ty = Type.initTag(.enum_literal),
1101 .val = try Value.Tag.enum_literal.create(
1102 &fn_type_scope_arena.allocator,
1103 try fn_type_scope_arena.allocator.dupe(u8, "Unspecified"),
1104 ),
1105 });
1106
1107 const return_type_expr = switch (fn_proto.return_type) {
1108 .Explicit => |node| node,
1109 .InferErrorSet => |node| return self.failNode(&fn_type_scope.base, node, "TODO implement inferred error sets", .{}),
1110 .Invalid => |tok| return self.failTok(&fn_type_scope.base, tok, "unable to parse return type", .{}),
1111 };
1112
1113 const return_type_inst = try astgen.expr(self, &fn_type_scope.base, type_type_rl, return_type_expr);
1114 const fn_type_inst = try astgen.addZIRInst(self, &fn_type_scope.base, fn_src, zir.Inst.FnType, .{
1115 .return_type = return_type_inst,
1116 .param_types = param_types,
1117 .cc = cc,
1118 }, .{});
1119
1120 if (std.builtin.mode == .Debug and self.comp.verbose_ir) {
1121 zir.dumpZir(self.gpa, "fn_type", decl.name, fn_type_scope.instructions.items) catch {};
1122 }
1123
1124 // We need the memory for the Type to go into the arena for the Decl
1125 var decl_arena = std.heap.ArenaAllocator.init(self.gpa);
1126 errdefer decl_arena.deinit();
1127 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
1128
1129 var inst_table = Scope.Block.InstTable.init(self.gpa);
1058 var inst_table = Scope.Block.InstTable.init(mod.gpa);
11301059 defer inst_table.deinit();
11311060
11321061 var branch_quota: u32 = default_eval_branch_quota;
......@@ -1138,424 +1067,627 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
11381067 .owner_decl = decl,
11391068 .src_decl = decl,
11401069 .instructions = .{},
1141 .arena = &decl_arena.allocator,
1070 .arena = &analysis_arena.allocator,
11421071 .inlining = null,
1143 .is_comptime = false,
1072 .is_comptime = true,
11441073 .branch_quota = &branch_quota,
11451074 };
1146 defer block_scope.instructions.deinit(self.gpa);
1075 defer block_scope.instructions.deinit(mod.gpa);
11471076
1148 const fn_type = try zir_sema.analyzeBodyValueAsType(self, &block_scope, fn_type_inst, .{
1149 .instructions = fn_type_scope.instructions.items,
1077 _ = try zir_sema.analyzeBody(mod, &block_scope, .{
1078 .instructions = gen_scope.instructions.items,
11501079 });
1151 const body_node = fn_proto.getBodyNode() orelse {
1152 // Extern function.
1153 var type_changed = true;
1154 if (decl.typedValueManaged()) |tvm| {
1155 type_changed = !tvm.typed_value.ty.eql(fn_type);
11561080
1157 tvm.deinit(self.gpa);
1158 }
1159 const fn_val = try Value.Tag.extern_fn.create(&decl_arena.allocator, decl);
1081 decl.analysis = .complete;
1082 decl.generation = mod.generation;
1083 return true;
1084 },
1085 .@"usingnamespace" => @panic("TODO usingnamespace decl"),
1086 else => unreachable,
1087 }
1088}
11601089
1161 decl_arena_state.* = decl_arena.state;
1162 decl.typed_value = .{
1163 .most_recent = .{
1164 .typed_value = .{ .ty = fn_type, .val = fn_val },
1165 .arena = decl_arena_state,
1166 },
1167 };
1168 decl.analysis = .complete;
1169 decl.generation = self.generation;
1090fn astgenAndSemaFn(
1091 mod: *Module,
1092 decl: *Decl,
1093 tree: ast.Tree,
1094 body_node: ast.Node.Index,
1095 fn_proto: ast.full.FnProto,
1096) !bool {
1097 const tracy = trace(@src());
1098 defer tracy.end();
1099
1100 decl.analysis = .in_progress;
1101
1102 const token_starts = tree.tokens.items(.start);
1103 const token_tags = tree.tokens.items(.tag);
1104
1105 // This arena allocator's memory is discarded at the end of this function. It is used
1106 // to determine the type of the function, and hence the type of the decl, which is needed
1107 // to complete the Decl analysis.
1108 var fn_type_scope_arena = std.heap.ArenaAllocator.init(mod.gpa);
1109 defer fn_type_scope_arena.deinit();
1110 var fn_type_scope: Scope.GenZIR = .{
1111 .decl = decl,
1112 .arena = &fn_type_scope_arena.allocator,
1113 .parent = &decl.container.base,
1114 .force_comptime = true,
1115 };
1116 defer fn_type_scope.instructions.deinit(mod.gpa);
11701117
1171 try self.comp.bin_file.allocateDeclIndexes(decl);
1172 try self.comp.work_queue.writeItem(.{ .codegen_decl = decl });
1118 decl.is_pub = fn_proto.visib_token != null;
11731119
1174 if (type_changed and self.emit_h != null) {
1175 try self.comp.work_queue.writeItem(.{ .emit_h_decl = decl });
1120 // The AST params array does not contain anytype and ... parameters.
1121 // We must iterate to count how many param types to allocate.
1122 const param_count = blk: {
1123 var count: usize = 0;
1124 var it = fn_proto.iterate(tree);
1125 while (it.next()) |_| {
1126 count += 1;
1127 }
1128 break :blk count;
1129 };
1130 const param_types = try fn_type_scope.arena.alloc(*zir.Inst, param_count);
1131 const fn_src = token_starts[fn_proto.ast.fn_token];
1132 const type_type = try astgen.addZIRInstConst(mod, &fn_type_scope.base, fn_src, .{
1133 .ty = Type.initTag(.type),
1134 .val = Value.initTag(.type_type),
1135 });
1136 const type_type_rl: astgen.ResultLoc = .{ .ty = type_type };
1137
1138 {
1139 var param_type_i: usize = 0;
1140 var it = fn_proto.iterate(tree);
1141 while (it.next()) |param| : (param_type_i += 1) {
1142 if (param.anytype_ellipsis3) |token| {
1143 switch (token_tags[token]) {
1144 .keyword_anytype => return mod.failTok(
1145 &fn_type_scope.base,
1146 token,
1147 "TODO implement anytype parameter",
1148 .{},
1149 ),
1150 .ellipsis3 => return mod.failTok(
1151 &fn_type_scope.base,
1152 token,
1153 "TODO implement var args",
1154 .{},
1155 ),
1156 else => unreachable,
11761157 }
1158 }
1159 const param_type_node = param.type_expr;
1160 assert(param_type_node != 0);
1161 param_types[param_type_i] =
1162 try astgen.expr(mod, &fn_type_scope.base, type_type_rl, param_type_node);
1163 }
1164 assert(param_type_i == param_count);
1165 }
1166 if (fn_proto.lib_name) |lib_name_token| blk: {
1167 // TODO call std.zig.parseStringLiteral
1168 const lib_name_str = mem.trim(u8, tree.tokenSlice(lib_name_token), "\"");
1169 log.debug("extern fn symbol expected in lib '{s}'", .{lib_name_str});
1170 const target = mod.comp.getTarget();
1171 if (target_util.is_libc_lib_name(target, lib_name_str)) {
1172 if (!mod.comp.bin_file.options.link_libc) {
1173 return mod.failTok(
1174 &fn_type_scope.base,
1175 lib_name_token,
1176 "dependency on libc must be explicitly specified in the build command",
1177 .{},
1178 );
1179 }
1180 break :blk;
1181 }
1182 if (target_util.is_libcpp_lib_name(target, lib_name_str)) {
1183 if (!mod.comp.bin_file.options.link_libcpp) {
1184 return mod.failTok(
1185 &fn_type_scope.base,
1186 lib_name_token,
1187 "dependency on libc++ must be explicitly specified in the build command",
1188 .{},
1189 );
1190 }
1191 break :blk;
1192 }
1193 if (!target.isWasm() and !mod.comp.bin_file.options.pic) {
1194 return mod.failTok(
1195 &fn_type_scope.base,
1196 lib_name_token,
1197 "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by `-l{s}` or `-fPIC`.",
1198 .{ lib_name_str, lib_name_str },
1199 );
1200 }
1201 mod.comp.stage1AddLinkLib(lib_name_str) catch |err| {
1202 return mod.failTok(
1203 &fn_type_scope.base,
1204 lib_name_token,
1205 "unable to add link lib '{s}': {s}",
1206 .{ lib_name_str, @errorName(err) },
1207 );
1208 };
1209 }
1210 if (fn_proto.ast.align_expr != 0) {
1211 return mod.failNode(
1212 &fn_type_scope.base,
1213 fn_proto.ast.align_expr,
1214 "TODO implement function align expression",
1215 .{},
1216 );
1217 }
1218 if (fn_proto.ast.section_expr != 0) {
1219 return mod.failNode(
1220 &fn_type_scope.base,
1221 fn_proto.ast.section_expr,
1222 "TODO implement function section expression",
1223 .{},
1224 );
1225 }
11771226
1178 return type_changed;
1179 };
1227 const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1;
1228 if (token_tags[maybe_bang] == .bang) {
1229 return mod.failTok(&fn_type_scope.base, maybe_bang, "TODO implement inferred error sets", .{});
1230 }
1231 const return_type_inst = try astgen.expr(
1232 mod,
1233 &fn_type_scope.base,
1234 type_type_rl,
1235 fn_proto.ast.return_type,
1236 );
1237 const fn_type_inst = if (fn_proto.ast.callconv_expr != 0) cc: {
1238 // TODO instead of enum literal type, this needs to be the
1239 // std.builtin.CallingConvention enum. We need to implement importing other files
1240 // and enums in order to fix this.
1241 const src = token_starts[tree.firstToken(fn_proto.ast.callconv_expr)];
1242 const enum_lit_ty = try astgen.addZIRInstConst(mod, &fn_type_scope.base, src, .{
1243 .ty = Type.initTag(.type),
1244 .val = Value.initTag(.enum_literal_type),
1245 });
1246 const cc = try astgen.comptimeExpr(mod, &fn_type_scope.base, .{
1247 .ty = enum_lit_ty,
1248 }, fn_proto.ast.callconv_expr);
1249 break :cc try astgen.addZirInstTag(mod, &fn_type_scope.base, fn_src, .fn_type_cc, .{
1250 .return_type = return_type_inst,
1251 .param_types = param_types,
1252 .cc = cc,
1253 });
1254 } else
1255 try astgen.addZirInstTag(mod, &fn_type_scope.base, fn_src, .fn_type, .{
1256 .return_type = return_type_inst,
1257 .param_types = param_types,
1258 });
11801259
1181 const new_func = try decl_arena.allocator.create(Fn);
1182 const fn_payload = try decl_arena.allocator.create(Value.Payload.Function);
1260 if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
1261 zir.dumpZir(mod.gpa, "fn_type", decl.name, fn_type_scope.instructions.items) catch {};
1262 }
11831263
1184 const fn_zir: zir.Body = blk: {
1185 // We put the ZIR inside the Decl arena.
1186 var gen_scope: Scope.GenZIR = .{
1187 .decl = decl,
1188 .arena = &decl_arena.allocator,
1189 .parent = &decl.container.base,
1190 .force_comptime = false,
1191 };
1192 defer gen_scope.instructions.deinit(self.gpa);
1193
1194 // We need an instruction for each parameter, and they must be first in the body.
1195 try gen_scope.instructions.resize(self.gpa, fn_proto.params_len);
1196 var params_scope = &gen_scope.base;
1197 for (fn_proto.params()) |param, i| {
1198 const name_token = param.name_token.?;
1199 const src = tree.token_locs[name_token].start;
1200 const param_name = try self.identifierTokenString(&gen_scope.base, name_token);
1201 const arg = try decl_arena.allocator.create(zir.Inst.Arg);
1202 arg.* = .{
1203 .base = .{
1204 .tag = .arg,
1205 .src = src,
1206 },
1207 .positionals = .{
1208 .name = param_name,
1209 },
1210 .kw_args = .{},
1211 };
1212 gen_scope.instructions.items[i] = &arg.base;
1213 const sub_scope = try decl_arena.allocator.create(Scope.LocalVal);
1214 sub_scope.* = .{
1215 .parent = params_scope,
1216 .gen_zir = &gen_scope,
1217 .name = param_name,
1218 .inst = &arg.base,
1219 };
1220 params_scope = &sub_scope.base;
1221 }
1264 // We need the memory for the Type to go into the arena for the Decl
1265 var decl_arena = std.heap.ArenaAllocator.init(mod.gpa);
1266 errdefer decl_arena.deinit();
1267 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
12221268
1223 const body_block = body_node.cast(ast.Node.Block).?;
1269 var inst_table = Scope.Block.InstTable.init(mod.gpa);
1270 defer inst_table.deinit();
12241271
1225 try astgen.blockExpr(self, params_scope, body_block);
1272 var branch_quota: u32 = default_eval_branch_quota;
12261273
1227 if (gen_scope.instructions.items.len == 0 or
1228 !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn())
1229 {
1230 const src = tree.token_locs[body_block.rbrace].start;
1231 _ = try astgen.addZIRNoOp(self, &gen_scope.base, src, .return_void);
1232 }
1274 var block_scope: Scope.Block = .{
1275 .parent = null,
1276 .inst_table = &inst_table,
1277 .func = null,
1278 .owner_decl = decl,
1279 .src_decl = decl,
1280 .instructions = .{},
1281 .arena = &decl_arena.allocator,
1282 .inlining = null,
1283 .is_comptime = false,
1284 .branch_quota = &branch_quota,
1285 };
1286 defer block_scope.instructions.deinit(mod.gpa);
12331287
1234 if (std.builtin.mode == .Debug and self.comp.verbose_ir) {
1235 zir.dumpZir(self.gpa, "fn_body", decl.name, gen_scope.instructions.items) catch {};
1236 }
1288 const fn_type = try zir_sema.analyzeBodyValueAsType(mod, &block_scope, fn_type_inst, .{
1289 .instructions = fn_type_scope.instructions.items,
1290 });
1291 if (body_node == 0) {
1292 // Extern function.
1293 var type_changed = true;
1294 if (decl.typedValueManaged()) |tvm| {
1295 type_changed = !tvm.typed_value.ty.eql(fn_type);
12371296
1238 break :blk .{
1239 .instructions = try gen_scope.arena.dupe(*zir.Inst, gen_scope.instructions.items),
1240 };
1241 };
1297 tvm.deinit(mod.gpa);
1298 }
1299 const fn_val = try Value.Tag.extern_fn.create(&decl_arena.allocator, decl);
12421300
1243 const is_inline = fn_type.fnCallingConvention() == .Inline;
1244 const anal_state = ([2]Fn.Analysis{ .queued, .inline_only })[@boolToInt(is_inline)];
1301 decl_arena_state.* = decl_arena.state;
1302 decl.typed_value = .{
1303 .most_recent = .{
1304 .typed_value = .{ .ty = fn_type, .val = fn_val },
1305 .arena = decl_arena_state,
1306 },
1307 };
1308 decl.analysis = .complete;
1309 decl.generation = mod.generation;
12451310
1246 new_func.* = .{
1247 .state = anal_state,
1248 .zir = fn_zir,
1249 .body = undefined,
1250 .owner_decl = decl,
1251 };
1252 fn_payload.* = .{
1253 .base = .{ .tag = .function },
1254 .data = new_func,
1255 };
1311 try mod.comp.bin_file.allocateDeclIndexes(decl);
1312 try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl });
12561313
1257 var prev_type_has_bits = false;
1258 var prev_is_inline = false;
1259 var type_changed = true;
1314 if (type_changed and mod.emit_h != null) {
1315 try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl });
1316 }
12601317
1261 if (decl.typedValueManaged()) |tvm| {
1262 prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits();
1263 type_changed = !tvm.typed_value.ty.eql(fn_type);
1264 if (tvm.typed_value.val.castTag(.function)) |payload| {
1265 const prev_func = payload.data;
1266 prev_is_inline = prev_func.state == .inline_only;
1267 }
1318 return type_changed;
1319 }
12681320
1269 tvm.deinit(self.gpa);
1270 }
1321 const new_func = try decl_arena.allocator.create(Fn);
1322 const fn_payload = try decl_arena.allocator.create(Value.Payload.Function);
12711323
1272 decl_arena_state.* = decl_arena.state;
1273 decl.typed_value = .{
1274 .most_recent = .{
1275 .typed_value = .{
1276 .ty = fn_type,
1277 .val = Value.initPayload(&fn_payload.base),
1278 },
1279 .arena = decl_arena_state,
1324 const fn_zir: zir.Body = blk: {
1325 // We put the ZIR inside the Decl arena.
1326 var gen_scope: Scope.GenZIR = .{
1327 .decl = decl,
1328 .arena = &decl_arena.allocator,
1329 .parent = &decl.container.base,
1330 .force_comptime = false,
1331 };
1332 defer gen_scope.instructions.deinit(mod.gpa);
1333
1334 // We need an instruction for each parameter, and they must be first in the body.
1335 try gen_scope.instructions.resize(mod.gpa, param_count);
1336 var params_scope = &gen_scope.base;
1337 var i: usize = 0;
1338 var it = fn_proto.iterate(tree);
1339 while (it.next()) |param| : (i += 1) {
1340 const name_token = param.name_token.?;
1341 const src = token_starts[name_token];
1342 const param_name = try mod.identifierTokenString(&gen_scope.base, name_token);
1343 const arg = try decl_arena.allocator.create(zir.Inst.Arg);
1344 arg.* = .{
1345 .base = .{
1346 .tag = .arg,
1347 .src = src,
1348 },
1349 .positionals = .{
1350 .name = param_name,
12801351 },
1352 .kw_args = .{},
12811353 };
1282 decl.analysis = .complete;
1283 decl.generation = self.generation;
1284
1285 if (!is_inline and fn_type.hasCodeGenBits()) {
1286 // We don't fully codegen the decl until later, but we do need to reserve a global
1287 // offset table index for it. This allows us to codegen decls out of dependency order,
1288 // increasing how many computations can be done in parallel.
1289 try self.comp.bin_file.allocateDeclIndexes(decl);
1290 try self.comp.work_queue.writeItem(.{ .codegen_decl = decl });
1291 if (type_changed and self.emit_h != null) {
1292 try self.comp.work_queue.writeItem(.{ .emit_h_decl = decl });
1293 }
1294 } else if (!prev_is_inline and prev_type_has_bits) {
1295 self.comp.bin_file.freeDecl(decl);
1296 }
1354 gen_scope.instructions.items[i] = &arg.base;
1355 const sub_scope = try decl_arena.allocator.create(Scope.LocalVal);
1356 sub_scope.* = .{
1357 .parent = params_scope,
1358 .gen_zir = &gen_scope,
1359 .name = param_name,
1360 .inst = &arg.base,
1361 };
1362 params_scope = &sub_scope.base;
1363 }
12971364
1298 if (fn_proto.getExternExportInlineToken()) |maybe_export_token| {
1299 if (tree.token_ids[maybe_export_token] == .Keyword_export) {
1300 if (is_inline) {
1301 return self.failTok(
1302 &block_scope.base,
1303 maybe_export_token,
1304 "export of inline function",
1305 .{},
1306 );
1307 }
1308 const export_src = tree.token_locs[maybe_export_token].start;
1309 const name_loc = tree.token_locs[fn_proto.getNameToken().?];
1310 const name = tree.tokenSliceLoc(name_loc);
1311 // The scope needs to have the decl in it.
1312 try self.analyzeExport(&block_scope.base, export_src, name, decl);
1313 }
1314 }
1315 return type_changed or is_inline != prev_is_inline;
1316 },
1317 .VarDecl => {
1318 const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", ast_node);
1365 _ = try astgen.expr(mod, params_scope, .none, body_node);
13191366
1320 decl.analysis = .in_progress;
1367 if (gen_scope.instructions.items.len == 0 or
1368 !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn())
1369 {
1370 const src = token_starts[tree.lastToken(body_node)];
1371 _ = try astgen.addZIRNoOp(mod, &gen_scope.base, src, .return_void);
1372 }
13211373
1322 // We need the memory for the Type to go into the arena for the Decl
1323 var decl_arena = std.heap.ArenaAllocator.init(self.gpa);
1324 errdefer decl_arena.deinit();
1325 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
1374 if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
1375 zir.dumpZir(mod.gpa, "fn_body", decl.name, gen_scope.instructions.items) catch {};
1376 }
13261377
1327 var decl_inst_table = Scope.Block.InstTable.init(self.gpa);
1328 defer decl_inst_table.deinit();
1378 break :blk .{
1379 .instructions = try gen_scope.arena.dupe(*zir.Inst, gen_scope.instructions.items),
1380 };
1381 };
13291382
1330 var branch_quota: u32 = default_eval_branch_quota;
1383 const is_inline = fn_type.fnCallingConvention() == .Inline;
1384 const anal_state: Fn.Analysis = if (is_inline) .inline_only else .queued;
13311385
1332 var block_scope: Scope.Block = .{
1333 .parent = null,
1334 .inst_table = &decl_inst_table,
1335 .func = null,
1336 .owner_decl = decl,
1337 .src_decl = decl,
1338 .instructions = .{},
1339 .arena = &decl_arena.allocator,
1340 .inlining = null,
1341 .is_comptime = true,
1342 .branch_quota = &branch_quota,
1343 };
1344 defer block_scope.instructions.deinit(self.gpa);
1345
1346 decl.is_pub = var_decl.getVisibToken() != null;
1347 const is_extern = blk: {
1348 const maybe_extern_token = var_decl.getExternExportToken() orelse
1349 break :blk false;
1350 if (tree.token_ids[maybe_extern_token] != .Keyword_extern) break :blk false;
1351 if (var_decl.getInitNode()) |some| {
1352 return self.failNode(&block_scope.base, some, "extern variables have no initializers", .{});
1353 }
1354 break :blk true;
1355 };
1356 if (var_decl.getLibName()) |lib_name| {
1357 assert(is_extern);
1358 return self.failNode(&block_scope.base, lib_name, "TODO implement function library name", .{});
1359 }
1360 const is_mutable = tree.token_ids[var_decl.mut_token] == .Keyword_var;
1361 const is_threadlocal = if (var_decl.getThreadLocalToken()) |some| blk: {
1362 if (!is_mutable) {
1363 return self.failTok(&block_scope.base, some, "threadlocal variable cannot be constant", .{});
1364 }
1365 break :blk true;
1366 } else false;
1367 assert(var_decl.getComptimeToken() == null);
1368 if (var_decl.getAlignNode()) |align_expr| {
1369 return self.failNode(&block_scope.base, align_expr, "TODO implement function align expression", .{});
1370 }
1371 if (var_decl.getSectionNode()) |sect_expr| {
1372 return self.failNode(&block_scope.base, sect_expr, "TODO implement function section expression", .{});
1373 }
1386 new_func.* = .{
1387 .state = anal_state,
1388 .zir = fn_zir,
1389 .body = undefined,
1390 .owner_decl = decl,
1391 };
1392 fn_payload.* = .{
1393 .base = .{ .tag = .function },
1394 .data = new_func,
1395 };
13741396
1375 const var_info: struct { ty: Type, val: ?Value } = if (var_decl.getInitNode()) |init_node| vi: {
1376 var gen_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
1377 defer gen_scope_arena.deinit();
1378 var gen_scope: Scope.GenZIR = .{
1379 .decl = decl,
1380 .arena = &gen_scope_arena.allocator,
1381 .parent = &decl.container.base,
1382 .force_comptime = false,
1383 };
1384 defer gen_scope.instructions.deinit(self.gpa);
1397 var prev_type_has_bits = false;
1398 var prev_is_inline = false;
1399 var type_changed = true;
13851400
1386 const init_result_loc: astgen.ResultLoc = if (var_decl.getTypeNode()) |type_node| rl: {
1387 const src = tree.token_locs[type_node.firstToken()].start;
1388 const type_type = try astgen.addZIRInstConst(self, &gen_scope.base, src, .{
1389 .ty = Type.initTag(.type),
1390 .val = Value.initTag(.type_type),
1391 });
1392 const var_type = try astgen.expr(self, &gen_scope.base, .{ .ty = type_type }, type_node);
1393 break :rl .{ .ty = var_type };
1394 } else .none;
1401 if (decl.typedValueManaged()) |tvm| {
1402 prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits();
1403 type_changed = !tvm.typed_value.ty.eql(fn_type);
1404 if (tvm.typed_value.val.castTag(.function)) |payload| {
1405 const prev_func = payload.data;
1406 prev_is_inline = prev_func.state == .inline_only;
1407 }
13951408
1396 const init_inst = try astgen.comptimeExpr(self, &gen_scope.base, init_result_loc, init_node);
1397 if (std.builtin.mode == .Debug and self.comp.verbose_ir) {
1398 zir.dumpZir(self.gpa, "var_init", decl.name, gen_scope.instructions.items) catch {};
1399 }
1409 tvm.deinit(mod.gpa);
1410 }
14001411
1401 var var_inst_table = Scope.Block.InstTable.init(self.gpa);
1402 defer var_inst_table.deinit();
1403
1404 var branch_quota_vi: u32 = default_eval_branch_quota;
1405 var inner_block: Scope.Block = .{
1406 .parent = null,
1407 .inst_table = &var_inst_table,
1408 .func = null,
1409 .owner_decl = decl,
1410 .src_decl = decl,
1411 .instructions = .{},
1412 .arena = &gen_scope_arena.allocator,
1413 .inlining = null,
1414 .is_comptime = true,
1415 .branch_quota = &branch_quota_vi,
1416 };
1417 defer inner_block.instructions.deinit(self.gpa);
1418 try zir_sema.analyzeBody(self, &inner_block, .{
1419 .instructions = gen_scope.instructions.items,
1420 });
1412 decl_arena_state.* = decl_arena.state;
1413 decl.typed_value = .{
1414 .most_recent = .{
1415 .typed_value = .{
1416 .ty = fn_type,
1417 .val = Value.initPayload(&fn_payload.base),
1418 },
1419 .arena = decl_arena_state,
1420 },
1421 };
1422 decl.analysis = .complete;
1423 decl.generation = mod.generation;
1424
1425 if (!is_inline and fn_type.hasCodeGenBits()) {
1426 // We don't fully codegen the decl until later, but we do need to reserve a global
1427 // offset table index for it. This allows us to codegen decls out of dependency order,
1428 // increasing how many computations can be done in parallel.
1429 try mod.comp.bin_file.allocateDeclIndexes(decl);
1430 try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl });
1431 if (type_changed and mod.emit_h != null) {
1432 try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl });
1433 }
1434 } else if (!prev_is_inline and prev_type_has_bits) {
1435 mod.comp.bin_file.freeDecl(decl);
1436 }
1437
1438 if (fn_proto.extern_export_token) |maybe_export_token| {
1439 if (token_tags[maybe_export_token] == .keyword_export) {
1440 if (is_inline) {
1441 return mod.failTok(
1442 &block_scope.base,
1443 maybe_export_token,
1444 "export of inline function",
1445 .{},
1446 );
1447 }
1448 const export_src = token_starts[maybe_export_token];
1449 const name = tree.tokenSlice(fn_proto.name_token.?); // TODO identifierTokenString
1450 // The scope needs to have the decl in it.
1451 try mod.analyzeExport(&block_scope.base, export_src, name, decl);
1452 }
1453 }
1454 return type_changed or is_inline != prev_is_inline;
1455}
14211456
1422 // The result location guarantees the type coercion.
1423 const analyzed_init_inst = var_inst_table.get(init_inst).?;
1424 // The is_comptime in the Scope.Block guarantees the result is comptime-known.
1425 const val = analyzed_init_inst.value().?;
1457fn astgenAndSemaVarDecl(
1458 mod: *Module,
1459 decl: *Decl,
1460 tree: ast.Tree,
1461 var_decl: ast.full.VarDecl,
1462) !bool {
1463 const tracy = trace(@src());
1464 defer tracy.end();
14261465
1427 const ty = try analyzed_init_inst.ty.copy(block_scope.arena);
1428 break :vi .{
1429 .ty = ty,
1430 .val = try val.copy(block_scope.arena),
1431 };
1432 } else if (!is_extern) {
1433 return self.failTok(&block_scope.base, var_decl.firstToken(), "variables must be initialized", .{});
1434 } else if (var_decl.getTypeNode()) |type_node| vi: {
1435 // Temporary arena for the zir instructions.
1436 var type_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
1437 defer type_scope_arena.deinit();
1438 var type_scope: Scope.GenZIR = .{
1439 .decl = decl,
1440 .arena = &type_scope_arena.allocator,
1441 .parent = &decl.container.base,
1442 .force_comptime = true,
1443 };
1444 defer type_scope.instructions.deinit(self.gpa);
1466 decl.analysis = .in_progress;
14451467
1446 const var_type = try astgen.typeExpr(self, &type_scope.base, type_node);
1447 if (std.builtin.mode == .Debug and self.comp.verbose_ir) {
1448 zir.dumpZir(self.gpa, "var_type", decl.name, type_scope.instructions.items) catch {};
1449 }
1468 const token_starts = tree.tokens.items(.start);
1469 const token_tags = tree.tokens.items(.tag);
14501470
1451 const ty = try zir_sema.analyzeBodyValueAsType(self, &block_scope, var_type, .{
1452 .instructions = type_scope.instructions.items,
1453 });
1454 break :vi .{
1455 .ty = ty,
1456 .val = null,
1457 };
1458 } else {
1459 return self.failTok(&block_scope.base, var_decl.firstToken(), "unable to infer variable type", .{});
1460 };
1471 // We need the memory for the Type to go into the arena for the Decl
1472 var decl_arena = std.heap.ArenaAllocator.init(mod.gpa);
1473 errdefer decl_arena.deinit();
1474 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
14611475
1462 if (is_mutable and !var_info.ty.isValidVarType(is_extern)) {
1463 return self.failTok(&block_scope.base, var_decl.firstToken(), "variable of type '{}' must be const", .{var_info.ty});
1464 }
1476 var decl_inst_table = Scope.Block.InstTable.init(mod.gpa);
1477 defer decl_inst_table.deinit();
14651478
1466 var type_changed = true;
1467 if (decl.typedValueManaged()) |tvm| {
1468 type_changed = !tvm.typed_value.ty.eql(var_info.ty);
1479 var branch_quota: u32 = default_eval_branch_quota;
14691480
1470 tvm.deinit(self.gpa);
1471 }
1481 var block_scope: Scope.Block = .{
1482 .parent = null,
1483 .inst_table = &decl_inst_table,
1484 .func = null,
1485 .owner_decl = decl,
1486 .src_decl = decl,
1487 .instructions = .{},
1488 .arena = &decl_arena.allocator,
1489 .inlining = null,
1490 .is_comptime = true,
1491 .branch_quota = &branch_quota,
1492 };
1493 defer block_scope.instructions.deinit(mod.gpa);
1494
1495 decl.is_pub = var_decl.visib_token != null;
1496 const is_extern = blk: {
1497 const maybe_extern_token = var_decl.extern_export_token orelse break :blk false;
1498 if (token_tags[maybe_extern_token] != .keyword_extern) break :blk false;
1499 if (var_decl.ast.init_node != 0) {
1500 return mod.failNode(
1501 &block_scope.base,
1502 var_decl.ast.init_node,
1503 "extern variables have no initializers",
1504 .{},
1505 );
1506 }
1507 break :blk true;
1508 };
1509 if (var_decl.lib_name) |lib_name| {
1510 assert(is_extern);
1511 return mod.failTok(&block_scope.base, lib_name, "TODO implement function library name", .{});
1512 }
1513 const is_mutable = token_tags[var_decl.ast.mut_token] == .keyword_var;
1514 const is_threadlocal = if (var_decl.threadlocal_token) |some| blk: {
1515 if (!is_mutable) {
1516 return mod.failTok(&block_scope.base, some, "threadlocal variable cannot be constant", .{});
1517 }
1518 break :blk true;
1519 } else false;
1520 assert(var_decl.comptime_token == null);
1521 if (var_decl.ast.align_node != 0) {
1522 return mod.failNode(
1523 &block_scope.base,
1524 var_decl.ast.align_node,
1525 "TODO implement function align expression",
1526 .{},
1527 );
1528 }
1529 if (var_decl.ast.section_node != 0) {
1530 return mod.failNode(
1531 &block_scope.base,
1532 var_decl.ast.section_node,
1533 "TODO implement function section expression",
1534 .{},
1535 );
1536 }
14721537
1473 const new_variable = try decl_arena.allocator.create(Var);
1474 new_variable.* = .{
1475 .owner_decl = decl,
1476 .init = var_info.val orelse undefined,
1477 .is_extern = is_extern,
1478 .is_mutable = is_mutable,
1479 .is_threadlocal = is_threadlocal,
1480 };
1481 const var_val = try Value.Tag.variable.create(&decl_arena.allocator, new_variable);
1482
1483 decl_arena_state.* = decl_arena.state;
1484 decl.typed_value = .{
1485 .most_recent = .{
1486 .typed_value = .{
1487 .ty = var_info.ty,
1488 .val = var_val,
1489 },
1490 .arena = decl_arena_state,
1491 },
1492 };
1493 decl.analysis = .complete;
1494 decl.generation = self.generation;
1495
1496 if (var_decl.getExternExportToken()) |maybe_export_token| {
1497 if (tree.token_ids[maybe_export_token] == .Keyword_export) {
1498 const export_src = tree.token_locs[maybe_export_token].start;
1499 const name_loc = tree.token_locs[var_decl.name_token];
1500 const name = tree.tokenSliceLoc(name_loc);
1501 // The scope needs to have the decl in it.
1502 try self.analyzeExport(&block_scope.base, export_src, name, decl);
1503 }
1504 }
1505 return type_changed;
1506 },
1507 .Comptime => {
1508 const comptime_decl = @fieldParentPtr(ast.Node.Comptime, "base", ast_node);
1538 const var_info: struct { ty: Type, val: ?Value } = if (var_decl.ast.init_node != 0) vi: {
1539 var gen_scope_arena = std.heap.ArenaAllocator.init(mod.gpa);
1540 defer gen_scope_arena.deinit();
1541 var gen_scope: Scope.GenZIR = .{
1542 .decl = decl,
1543 .arena = &gen_scope_arena.allocator,
1544 .parent = &decl.container.base,
1545 .force_comptime = true,
1546 };
1547 defer gen_scope.instructions.deinit(mod.gpa);
15091548
1510 decl.analysis = .in_progress;
1549 const init_result_loc: astgen.ResultLoc = if (var_decl.ast.type_node != 0) rl: {
1550 const type_node = var_decl.ast.type_node;
1551 const src = token_starts[tree.firstToken(type_node)];
1552 const type_type = try astgen.addZIRInstConst(mod, &gen_scope.base, src, .{
1553 .ty = Type.initTag(.type),
1554 .val = Value.initTag(.type_type),
1555 });
1556 const var_type = try astgen.expr(mod, &gen_scope.base, .{ .ty = type_type }, type_node);
1557 break :rl .{ .ty = var_type };
1558 } else .none;
1559
1560 const init_inst = try astgen.comptimeExpr(
1561 mod,
1562 &gen_scope.base,
1563 init_result_loc,
1564 var_decl.ast.init_node,
1565 );
1566 if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
1567 zir.dumpZir(mod.gpa, "var_init", decl.name, gen_scope.instructions.items) catch {};
1568 }
15111569
1512 // A comptime decl does not store any value so we can just deinit
1513 // this arena after analysis is done.
1514 var analysis_arena = std.heap.ArenaAllocator.init(self.gpa);
1515 defer analysis_arena.deinit();
1516 var gen_scope: Scope.GenZIR = .{
1517 .decl = decl,
1518 .arena = &analysis_arena.allocator,
1519 .parent = &decl.container.base,
1520 .force_comptime = true,
1521 };
1522 defer gen_scope.instructions.deinit(self.gpa);
1570 var var_inst_table = Scope.Block.InstTable.init(mod.gpa);
1571 defer var_inst_table.deinit();
1572
1573 var branch_quota_vi: u32 = default_eval_branch_quota;
1574 var inner_block: Scope.Block = .{
1575 .parent = null,
1576 .inst_table = &var_inst_table,
1577 .func = null,
1578 .owner_decl = decl,
1579 .src_decl = decl,
1580 .instructions = .{},
1581 .arena = &gen_scope_arena.allocator,
1582 .inlining = null,
1583 .is_comptime = true,
1584 .branch_quota = &branch_quota_vi,
1585 };
1586 defer inner_block.instructions.deinit(mod.gpa);
1587 try zir_sema.analyzeBody(mod, &inner_block, .{
1588 .instructions = gen_scope.instructions.items,
1589 });
15231590
1524 _ = try astgen.comptimeExpr(self, &gen_scope.base, .none, comptime_decl.expr);
1525 if (std.builtin.mode == .Debug and self.comp.verbose_ir) {
1526 zir.dumpZir(self.gpa, "comptime_block", decl.name, gen_scope.instructions.items) catch {};
1527 }
1591 // The result location guarantees the type coercion.
1592 const analyzed_init_inst = var_inst_table.get(init_inst).?;
1593 // The is_comptime in the Scope.Block guarantees the result is comptime-known.
1594 const val = analyzed_init_inst.value().?;
15281595
1529 var inst_table = Scope.Block.InstTable.init(self.gpa);
1530 defer inst_table.deinit();
1596 const ty = try analyzed_init_inst.ty.copy(block_scope.arena);
1597 break :vi .{
1598 .ty = ty,
1599 .val = try val.copy(block_scope.arena),
1600 };
1601 } else if (!is_extern) {
1602 return mod.failTok(
1603 &block_scope.base,
1604 var_decl.ast.mut_token,
1605 "variables must be initialized",
1606 .{},
1607 );
1608 } else if (var_decl.ast.type_node != 0) vi: {
1609 const type_node = var_decl.ast.type_node;
1610 // Temporary arena for the zir instructions.
1611 var type_scope_arena = std.heap.ArenaAllocator.init(mod.gpa);
1612 defer type_scope_arena.deinit();
1613 var type_scope: Scope.GenZIR = .{
1614 .decl = decl,
1615 .arena = &type_scope_arena.allocator,
1616 .parent = &decl.container.base,
1617 .force_comptime = true,
1618 };
1619 defer type_scope.instructions.deinit(mod.gpa);
15311620
1532 var branch_quota: u32 = default_eval_branch_quota;
1621 const var_type = try astgen.typeExpr(mod, &type_scope.base, type_node);
1622 if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
1623 zir.dumpZir(mod.gpa, "var_type", decl.name, type_scope.instructions.items) catch {};
1624 }
15331625
1534 var block_scope: Scope.Block = .{
1535 .parent = null,
1536 .inst_table = &inst_table,
1537 .func = null,
1538 .owner_decl = decl,
1539 .src_decl = decl,
1540 .instructions = .{},
1541 .arena = &analysis_arena.allocator,
1542 .inlining = null,
1543 .is_comptime = true,
1544 .branch_quota = &branch_quota,
1545 };
1546 defer block_scope.instructions.deinit(self.gpa);
1626 const ty = try zir_sema.analyzeBodyValueAsType(mod, &block_scope, var_type, .{
1627 .instructions = type_scope.instructions.items,
1628 });
1629 break :vi .{
1630 .ty = ty,
1631 .val = null,
1632 };
1633 } else {
1634 return mod.failTok(
1635 &block_scope.base,
1636 var_decl.ast.mut_token,
1637 "unable to infer variable type",
1638 .{},
1639 );
1640 };
15471641
1548 _ = try zir_sema.analyzeBody(self, &block_scope, .{
1549 .instructions = gen_scope.instructions.items,
1550 });
1642 if (is_mutable and !var_info.ty.isValidVarType(is_extern)) {
1643 return mod.failTok(
1644 &block_scope.base,
1645 var_decl.ast.mut_token,
1646 "variable of type '{}' must be const",
1647 .{var_info.ty},
1648 );
1649 }
15511650
1552 decl.analysis = .complete;
1553 decl.generation = self.generation;
1554 return true;
1651 var type_changed = true;
1652 if (decl.typedValueManaged()) |tvm| {
1653 type_changed = !tvm.typed_value.ty.eql(var_info.ty);
1654
1655 tvm.deinit(mod.gpa);
1656 }
1657
1658 const new_variable = try decl_arena.allocator.create(Var);
1659 new_variable.* = .{
1660 .owner_decl = decl,
1661 .init = var_info.val orelse undefined,
1662 .is_extern = is_extern,
1663 .is_mutable = is_mutable,
1664 .is_threadlocal = is_threadlocal,
1665 };
1666 const var_val = try Value.Tag.variable.create(&decl_arena.allocator, new_variable);
1667
1668 decl_arena_state.* = decl_arena.state;
1669 decl.typed_value = .{
1670 .most_recent = .{
1671 .typed_value = .{
1672 .ty = var_info.ty,
1673 .val = var_val,
1674 },
1675 .arena = decl_arena_state,
15551676 },
1556 .Use => @panic("TODO usingnamespace decl"),
1557 else => unreachable,
1677 };
1678 decl.analysis = .complete;
1679 decl.generation = mod.generation;
1680
1681 if (var_decl.extern_export_token) |maybe_export_token| {
1682 if (token_tags[maybe_export_token] == .keyword_export) {
1683 const export_src = token_starts[maybe_export_token];
1684 const name_token = var_decl.ast.mut_token + 1;
1685 const name = tree.tokenSlice(name_token); // TODO identifierTokenString
1686 // The scope needs to have the decl in it.
1687 try mod.analyzeExport(&block_scope.base, export_src, name, decl);
1688 }
15581689 }
1690 return type_changed;
15591691}
15601692
15611693fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void {
......@@ -1566,7 +1698,7 @@ fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void
15661698 dependee.dependants.putAssumeCapacity(depender, {});
15671699}
15681700
1569pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {
1701pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*const ast.Tree {
15701702 const tracy = trace(@src());
15711703 defer tracy.end();
15721704
......@@ -1577,8 +1709,10 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {
15771709 const source = try root_scope.getSource(self);
15781710
15791711 var keep_tree = false;
1580 const tree = try std.zig.parse(self.gpa, source);
1581 defer if (!keep_tree) tree.deinit();
1712 root_scope.tree = try std.zig.parse(self.gpa, source);
1713 defer if (!keep_tree) root_scope.tree.deinit(self.gpa);
1714
1715 const tree = &root_scope.tree;
15821716
15831717 if (tree.errors.len != 0) {
15841718 const parse_err = tree.errors[0];
......@@ -1586,12 +1720,12 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {
15861720 var msg = std.ArrayList(u8).init(self.gpa);
15871721 defer msg.deinit();
15881722
1589 try parse_err.render(tree.token_ids, msg.writer());
1723 try tree.renderError(parse_err, msg.writer());
15901724 const err_msg = try self.gpa.create(ErrorMsg);
15911725 err_msg.* = .{
15921726 .src_loc = .{
15931727 .file_scope = root_scope,
1594 .byte_offset = tree.token_locs[parse_err.loc()].start,
1728 .byte_offset = tree.tokens.items(.start)[parse_err.token],
15951729 },
15961730 .msg = msg.toOwnedSlice(),
15971731 };
......@@ -1602,7 +1736,6 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {
16021736 }
16031737
16041738 root_scope.status = .loaded_success;
1605 root_scope.contents = .{ .tree = tree };
16061739 keep_tree = true;
16071740
16081741 return tree;
......@@ -1610,151 +1743,353 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {
16101743
16111744 .unloaded_parse_failure => return error.AnalysisFail,
16121745
1613 .loaded_success => return root_scope.contents.tree,
1746 .loaded_success => return &root_scope.tree,
16141747 }
16151748}
16161749
1617pub fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void {
1750pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void {
16181751 const tracy = trace(@src());
16191752 defer tracy.end();
16201753
16211754 // We may be analyzing it for the first time, or this may be
16221755 // an incremental update. This code handles both cases.
1623 const tree = try self.getAstTree(container_scope.file_scope);
1624 const decls = tree.root_node.decls();
1756 const tree = try mod.getAstTree(container_scope.file_scope);
1757 const node_tags = tree.nodes.items(.tag);
1758 const node_datas = tree.nodes.items(.data);
1759 const decls = tree.rootDecls();
16251760
1626 try self.comp.work_queue.ensureUnusedCapacity(decls.len);
1627 try container_scope.decls.ensureCapacity(self.gpa, decls.len);
1761 try mod.comp.work_queue.ensureUnusedCapacity(decls.len);
1762 try container_scope.decls.ensureCapacity(mod.gpa, decls.len);
16281763
16291764 // Keep track of the decls that we expect to see in this file so that
16301765 // we know which ones have been deleted.
1631 var deleted_decls = std.AutoArrayHashMap(*Decl, void).init(self.gpa);
1766 var deleted_decls = std.AutoArrayHashMap(*Decl, void).init(mod.gpa);
16321767 defer deleted_decls.deinit();
16331768 try deleted_decls.ensureCapacity(container_scope.decls.items().len);
16341769 for (container_scope.decls.items()) |entry| {
16351770 deleted_decls.putAssumeCapacityNoClobber(entry.key, {});
16361771 }
16371772
1638 for (decls) |src_decl, decl_i| {
1639 if (src_decl.cast(ast.Node.FnProto)) |fn_proto| {
1640 // We will create a Decl for it regardless of analysis status.
1641 const name_tok = fn_proto.getNameToken() orelse {
1642 @panic("TODO missing function name");
1643 };
1644
1645 const name_loc = tree.token_locs[name_tok];
1646 const name = tree.tokenSliceLoc(name_loc);
1647 const name_hash = container_scope.fullyQualifiedNameHash(name);
1648 const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));
1649 if (self.decl_table.get(name_hash)) |decl| {
1650 // Update the AST Node index of the decl, even if its contents are unchanged, it may
1651 // have been re-ordered.
1652 decl.src_index = decl_i;
1653 if (deleted_decls.swapRemove(decl) == null) {
1654 decl.analysis = .sema_failure;
1655 const msg = try ErrorMsg.create(self.gpa, .{
1656 .file_scope = container_scope.file_scope,
1657 .byte_offset = tree.token_locs[name_tok].start,
1658 }, "redefinition of '{s}'", .{decl.name});
1659 errdefer msg.destroy(self.gpa);
1660 try self.failed_decls.putNoClobber(self.gpa, decl, msg);
1661 } else {
1662 if (!srcHashEql(decl.contents_hash, contents_hash)) {
1663 try self.markOutdatedDecl(decl);
1664 decl.contents_hash = contents_hash;
1665 } else switch (self.comp.bin_file.tag) {
1666 .coff => {
1667 // TODO Implement for COFF
1668 },
1669 .elf => if (decl.fn_link.elf.len != 0) {
1670 // TODO Look into detecting when this would be unnecessary by storing enough state
1671 // in `Decl` to notice that the line number did not change.
1672 self.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
1673 },
1674 .macho => if (decl.fn_link.macho.len != 0) {
1675 // TODO Look into detecting when this would be unnecessary by storing enough state
1676 // in `Decl` to notice that the line number did not change.
1677 self.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
1678 },
1679 .c, .wasm, .spirv => {},
1680 }
1681 }
1682 } else {
1683 const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
1684 container_scope.decls.putAssumeCapacity(new_decl, {});
1685 if (fn_proto.getExternExportInlineToken()) |maybe_export_token| {
1686 if (tree.token_ids[maybe_export_token] == .Keyword_export) {
1687 self.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
1688 }
1689 }
1690 }
1691 } else if (src_decl.castTag(.VarDecl)) |var_decl| {
1692 const name_loc = tree.token_locs[var_decl.name_token];
1693 const name = tree.tokenSliceLoc(name_loc);
1694 const name_hash = container_scope.fullyQualifiedNameHash(name);
1695 const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));
1696 if (self.decl_table.get(name_hash)) |decl| {
1697 // Update the AST Node index of the decl, even if its contents are unchanged, it may
1698 // have been re-ordered.
1699 decl.src_index = decl_i;
1700 if (deleted_decls.swapRemove(decl) == null) {
1701 decl.analysis = .sema_failure;
1702 const err_msg = try ErrorMsg.create(self.gpa, .{
1703 .file_scope = container_scope.file_scope,
1704 .byte_offset = name_loc.start,
1705 }, "redefinition of '{s}'", .{decl.name});
1706 errdefer err_msg.destroy(self.gpa);
1707 try self.failed_decls.putNoClobber(self.gpa, decl, err_msg);
1708 } else if (!srcHashEql(decl.contents_hash, contents_hash)) {
1709 try self.markOutdatedDecl(decl);
1710 decl.contents_hash = contents_hash;
1711 }
1712 } else {
1713 const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
1714 container_scope.decls.putAssumeCapacity(new_decl, {});
1715 if (var_decl.getExternExportToken()) |maybe_export_token| {
1716 if (tree.token_ids[maybe_export_token] == .Keyword_export) {
1717 self.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
1718 }
1719 }
1773 for (decls) |decl_node, decl_i| switch (node_tags[decl_node]) {
1774 .fn_decl => {
1775 const fn_proto = node_datas[decl_node].lhs;
1776 const body = node_datas[decl_node].rhs;
1777 switch (node_tags[fn_proto]) {
1778 .fn_proto_simple => {
1779 var params: [1]ast.Node.Index = undefined;
1780 try mod.semaContainerFn(
1781 container_scope,
1782 &deleted_decls,
1783 decl_node,
1784 decl_i,
1785 tree.*,
1786 body,
1787 tree.fnProtoSimple(&params, fn_proto),
1788 );
1789 },
1790 .fn_proto_multi => try mod.semaContainerFn(
1791 container_scope,
1792 &deleted_decls,
1793 decl_node,
1794 decl_i,
1795 tree.*,
1796 body,
1797 tree.fnProtoMulti(fn_proto),
1798 ),
1799 .fn_proto_one => {
1800 var params: [1]ast.Node.Index = undefined;
1801 try mod.semaContainerFn(
1802 container_scope,
1803 &deleted_decls,
1804 decl_node,
1805 decl_i,
1806 tree.*,
1807 body,
1808 tree.fnProtoOne(&params, fn_proto),
1809 );
1810 },
1811 .fn_proto => try mod.semaContainerFn(
1812 container_scope,
1813 &deleted_decls,
1814 decl_node,
1815 decl_i,
1816 tree.*,
1817 body,
1818 tree.fnProto(fn_proto),
1819 ),
1820 else => unreachable,
17201821 }
1721 } else if (src_decl.castTag(.Comptime)) |comptime_node| {
1722 const name_index = self.getNextAnonNameIndex();
1723 const name = try std.fmt.allocPrint(self.gpa, "__comptime_{d}", .{name_index});
1724 defer self.gpa.free(name);
1822 },
1823 .fn_proto_simple => {
1824 var params: [1]ast.Node.Index = undefined;
1825 try mod.semaContainerFn(
1826 container_scope,
1827 &deleted_decls,
1828 decl_node,
1829 decl_i,
1830 tree.*,
1831 0,
1832 tree.fnProtoSimple(&params, decl_node),
1833 );
1834 },
1835 .fn_proto_multi => try mod.semaContainerFn(
1836 container_scope,
1837 &deleted_decls,
1838 decl_node,
1839 decl_i,
1840 tree.*,
1841 0,
1842 tree.fnProtoMulti(decl_node),
1843 ),
1844 .fn_proto_one => {
1845 var params: [1]ast.Node.Index = undefined;
1846 try mod.semaContainerFn(
1847 container_scope,
1848 &deleted_decls,
1849 decl_node,
1850 decl_i,
1851 tree.*,
1852 0,
1853 tree.fnProtoOne(&params, decl_node),
1854 );
1855 },
1856 .fn_proto => try mod.semaContainerFn(
1857 container_scope,
1858 &deleted_decls,
1859 decl_node,
1860 decl_i,
1861 tree.*,
1862 0,
1863 tree.fnProto(decl_node),
1864 ),
1865
1866 .global_var_decl => try mod.semaContainerVar(
1867 container_scope,
1868 &deleted_decls,
1869 decl_node,
1870 decl_i,
1871 tree.*,
1872 tree.globalVarDecl(decl_node),
1873 ),
1874 .local_var_decl => try mod.semaContainerVar(
1875 container_scope,
1876 &deleted_decls,
1877 decl_node,
1878 decl_i,
1879 tree.*,
1880 tree.localVarDecl(decl_node),
1881 ),
1882 .simple_var_decl => try mod.semaContainerVar(
1883 container_scope,
1884 &deleted_decls,
1885 decl_node,
1886 decl_i,
1887 tree.*,
1888 tree.simpleVarDecl(decl_node),
1889 ),
1890 .aligned_var_decl => try mod.semaContainerVar(
1891 container_scope,
1892 &deleted_decls,
1893 decl_node,
1894 decl_i,
1895 tree.*,
1896 tree.alignedVarDecl(decl_node),
1897 ),
1898
1899 .@"comptime" => {
1900 const name_index = mod.getNextAnonNameIndex();
1901 const name = try std.fmt.allocPrint(mod.gpa, "__comptime_{d}", .{name_index});
1902 defer mod.gpa.free(name);
17251903
17261904 const name_hash = container_scope.fullyQualifiedNameHash(name);
1727 const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));
1905 const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node));
17281906
1729 const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
1907 const new_decl = try mod.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
17301908 container_scope.decls.putAssumeCapacity(new_decl, {});
1731 self.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
1732 } else if (src_decl.castTag(.ContainerField)) |container_field| {
1733 log.err("TODO: analyze container field", .{});
1734 } else if (src_decl.castTag(.TestDecl)) |test_decl| {
1909 mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
1910 },
1911
1912 .container_field_init => try mod.semaContainerField(
1913 container_scope,
1914 &deleted_decls,
1915 decl_node,
1916 decl_i,
1917 tree.*,
1918 tree.containerFieldInit(decl_node),
1919 ),
1920 .container_field_align => try mod.semaContainerField(
1921 container_scope,
1922 &deleted_decls,
1923 decl_node,
1924 decl_i,
1925 tree.*,
1926 tree.containerFieldAlign(decl_node),
1927 ),
1928 .container_field => try mod.semaContainerField(
1929 container_scope,
1930 &deleted_decls,
1931 decl_node,
1932 decl_i,
1933 tree.*,
1934 tree.containerField(decl_node),
1935 ),
1936
1937 .test_decl => {
17351938 log.err("TODO: analyze test decl", .{});
1736 } else if (src_decl.castTag(.Use)) |use_decl| {
1939 },
1940 .@"usingnamespace" => {
17371941 log.err("TODO: analyze usingnamespace decl", .{});
1738 } else {
1739 unreachable;
1740 }
1741 }
1942 },
1943 else => unreachable,
1944 };
17421945 // Handle explicitly deleted decls from the source code. Not to be confused
17431946 // with when we delete decls because they are no longer referenced.
17441947 for (deleted_decls.items()) |entry| {
1745 log.debug("noticed '{s}' deleted from source\n", .{entry.key.name});
1746 try self.deleteDecl(entry.key);
1948 log.debug("noticed '{s}' deleted from source", .{entry.key.name});
1949 try mod.deleteDecl(entry.key);
1950 }
1951}
1952
1953fn semaContainerFn(
1954 mod: *Module,
1955 container_scope: *Scope.Container,
1956 deleted_decls: *std.AutoArrayHashMap(*Decl, void),
1957 decl_node: ast.Node.Index,
1958 decl_i: usize,
1959 tree: ast.Tree,
1960 body_node: ast.Node.Index,
1961 fn_proto: ast.full.FnProto,
1962) !void {
1963 const tracy = trace(@src());
1964 defer tracy.end();
1965
1966 const token_starts = tree.tokens.items(.start);
1967 const token_tags = tree.tokens.items(.tag);
1968
1969 // We will create a Decl for it regardless of analysis status.
1970 const name_tok = fn_proto.name_token orelse {
1971 @panic("TODO missing function name");
1972 };
1973 const name = tree.tokenSlice(name_tok); // TODO use identifierTokenString
1974 const name_hash = container_scope.fullyQualifiedNameHash(name);
1975 const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node));
1976 if (mod.decl_table.get(name_hash)) |decl| {
1977 // Update the AST Node index of the decl, even if its contents are unchanged, it may
1978 // have been re-ordered.
1979 decl.src_index = decl_i;
1980 if (deleted_decls.swapRemove(decl) == null) {
1981 decl.analysis = .sema_failure;
1982 const msg = try ErrorMsg.create(mod.gpa, .{
1983 .file_scope = container_scope.file_scope,
1984 .byte_offset = token_starts[name_tok],
1985 }, "redefinition of '{s}'", .{decl.name});
1986 errdefer msg.destroy(mod.gpa);
1987 try mod.failed_decls.putNoClobber(mod.gpa, decl, msg);
1988 } else {
1989 if (!srcHashEql(decl.contents_hash, contents_hash)) {
1990 try mod.markOutdatedDecl(decl);
1991 decl.contents_hash = contents_hash;
1992 } else switch (mod.comp.bin_file.tag) {
1993 .coff => {
1994 // TODO Implement for COFF
1995 },
1996 .elf => if (decl.fn_link.elf.len != 0) {
1997 // TODO Look into detecting when this would be unnecessary by storing enough state
1998 // in `Decl` to notice that the line number did not change.
1999 mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
2000 },
2001 .macho => if (decl.fn_link.macho.len != 0) {
2002 // TODO Look into detecting when this would be unnecessary by storing enough state
2003 // in `Decl` to notice that the line number did not change.
2004 mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
2005 },
2006 .c, .wasm, .spirv => {},
2007 }
2008 }
2009 } else {
2010 const new_decl = try mod.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
2011 container_scope.decls.putAssumeCapacity(new_decl, {});
2012 if (fn_proto.extern_export_token) |maybe_export_token| {
2013 if (token_tags[maybe_export_token] == .keyword_export) {
2014 mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
2015 }
2016 }
2017 }
2018}
2019
2020fn semaContainerVar(
2021 mod: *Module,
2022 container_scope: *Scope.Container,
2023 deleted_decls: *std.AutoArrayHashMap(*Decl, void),
2024 decl_node: ast.Node.Index,
2025 decl_i: usize,
2026 tree: ast.Tree,
2027 var_decl: ast.full.VarDecl,
2028) !void {
2029 const tracy = trace(@src());
2030 defer tracy.end();
2031
2032 const token_starts = tree.tokens.items(.start);
2033 const token_tags = tree.tokens.items(.tag);
2034
2035 const name_token = var_decl.ast.mut_token + 1;
2036 const name_src = token_starts[name_token];
2037 const name = tree.tokenSlice(name_token); // TODO identifierTokenString
2038 const name_hash = container_scope.fullyQualifiedNameHash(name);
2039 const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node));
2040 if (mod.decl_table.get(name_hash)) |decl| {
2041 // Update the AST Node index of the decl, even if its contents are unchanged, it may
2042 // have been re-ordered.
2043 decl.src_index = decl_i;
2044 if (deleted_decls.swapRemove(decl) == null) {
2045 decl.analysis = .sema_failure;
2046 const err_msg = try ErrorMsg.create(mod.gpa, .{
2047 .file_scope = container_scope.file_scope,
2048 .byte_offset = name_src,
2049 }, "redefinition of '{s}'", .{decl.name});
2050 errdefer err_msg.destroy(mod.gpa);
2051 try mod.failed_decls.putNoClobber(mod.gpa, decl, err_msg);
2052 } else if (!srcHashEql(decl.contents_hash, contents_hash)) {
2053 try mod.markOutdatedDecl(decl);
2054 decl.contents_hash = contents_hash;
2055 }
2056 } else {
2057 const new_decl = try mod.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
2058 container_scope.decls.putAssumeCapacity(new_decl, {});
2059 if (var_decl.extern_export_token) |maybe_export_token| {
2060 if (token_tags[maybe_export_token] == .keyword_export) {
2061 mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
2062 }
2063 }
17472064 }
17482065}
17492066
2067fn semaContainerField(
2068 mod: *Module,
2069 container_scope: *Scope.Container,
2070 deleted_decls: *std.AutoArrayHashMap(*Decl, void),
2071 decl_node: ast.Node.Index,
2072 decl_i: usize,
2073 tree: ast.Tree,
2074 field: ast.full.ContainerField,
2075) !void {
2076 const tracy = trace(@src());
2077 defer tracy.end();
2078
2079 log.err("TODO: analyze container field", .{});
2080}
2081
17502082pub fn deleteDecl(self: *Module, decl: *Decl) !void {
2083 const tracy = trace(@src());
2084 defer tracy.end();
2085
17512086 try self.deletion_set.ensureCapacity(self.gpa, self.deletion_set.items.len + decl.dependencies.items().len);
17522087
17532088 // Remove from the namespace it resides in. In the case of an anonymous Decl it will
17542089 // not be present in the set, and this does nothing.
17552090 decl.container.removeDecl(decl);
17562091
1757 log.debug("deleting decl '{s}'\n", .{decl.name});
2092 log.debug("deleting decl '{s}'", .{decl.name});
17582093 const name_hash = decl.fullyQualifiedNameHash();
17592094 self.decl_table.removeAssertDiscard(name_hash);
17602095 // Remove itself from its dependencies, because we are about to destroy the decl pointer.
......@@ -1856,18 +2191,18 @@ pub fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
18562191 defer inner_block.instructions.deinit(self.gpa);
18572192
18582193 func.state = .in_progress;
1859 log.debug("set {s} to in_progress\n", .{decl.name});
2194 log.debug("set {s} to in_progress", .{decl.name});
18602195
18612196 try zir_sema.analyzeBody(self, &inner_block, func.zir);
18622197
18632198 const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items);
18642199 func.state = .success;
18652200 func.body = .{ .instructions = instructions };
1866 log.debug("set {s} to success\n", .{decl.name});
2201 log.debug("set {s} to success", .{decl.name});
18672202}
18682203
18692204fn markOutdatedDecl(self: *Module, decl: *Decl) !void {
1870 log.debug("mark {s} outdated\n", .{decl.name});
2205 log.debug("mark {s} outdated", .{decl.name});
18712206 try self.comp.work_queue.writeItem(.{ .analyze_decl = decl });
18722207 if (self.failed_decls.swapRemove(decl)) |entry| {
18732208 entry.value.destroy(self.gpa);
......@@ -2395,15 +2730,16 @@ pub fn createContainerDecl(
23952730fn getAnonTypeName(self: *Module, scope: *Scope, base_token: std.zig.ast.TokenIndex) ![]u8 {
23962731 // TODO add namespaces, generic function signatrues
23972732 const tree = scope.tree();
2398 const base_name = switch (tree.token_ids[base_token]) {
2399 .Keyword_struct => "struct",
2400 .Keyword_enum => "enum",
2401 .Keyword_union => "union",
2402 .Keyword_opaque => "opaque",
2733 const token_tags = tree.tokens.items(.tag);
2734 const base_name = switch (token_tags[base_token]) {
2735 .keyword_struct => "struct",
2736 .keyword_enum => "enum",
2737 .keyword_union => "union",
2738 .keyword_opaque => "opaque",
24032739 else => unreachable,
24042740 };
2405 const loc = tree.tokenLocationLoc(0, tree.token_locs[base_token]);
2406 return std.fmt.allocPrint(self.gpa, "{s}:{}:{}", .{ base_name, loc.line, loc.column });
2741 const loc = tree.tokenLocation(0, base_token);
2742 return std.fmt.allocPrint(self.gpa, "{s}:{d}:{d}", .{ base_name, loc.line, loc.column });
24072743}
24082744
24092745fn getNextAnonNameIndex(self: *Module) usize {
......@@ -2639,7 +2975,7 @@ pub fn analyzeImport(self: *Module, scope: *Scope, src: usize, target_string: []
26392975 file_scope.* = .{
26402976 .sub_file_path = resolved_path,
26412977 .source = .{ .unloaded = {} },
2642 .contents = .{ .not_available = {} },
2978 .tree = undefined,
26432979 .status = .never_loaded,
26442980 .pkg = found_pkg orelse cur_pkg,
26452981 .root_container = .{
......@@ -3149,18 +3485,19 @@ pub fn failTok(
31493485 comptime format: []const u8,
31503486 args: anytype,
31513487) InnerError {
3152 const src = scope.tree().token_locs[token_index].start;
3488 const src = scope.tree().tokens.items(.start)[token_index];
31533489 return self.fail(scope, src, format, args);
31543490}
31553491
31563492pub fn failNode(
31573493 self: *Module,
31583494 scope: *Scope,
3159 ast_node: *ast.Node,
3495 ast_node: ast.Node.Index,
31603496 comptime format: []const u8,
31613497 args: anytype,
31623498) InnerError {
3163 const src = scope.tree().token_locs[ast_node.firstToken()].start;
3499 const tree = scope.tree();
3500 const src = tree.tokens.items(.start)[tree.firstToken(ast_node)];
31643501 return self.fail(scope, src, format, args);
31653502}
31663503
......@@ -3594,6 +3931,9 @@ pub fn validateVarType(mod: *Module, scope: *Scope, src: usize, ty: Type) !void
35943931/// Identifier token -> String (allocated in scope.arena())
35953932pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 {
35963933 const tree = scope.tree();
3934 const token_tags = tree.tokens.items(.tag);
3935 const token_starts = tree.tokens.items(.start);
3936 assert(token_tags[token] == .identifier);
35973937
35983938 const ident_name = tree.tokenSlice(token);
35993939 if (mem.startsWith(u8, ident_name, "@")) {
......@@ -3602,7 +3942,7 @@ pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex)
36023942 return std.zig.parseStringLiteral(scope.arena(), raw_string, &bad_index) catch |err| switch (err) {
36033943 error.InvalidCharacter => {
36043944 const bad_byte = raw_string[bad_index];
3605 const src = tree.token_locs[token].start;
3945 const src = token_starts[token];
36063946 return mod.fail(scope, src + 1 + bad_index, "invalid string literal character: '{c}'\n", .{bad_byte});
36073947 },
36083948 else => |e| return e,
src/astgen.zig+1908-1427
......@@ -1,16 +1,18 @@
11const std = @import("std");
22const mem = std.mem;
33const Allocator = std.mem.Allocator;
4const assert = std.debug.assert;
5
46const Value = @import("value.zig").Value;
57const Type = @import("type.zig").Type;
68const TypedValue = @import("TypedValue.zig");
7const assert = std.debug.assert;
89const zir = @import("zir.zig");
910const Module = @import("Module.zig");
1011const ast = std.zig.ast;
1112const trace = @import("tracy.zig").trace;
1213const Scope = Module.Scope;
1314const InnerError = Module.InnerError;
15const BuiltinFn = @import("BuiltinFn.zig");
1416
1517pub const ResultLoc = union(enum) {
1618 /// The expression is the right-hand side of assignment to `_`. Only the side-effects of the
......@@ -55,8 +57,11 @@ pub const ResultLoc = union(enum) {
5557 };
5658};
5759
58pub fn typeExpr(mod: *Module, scope: *Scope, type_node: *ast.Node) InnerError!*zir.Inst {
59 const type_src = scope.tree().token_locs[type_node.firstToken()].start;
60pub fn typeExpr(mod: *Module, scope: *Scope, type_node: ast.Node.Index) InnerError!*zir.Inst {
61 const tree = scope.tree();
62 const token_starts = tree.tokens.items(.start);
63
64 const type_src = token_starts[tree.firstToken(type_node)];
6065 const type_type = try addZIRInstConst(mod, scope, type_src, .{
6166 .ty = Type.initTag(.type),
6267 .val = Value.initTag(.type_type),
......@@ -65,134 +70,191 @@ pub fn typeExpr(mod: *Module, scope: *Scope, type_node: *ast.Node) InnerError!*z
6570 return expr(mod, scope, type_rl, type_node);
6671}
6772
68fn lvalExpr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst {
69 switch (node.tag) {
70 .Root => unreachable,
71 .Use => unreachable,
72 .TestDecl => unreachable,
73 .DocComment => unreachable,
74 .VarDecl => unreachable,
75 .SwitchCase => unreachable,
76 .SwitchElse => unreachable,
77 .Else => unreachable,
78 .Payload => unreachable,
79 .PointerPayload => unreachable,
80 .PointerIndexPayload => unreachable,
81 .ErrorTag => unreachable,
82 .FieldInitializer => unreachable,
83 .ContainerField => unreachable,
84
85 .Assign,
86 .AssignBitAnd,
87 .AssignBitOr,
88 .AssignBitShiftLeft,
89 .AssignBitShiftRight,
90 .AssignBitXor,
91 .AssignDiv,
92 .AssignSub,
93 .AssignSubWrap,
94 .AssignMod,
95 .AssignAdd,
96 .AssignAddWrap,
97 .AssignMul,
98 .AssignMulWrap,
99 .Add,
100 .AddWrap,
101 .Sub,
102 .SubWrap,
103 .Mul,
104 .MulWrap,
105 .Div,
106 .Mod,
107 .BitAnd,
108 .BitOr,
109 .BitShiftLeft,
110 .BitShiftRight,
111 .BitXor,
112 .BangEqual,
113 .EqualEqual,
114 .GreaterThan,
115 .GreaterOrEqual,
116 .LessThan,
117 .LessOrEqual,
118 .ArrayCat,
119 .ArrayMult,
120 .BoolAnd,
121 .BoolOr,
122 .Asm,
123 .StringLiteral,
124 .IntegerLiteral,
125 .Call,
126 .Unreachable,
127 .Return,
128 .If,
129 .While,
130 .BoolNot,
131 .AddressOf,
132 .FloatLiteral,
133 .UndefinedLiteral,
134 .BoolLiteral,
135 .NullLiteral,
136 .OptionalType,
137 .Block,
138 .LabeledBlock,
139 .Break,
140 .PtrType,
141 .ArrayType,
142 .ArrayTypeSentinel,
143 .EnumLiteral,
144 .MultilineStringLiteral,
145 .CharLiteral,
146 .Defer,
147 .Catch,
148 .ErrorUnion,
149 .MergeErrorSets,
150 .Range,
151 .Await,
152 .BitNot,
153 .Negation,
154 .NegationWrap,
155 .Resume,
156 .Try,
157 .SliceType,
158 .Slice,
159 .ArrayInitializer,
160 .ArrayInitializerDot,
161 .StructInitializer,
162 .StructInitializerDot,
163 .Switch,
164 .For,
165 .Suspend,
166 .Continue,
167 .AnyType,
168 .ErrorType,
169 .FnProto,
170 .AnyFrameType,
171 .ErrorSetDecl,
172 .ContainerDecl,
173 .Comptime,
174 .Nosuspend,
73fn lvalExpr(mod: *Module, scope: *Scope, node: ast.Node.Index) InnerError!*zir.Inst {
74 const tree = scope.tree();
75 const node_tags = tree.nodes.items(.tag);
76 const main_tokens = tree.nodes.items(.main_token);
77 switch (node_tags[node]) {
78 .root => unreachable,
79 .@"usingnamespace" => unreachable,
80 .test_decl => unreachable,
81 .global_var_decl => unreachable,
82 .local_var_decl => unreachable,
83 .simple_var_decl => unreachable,
84 .aligned_var_decl => unreachable,
85 .switch_case => unreachable,
86 .switch_case_one => unreachable,
87 .container_field_init => unreachable,
88 .container_field_align => unreachable,
89 .container_field => unreachable,
90 .asm_output => unreachable,
91 .asm_input => unreachable,
92
93 .assign,
94 .assign_bit_and,
95 .assign_bit_or,
96 .assign_bit_shift_left,
97 .assign_bit_shift_right,
98 .assign_bit_xor,
99 .assign_div,
100 .assign_sub,
101 .assign_sub_wrap,
102 .assign_mod,
103 .assign_add,
104 .assign_add_wrap,
105 .assign_mul,
106 .assign_mul_wrap,
107 .add,
108 .add_wrap,
109 .sub,
110 .sub_wrap,
111 .mul,
112 .mul_wrap,
113 .div,
114 .mod,
115 .bit_and,
116 .bit_or,
117 .bit_shift_left,
118 .bit_shift_right,
119 .bit_xor,
120 .bang_equal,
121 .equal_equal,
122 .greater_than,
123 .greater_or_equal,
124 .less_than,
125 .less_or_equal,
126 .array_cat,
127 .array_mult,
128 .bool_and,
129 .bool_or,
130 .@"asm",
131 .asm_simple,
132 .string_literal,
133 .integer_literal,
134 .call,
135 .call_comma,
136 .async_call,
137 .async_call_comma,
138 .call_one,
139 .call_one_comma,
140 .async_call_one,
141 .async_call_one_comma,
142 .unreachable_literal,
143 .@"return",
144 .@"if",
145 .if_simple,
146 .@"while",
147 .while_simple,
148 .while_cont,
149 .bool_not,
150 .address_of,
151 .float_literal,
152 .undefined_literal,
153 .true_literal,
154 .false_literal,
155 .null_literal,
156 .optional_type,
157 .block,
158 .block_semicolon,
159 .block_two,
160 .block_two_semicolon,
161 .@"break",
162 .ptr_type_aligned,
163 .ptr_type_sentinel,
164 .ptr_type,
165 .ptr_type_bit_range,
166 .array_type,
167 .array_type_sentinel,
168 .enum_literal,
169 .multiline_string_literal,
170 .char_literal,
171 .@"defer",
172 .@"errdefer",
173 .@"catch",
174 .error_union,
175 .merge_error_sets,
176 .switch_range,
177 .@"await",
178 .bit_not,
179 .negation,
180 .negation_wrap,
181 .@"resume",
182 .@"try",
183 .slice,
184 .slice_open,
185 .slice_sentinel,
186 .array_init_one,
187 .array_init_one_comma,
188 .array_init_dot_two,
189 .array_init_dot_two_comma,
190 .array_init_dot,
191 .array_init_dot_comma,
192 .array_init,
193 .array_init_comma,
194 .struct_init_one,
195 .struct_init_one_comma,
196 .struct_init_dot_two,
197 .struct_init_dot_two_comma,
198 .struct_init_dot,
199 .struct_init_dot_comma,
200 .struct_init,
201 .struct_init_comma,
202 .@"switch",
203 .switch_comma,
204 .@"for",
205 .for_simple,
206 .@"suspend",
207 .@"continue",
208 .@"anytype",
209 .fn_proto_simple,
210 .fn_proto_multi,
211 .fn_proto_one,
212 .fn_proto,
213 .fn_decl,
214 .anyframe_type,
215 .anyframe_literal,
216 .error_set_decl,
217 .container_decl,
218 .container_decl_trailing,
219 .container_decl_two,
220 .container_decl_two_trailing,
221 .container_decl_arg,
222 .container_decl_arg_trailing,
223 .tagged_union,
224 .tagged_union_trailing,
225 .tagged_union_two,
226 .tagged_union_two_trailing,
227 .tagged_union_enum_tag,
228 .tagged_union_enum_tag_trailing,
229 .@"comptime",
230 .@"nosuspend",
231 .error_value,
175232 => return mod.failNode(scope, node, "invalid left-hand side to assignment", .{}),
176233
177 // @field can be assigned to
178 .BuiltinCall => {
179 const call = node.castTag(.BuiltinCall).?;
180 const tree = scope.tree();
181 const builtin_name = tree.tokenSlice(call.builtin_token);
182
183 if (!mem.eql(u8, builtin_name, "@field")) {
184 return mod.failNode(scope, node, "invalid left-hand side to assignment", .{});
234 .builtin_call,
235 .builtin_call_comma,
236 .builtin_call_two,
237 .builtin_call_two_comma,
238 => {
239 const builtin_token = main_tokens[node];
240 const builtin_name = tree.tokenSlice(builtin_token);
241 // If the builtin is an invalid name, we don't cause an error here; instead
242 // let it pass, and the error will be "invalid builtin function" later.
243 if (BuiltinFn.list.get(builtin_name)) |info| {
244 if (!info.allows_lvalue) {
245 return mod.failNode(scope, node, "invalid left-hand side to assignment", .{});
246 }
185247 }
186248 },
187249
188 // can be assigned to
189 .UnwrapOptional,
190 .Deref,
191 .Period,
192 .ArrayAccess,
193 .Identifier,
194 .GroupedExpression,
195 .OrElse,
250 // These can be assigned to.
251 .unwrap_optional,
252 .deref,
253 .field_access,
254 .array_access,
255 .identifier,
256 .grouped_expression,
257 .@"orelse",
196258 => {},
197259 }
198260 return expr(mod, scope, .ref, node);
......@@ -202,154 +264,403 @@ fn lvalExpr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst {
202264/// When `rl` is discard, ptr, inferred_ptr, bitcasted_ptr, or inferred_ptr, the
203265/// result instruction can be used to inspect whether it is isNoReturn() but that is it,
204266/// it must otherwise not be used.
205pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerError!*zir.Inst {
206 switch (node.tag) {
207 .Root => unreachable, // Top-level declaration.
208 .Use => unreachable, // Top-level declaration.
209 .TestDecl => unreachable, // Top-level declaration.
210 .DocComment => unreachable, // Top-level declaration.
211 .VarDecl => unreachable, // Handled in `blockExpr`.
212 .SwitchCase => unreachable, // Handled in `switchExpr`.
213 .SwitchElse => unreachable, // Handled in `switchExpr`.
214 .Range => unreachable, // Handled in `switchExpr`.
215 .Else => unreachable, // Handled explicitly the control flow expression functions.
216 .Payload => unreachable, // Handled explicitly.
217 .PointerPayload => unreachable, // Handled explicitly.
218 .PointerIndexPayload => unreachable, // Handled explicitly.
219 .ErrorTag => unreachable, // Handled explicitly.
220 .FieldInitializer => unreachable, // Handled explicitly.
221 .ContainerField => unreachable, // Handled explicitly.
222
223 .Assign => return rvalueVoid(mod, scope, rl, node, try assign(mod, scope, node.castTag(.Assign).?)),
224 .AssignBitAnd => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitAnd).?, .bit_and)),
225 .AssignBitOr => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitOr).?, .bit_or)),
226 .AssignBitShiftLeft => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitShiftLeft).?, .shl)),
227 .AssignBitShiftRight => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitShiftRight).?, .shr)),
228 .AssignBitXor => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitXor).?, .xor)),
229 .AssignDiv => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignDiv).?, .div)),
230 .AssignSub => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignSub).?, .sub)),
231 .AssignSubWrap => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignSubWrap).?, .subwrap)),
232 .AssignMod => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignMod).?, .mod_rem)),
233 .AssignAdd => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignAdd).?, .add)),
234 .AssignAddWrap => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignAddWrap).?, .addwrap)),
235 .AssignMul => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignMul).?, .mul)),
236 .AssignMulWrap => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignMulWrap).?, .mulwrap)),
237
238 .Add => return simpleBinOp(mod, scope, rl, node.castTag(.Add).?, .add),
239 .AddWrap => return simpleBinOp(mod, scope, rl, node.castTag(.AddWrap).?, .addwrap),
240 .Sub => return simpleBinOp(mod, scope, rl, node.castTag(.Sub).?, .sub),
241 .SubWrap => return simpleBinOp(mod, scope, rl, node.castTag(.SubWrap).?, .subwrap),
242 .Mul => return simpleBinOp(mod, scope, rl, node.castTag(.Mul).?, .mul),
243 .MulWrap => return simpleBinOp(mod, scope, rl, node.castTag(.MulWrap).?, .mulwrap),
244 .Div => return simpleBinOp(mod, scope, rl, node.castTag(.Div).?, .div),
245 .Mod => return simpleBinOp(mod, scope, rl, node.castTag(.Mod).?, .mod_rem),
246 .BitAnd => return simpleBinOp(mod, scope, rl, node.castTag(.BitAnd).?, .bit_and),
247 .BitOr => return simpleBinOp(mod, scope, rl, node.castTag(.BitOr).?, .bit_or),
248 .BitShiftLeft => return simpleBinOp(mod, scope, rl, node.castTag(.BitShiftLeft).?, .shl),
249 .BitShiftRight => return simpleBinOp(mod, scope, rl, node.castTag(.BitShiftRight).?, .shr),
250 .BitXor => return simpleBinOp(mod, scope, rl, node.castTag(.BitXor).?, .xor),
251
252 .BangEqual => return simpleBinOp(mod, scope, rl, node.castTag(.BangEqual).?, .cmp_neq),
253 .EqualEqual => return simpleBinOp(mod, scope, rl, node.castTag(.EqualEqual).?, .cmp_eq),
254 .GreaterThan => return simpleBinOp(mod, scope, rl, node.castTag(.GreaterThan).?, .cmp_gt),
255 .GreaterOrEqual => return simpleBinOp(mod, scope, rl, node.castTag(.GreaterOrEqual).?, .cmp_gte),
256 .LessThan => return simpleBinOp(mod, scope, rl, node.castTag(.LessThan).?, .cmp_lt),
257 .LessOrEqual => return simpleBinOp(mod, scope, rl, node.castTag(.LessOrEqual).?, .cmp_lte),
258
259 .ArrayCat => return simpleBinOp(mod, scope, rl, node.castTag(.ArrayCat).?, .array_cat),
260 .ArrayMult => return simpleBinOp(mod, scope, rl, node.castTag(.ArrayMult).?, .array_mul),
261
262 .BoolAnd => return boolBinOp(mod, scope, rl, node.castTag(.BoolAnd).?),
263 .BoolOr => return boolBinOp(mod, scope, rl, node.castTag(.BoolOr).?),
264
265 .BoolNot => return rvalue(mod, scope, rl, try boolNot(mod, scope, node.castTag(.BoolNot).?)),
266 .BitNot => return rvalue(mod, scope, rl, try bitNot(mod, scope, node.castTag(.BitNot).?)),
267 .Negation => return rvalue(mod, scope, rl, try negation(mod, scope, node.castTag(.Negation).?, .sub)),
268 .NegationWrap => return rvalue(mod, scope, rl, try negation(mod, scope, node.castTag(.NegationWrap).?, .subwrap)),
269
270 .Identifier => return try identifier(mod, scope, rl, node.castTag(.Identifier).?),
271 .Asm => return rvalue(mod, scope, rl, try assembly(mod, scope, node.castTag(.Asm).?)),
272 .StringLiteral => return rvalue(mod, scope, rl, try stringLiteral(mod, scope, node.castTag(.StringLiteral).?)),
273 .IntegerLiteral => return rvalue(mod, scope, rl, try integerLiteral(mod, scope, node.castTag(.IntegerLiteral).?)),
274 .BuiltinCall => return builtinCall(mod, scope, rl, node.castTag(.BuiltinCall).?),
275 .Call => return callExpr(mod, scope, rl, node.castTag(.Call).?),
276 .Unreachable => return unreach(mod, scope, node.castTag(.Unreachable).?),
277 .Return => return ret(mod, scope, node.castTag(.Return).?),
278 .If => return ifExpr(mod, scope, rl, node.castTag(.If).?),
279 .While => return whileExpr(mod, scope, rl, node.castTag(.While).?),
280 .Period => return field(mod, scope, rl, node.castTag(.Period).?),
281 .Deref => return rvalue(mod, scope, rl, try deref(mod, scope, node.castTag(.Deref).?)),
282 .AddressOf => return rvalue(mod, scope, rl, try addressOf(mod, scope, node.castTag(.AddressOf).?)),
283 .FloatLiteral => return rvalue(mod, scope, rl, try floatLiteral(mod, scope, node.castTag(.FloatLiteral).?)),
284 .UndefinedLiteral => return rvalue(mod, scope, rl, try undefLiteral(mod, scope, node.castTag(.UndefinedLiteral).?)),
285 .BoolLiteral => return rvalue(mod, scope, rl, try boolLiteral(mod, scope, node.castTag(.BoolLiteral).?)),
286 .NullLiteral => return rvalue(mod, scope, rl, try nullLiteral(mod, scope, node.castTag(.NullLiteral).?)),
287 .OptionalType => return rvalue(mod, scope, rl, try optionalType(mod, scope, node.castTag(.OptionalType).?)),
288 .UnwrapOptional => return unwrapOptional(mod, scope, rl, node.castTag(.UnwrapOptional).?),
289 .Block => return rvalueVoid(mod, scope, rl, node, try blockExpr(mod, scope, node.castTag(.Block).?)),
290 .LabeledBlock => return labeledBlockExpr(mod, scope, rl, node.castTag(.LabeledBlock).?, .block),
291 .Break => return rvalue(mod, scope, rl, try breakExpr(mod, scope, node.castTag(.Break).?)),
292 .Continue => return rvalue(mod, scope, rl, try continueExpr(mod, scope, node.castTag(.Continue).?)),
293 .PtrType => return rvalue(mod, scope, rl, try ptrType(mod, scope, node.castTag(.PtrType).?)),
294 .GroupedExpression => return expr(mod, scope, rl, node.castTag(.GroupedExpression).?.expr),
295 .ArrayType => return rvalue(mod, scope, rl, try arrayType(mod, scope, node.castTag(.ArrayType).?)),
296 .ArrayTypeSentinel => return rvalue(mod, scope, rl, try arrayTypeSentinel(mod, scope, node.castTag(.ArrayTypeSentinel).?)),
297 .EnumLiteral => return rvalue(mod, scope, rl, try enumLiteral(mod, scope, node.castTag(.EnumLiteral).?)),
298 .MultilineStringLiteral => return rvalue(mod, scope, rl, try multilineStrLiteral(mod, scope, node.castTag(.MultilineStringLiteral).?)),
299 .CharLiteral => return rvalue(mod, scope, rl, try charLiteral(mod, scope, node.castTag(.CharLiteral).?)),
300 .SliceType => return rvalue(mod, scope, rl, try sliceType(mod, scope, node.castTag(.SliceType).?)),
301 .ErrorUnion => return rvalue(mod, scope, rl, try typeInixOp(mod, scope, node.castTag(.ErrorUnion).?, .error_union_type)),
302 .MergeErrorSets => return rvalue(mod, scope, rl, try typeInixOp(mod, scope, node.castTag(.MergeErrorSets).?, .merge_error_sets)),
303 .AnyFrameType => return rvalue(mod, scope, rl, try anyFrameType(mod, scope, node.castTag(.AnyFrameType).?)),
304 .ErrorSetDecl => return rvalue(mod, scope, rl, try errorSetDecl(mod, scope, node.castTag(.ErrorSetDecl).?)),
305 .ErrorType => return rvalue(mod, scope, rl, try errorType(mod, scope, node.castTag(.ErrorType).?)),
306 .For => return forExpr(mod, scope, rl, node.castTag(.For).?),
307 .ArrayAccess => return arrayAccess(mod, scope, rl, node.castTag(.ArrayAccess).?),
308 .Slice => return rvalue(mod, scope, rl, try sliceExpr(mod, scope, node.castTag(.Slice).?)),
309 .Catch => return catchExpr(mod, scope, rl, node.castTag(.Catch).?),
310 .Comptime => return comptimeKeyword(mod, scope, rl, node.castTag(.Comptime).?),
311 .OrElse => return orelseExpr(mod, scope, rl, node.castTag(.OrElse).?),
312 .Switch => return switchExpr(mod, scope, rl, node.castTag(.Switch).?),
313 .ContainerDecl => return containerDecl(mod, scope, rl, node.castTag(.ContainerDecl).?),
314
315 .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}),
316 .Await => return mod.failNode(scope, node, "TODO implement astgen.expr for .Await", .{}),
317 .Resume => return mod.failNode(scope, node, "TODO implement astgen.expr for .Resume", .{}),
318 .Try => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}),
319 .ArrayInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializer", .{}),
320 .ArrayInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializerDot", .{}),
321 .StructInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializer", .{}),
322 .StructInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializerDot", .{}),
323 .Suspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Suspend", .{}),
324 .AnyType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyType", .{}),
325 .FnProto => return mod.failNode(scope, node, "TODO implement astgen.expr for .FnProto", .{}),
326 .Nosuspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Nosuspend", .{}),
327 }
328}
329
330fn comptimeKeyword(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Comptime) InnerError!*zir.Inst {
331 const tracy = trace(@src());
332 defer tracy.end();
267pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!*zir.Inst {
268 const tree = scope.tree();
269 const main_tokens = tree.nodes.items(.main_token);
270 const token_tags = tree.tokens.items(.tag);
271 const node_datas = tree.nodes.items(.data);
272 const node_tags = tree.nodes.items(.tag);
273 const token_starts = tree.tokens.items(.start);
274
275 switch (node_tags[node]) {
276 .root => unreachable, // Top-level declaration.
277 .@"usingnamespace" => unreachable, // Top-level declaration.
278 .test_decl => unreachable, // Top-level declaration.
279 .container_field_init => unreachable, // Top-level declaration.
280 .container_field_align => unreachable, // Top-level declaration.
281 .container_field => unreachable, // Top-level declaration.
282 .fn_decl => unreachable, // Top-level declaration.
283
284 .global_var_decl => unreachable, // Handled in `blockExpr`.
285 .local_var_decl => unreachable, // Handled in `blockExpr`.
286 .simple_var_decl => unreachable, // Handled in `blockExpr`.
287 .aligned_var_decl => unreachable, // Handled in `blockExpr`.
288
289 .switch_case => unreachable, // Handled in `switchExpr`.
290 .switch_case_one => unreachable, // Handled in `switchExpr`.
291 .switch_range => unreachable, // Handled in `switchExpr`.
292
293 .asm_output => unreachable, // Handled in `asmExpr`.
294 .asm_input => unreachable, // Handled in `asmExpr`.
295
296 .assign => return rvalueVoid(mod, scope, rl, node, try assign(mod, scope, node)),
297 .assign_bit_and => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .bit_and)),
298 .assign_bit_or => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .bit_or)),
299 .assign_bit_shift_left => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .shl)),
300 .assign_bit_shift_right => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .shr)),
301 .assign_bit_xor => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .xor)),
302 .assign_div => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .div)),
303 .assign_sub => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .sub)),
304 .assign_sub_wrap => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .subwrap)),
305 .assign_mod => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .mod_rem)),
306 .assign_add => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .add)),
307 .assign_add_wrap => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .addwrap)),
308 .assign_mul => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .mul)),
309 .assign_mul_wrap => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .mulwrap)),
310
311 .add => return simpleBinOp(mod, scope, rl, node, .add),
312 .add_wrap => return simpleBinOp(mod, scope, rl, node, .addwrap),
313 .sub => return simpleBinOp(mod, scope, rl, node, .sub),
314 .sub_wrap => return simpleBinOp(mod, scope, rl, node, .subwrap),
315 .mul => return simpleBinOp(mod, scope, rl, node, .mul),
316 .mul_wrap => return simpleBinOp(mod, scope, rl, node, .mulwrap),
317 .div => return simpleBinOp(mod, scope, rl, node, .div),
318 .mod => return simpleBinOp(mod, scope, rl, node, .mod_rem),
319 .bit_and => return simpleBinOp(mod, scope, rl, node, .bit_and),
320 .bit_or => return simpleBinOp(mod, scope, rl, node, .bit_or),
321 .bit_shift_left => return simpleBinOp(mod, scope, rl, node, .shl),
322 .bit_shift_right => return simpleBinOp(mod, scope, rl, node, .shr),
323 .bit_xor => return simpleBinOp(mod, scope, rl, node, .xor),
324
325 .bang_equal => return simpleBinOp(mod, scope, rl, node, .cmp_neq),
326 .equal_equal => return simpleBinOp(mod, scope, rl, node, .cmp_eq),
327 .greater_than => return simpleBinOp(mod, scope, rl, node, .cmp_gt),
328 .greater_or_equal => return simpleBinOp(mod, scope, rl, node, .cmp_gte),
329 .less_than => return simpleBinOp(mod, scope, rl, node, .cmp_lt),
330 .less_or_equal => return simpleBinOp(mod, scope, rl, node, .cmp_lte),
331
332 .array_cat => return simpleBinOp(mod, scope, rl, node, .array_cat),
333 .array_mult => return simpleBinOp(mod, scope, rl, node, .array_mul),
334
335 .bool_and => return boolBinOp(mod, scope, rl, node, true),
336 .bool_or => return boolBinOp(mod, scope, rl, node, false),
337
338 .bool_not => return rvalue(mod, scope, rl, try boolNot(mod, scope, node)),
339 .bit_not => return rvalue(mod, scope, rl, try bitNot(mod, scope, node)),
340 .negation => return rvalue(mod, scope, rl, try negation(mod, scope, node, .sub)),
341 .negation_wrap => return rvalue(mod, scope, rl, try negation(mod, scope, node, .subwrap)),
342
343 .identifier => return identifier(mod, scope, rl, node),
344
345 .asm_simple => return asmExpr(mod, scope, rl, tree.asmSimple(node)),
346 .@"asm" => return asmExpr(mod, scope, rl, tree.asmFull(node)),
347
348 .string_literal => return stringLiteral(mod, scope, rl, node),
349 .multiline_string_literal => return multilineStringLiteral(mod, scope, rl, node),
350
351 .integer_literal => return integerLiteral(mod, scope, rl, node),
352
353 .builtin_call_two, .builtin_call_two_comma => {
354 if (node_datas[node].lhs == 0) {
355 const params = [_]ast.Node.Index{};
356 return builtinCall(mod, scope, rl, node, &params);
357 } else if (node_datas[node].rhs == 0) {
358 const params = [_]ast.Node.Index{node_datas[node].lhs};
359 return builtinCall(mod, scope, rl, node, &params);
360 } else {
361 const params = [_]ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs };
362 return builtinCall(mod, scope, rl, node, &params);
363 }
364 },
365 .builtin_call, .builtin_call_comma => {
366 const params = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
367 return builtinCall(mod, scope, rl, node, params);
368 },
369
370 .call_one, .call_one_comma, .async_call_one, .async_call_one_comma => {
371 var params: [1]ast.Node.Index = undefined;
372 return callExpr(mod, scope, rl, tree.callOne(&params, node));
373 },
374 .call, .call_comma, .async_call, .async_call_comma => {
375 return callExpr(mod, scope, rl, tree.callFull(node));
376 },
377
378 .unreachable_literal => {
379 const main_token = main_tokens[node];
380 const src = token_starts[main_token];
381 return addZIRNoOp(mod, scope, src, .unreachable_safe);
382 },
383 .@"return" => return ret(mod, scope, node),
384 .field_access => return fieldAccess(mod, scope, rl, node),
385 .float_literal => return floatLiteral(mod, scope, rl, node),
386
387 .if_simple => return ifExpr(mod, scope, rl, tree.ifSimple(node)),
388 .@"if" => return ifExpr(mod, scope, rl, tree.ifFull(node)),
389
390 .while_simple => return whileExpr(mod, scope, rl, tree.whileSimple(node)),
391 .while_cont => return whileExpr(mod, scope, rl, tree.whileCont(node)),
392 .@"while" => return whileExpr(mod, scope, rl, tree.whileFull(node)),
393
394 .for_simple => return forExpr(mod, scope, rl, tree.forSimple(node)),
395 .@"for" => return forExpr(mod, scope, rl, tree.forFull(node)),
396
397 // TODO handling these separately would actually be simpler & have fewer branches
398 // once we have a ZIR instruction for each of these 3 cases.
399 .slice_open => return sliceExpr(mod, scope, rl, tree.sliceOpen(node)),
400 .slice => return sliceExpr(mod, scope, rl, tree.slice(node)),
401 .slice_sentinel => return sliceExpr(mod, scope, rl, tree.sliceSentinel(node)),
402
403 .deref => {
404 const lhs = try expr(mod, scope, .none, node_datas[node].lhs);
405 const src = token_starts[main_tokens[node]];
406 const result = try addZIRUnOp(mod, scope, src, .deref, lhs);
407 return rvalue(mod, scope, rl, result);
408 },
409 .address_of => {
410 const result = try expr(mod, scope, .ref, node_datas[node].lhs);
411 return rvalue(mod, scope, rl, result);
412 },
413 .undefined_literal => {
414 const main_token = main_tokens[node];
415 const src = token_starts[main_token];
416 const result = try addZIRInstConst(mod, scope, src, .{
417 .ty = Type.initTag(.@"undefined"),
418 .val = Value.initTag(.undef),
419 });
420 return rvalue(mod, scope, rl, result);
421 },
422 .true_literal => {
423 const main_token = main_tokens[node];
424 const src = token_starts[main_token];
425 const result = try addZIRInstConst(mod, scope, src, .{
426 .ty = Type.initTag(.bool),
427 .val = Value.initTag(.bool_true),
428 });
429 return rvalue(mod, scope, rl, result);
430 },
431 .false_literal => {
432 const main_token = main_tokens[node];
433 const src = token_starts[main_token];
434 const result = try addZIRInstConst(mod, scope, src, .{
435 .ty = Type.initTag(.bool),
436 .val = Value.initTag(.bool_false),
437 });
438 return rvalue(mod, scope, rl, result);
439 },
440 .null_literal => {
441 const main_token = main_tokens[node];
442 const src = token_starts[main_token];
443 const result = try addZIRInstConst(mod, scope, src, .{
444 .ty = Type.initTag(.@"null"),
445 .val = Value.initTag(.null_value),
446 });
447 return rvalue(mod, scope, rl, result);
448 },
449 .optional_type => {
450 const src = token_starts[main_tokens[node]];
451 const operand = try typeExpr(mod, scope, node_datas[node].lhs);
452 const result = try addZIRUnOp(mod, scope, src, .optional_type, operand);
453 return rvalue(mod, scope, rl, result);
454 },
455 .unwrap_optional => {
456 const operand = try expr(mod, scope, rl, node_datas[node].lhs);
457 const op: zir.Inst.Tag = switch (rl) {
458 .ref => .optional_payload_safe_ptr,
459 else => .optional_payload_safe,
460 };
461 const src = token_starts[main_tokens[node]];
462 return addZIRUnOp(mod, scope, src, op, operand);
463 },
464 .block_two, .block_two_semicolon => {
465 const statements = [2]ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs };
466 if (node_datas[node].lhs == 0) {
467 return blockExpr(mod, scope, rl, node, statements[0..0]);
468 } else if (node_datas[node].rhs == 0) {
469 return blockExpr(mod, scope, rl, node, statements[0..1]);
470 } else {
471 return blockExpr(mod, scope, rl, node, statements[0..2]);
472 }
473 },
474 .block, .block_semicolon => {
475 const statements = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
476 return blockExpr(mod, scope, rl, node, statements);
477 },
478 .enum_literal => {
479 const ident_token = main_tokens[node];
480 const name = try mod.identifierTokenString(scope, ident_token);
481 const src = token_starts[ident_token];
482 const result = try addZIRInst(mod, scope, src, zir.Inst.EnumLiteral, .{ .name = name }, .{});
483 return rvalue(mod, scope, rl, result);
484 },
485 .error_union => {
486 const error_set = try typeExpr(mod, scope, node_datas[node].lhs);
487 const payload = try typeExpr(mod, scope, node_datas[node].rhs);
488 const src = token_starts[main_tokens[node]];
489 const result = try addZIRBinOp(mod, scope, src, .error_union_type, error_set, payload);
490 return rvalue(mod, scope, rl, result);
491 },
492 .merge_error_sets => {
493 const lhs = try typeExpr(mod, scope, node_datas[node].lhs);
494 const rhs = try typeExpr(mod, scope, node_datas[node].rhs);
495 const src = token_starts[main_tokens[node]];
496 const result = try addZIRBinOp(mod, scope, src, .merge_error_sets, lhs, rhs);
497 return rvalue(mod, scope, rl, result);
498 },
499 .anyframe_literal => {
500 const main_token = main_tokens[node];
501 const src = token_starts[main_token];
502 const result = try addZIRInstConst(mod, scope, src, .{
503 .ty = Type.initTag(.type),
504 .val = Value.initTag(.anyframe_type),
505 });
506 return rvalue(mod, scope, rl, result);
507 },
508 .anyframe_type => {
509 const src = token_starts[node_datas[node].lhs];
510 const return_type = try typeExpr(mod, scope, node_datas[node].rhs);
511 const result = try addZIRUnOp(mod, scope, src, .anyframe_type, return_type);
512 return rvalue(mod, scope, rl, result);
513 },
514 .@"catch" => {
515 const catch_token = main_tokens[node];
516 const payload_token: ?ast.TokenIndex = if (token_tags[catch_token + 1] == .pipe)
517 catch_token + 2
518 else
519 null;
520 switch (rl) {
521 .ref => return orelseCatchExpr(
522 mod,
523 scope,
524 rl,
525 node_datas[node].lhs,
526 main_tokens[node],
527 .is_err_ptr,
528 .err_union_payload_unsafe_ptr,
529 .err_union_code_ptr,
530 node_datas[node].rhs,
531 payload_token,
532 ),
533 else => return orelseCatchExpr(
534 mod,
535 scope,
536 rl,
537 node_datas[node].lhs,
538 main_tokens[node],
539 .is_err,
540 .err_union_payload_unsafe,
541 .err_union_code,
542 node_datas[node].rhs,
543 payload_token,
544 ),
545 }
546 },
547 .@"orelse" => switch (rl) {
548 .ref => return orelseCatchExpr(
549 mod,
550 scope,
551 rl,
552 node_datas[node].lhs,
553 main_tokens[node],
554 .is_null_ptr,
555 .optional_payload_unsafe_ptr,
556 undefined,
557 node_datas[node].rhs,
558 null,
559 ),
560 else => return orelseCatchExpr(
561 mod,
562 scope,
563 rl,
564 node_datas[node].lhs,
565 main_tokens[node],
566 .is_null,
567 .optional_payload_unsafe,
568 undefined,
569 node_datas[node].rhs,
570 null,
571 ),
572 },
333573
334 return comptimeExpr(mod, scope, rl, node.expr);
574 .ptr_type_aligned => return ptrType(mod, scope, rl, tree.ptrTypeAligned(node)),
575 .ptr_type_sentinel => return ptrType(mod, scope, rl, tree.ptrTypeSentinel(node)),
576 .ptr_type => return ptrType(mod, scope, rl, tree.ptrType(node)),
577 .ptr_type_bit_range => return ptrType(mod, scope, rl, tree.ptrTypeBitRange(node)),
578
579 .container_decl,
580 .container_decl_trailing,
581 => return containerDecl(mod, scope, rl, tree.containerDecl(node)),
582 .container_decl_two, .container_decl_two_trailing => {
583 var buffer: [2]ast.Node.Index = undefined;
584 return containerDecl(mod, scope, rl, tree.containerDeclTwo(&buffer, node));
585 },
586 .container_decl_arg,
587 .container_decl_arg_trailing,
588 => return containerDecl(mod, scope, rl, tree.containerDeclArg(node)),
589
590 .tagged_union,
591 .tagged_union_trailing,
592 => return containerDecl(mod, scope, rl, tree.taggedUnion(node)),
593 .tagged_union_two, .tagged_union_two_trailing => {
594 var buffer: [2]ast.Node.Index = undefined;
595 return containerDecl(mod, scope, rl, tree.taggedUnionTwo(&buffer, node));
596 },
597 .tagged_union_enum_tag,
598 .tagged_union_enum_tag_trailing,
599 => return containerDecl(mod, scope, rl, tree.taggedUnionEnumTag(node)),
600
601 .@"break" => return breakExpr(mod, scope, rl, node),
602 .@"continue" => return continueExpr(mod, scope, rl, node),
603 .grouped_expression => return expr(mod, scope, rl, node_datas[node].lhs),
604 .array_type => return arrayType(mod, scope, rl, node),
605 .array_type_sentinel => return arrayTypeSentinel(mod, scope, rl, node),
606 .char_literal => return charLiteral(mod, scope, rl, node),
607 .error_set_decl => return errorSetDecl(mod, scope, rl, node),
608 .array_access => return arrayAccess(mod, scope, rl, node),
609 .@"comptime" => return comptimeExpr(mod, scope, rl, node_datas[node].lhs),
610 .@"switch", .switch_comma => return switchExpr(mod, scope, rl, node),
611
612 .@"defer" => return mod.failNode(scope, node, "TODO implement astgen.expr for .defer", .{}),
613 .@"errdefer" => return mod.failNode(scope, node, "TODO implement astgen.expr for .errdefer", .{}),
614 .@"await" => return mod.failNode(scope, node, "TODO implement astgen.expr for .await", .{}),
615 .@"resume" => return mod.failNode(scope, node, "TODO implement astgen.expr for .resume", .{}),
616 .@"try" => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}),
617
618 .array_init_one,
619 .array_init_one_comma,
620 .array_init_dot_two,
621 .array_init_dot_two_comma,
622 .array_init_dot,
623 .array_init_dot_comma,
624 .array_init,
625 .array_init_comma,
626 => return mod.failNode(scope, node, "TODO implement astgen.expr for array literals", .{}),
627
628 .struct_init_one,
629 .struct_init_one_comma,
630 .struct_init_dot_two,
631 .struct_init_dot_two_comma,
632 .struct_init_dot,
633 .struct_init_dot_comma,
634 .struct_init,
635 .struct_init_comma,
636 => return mod.failNode(scope, node, "TODO implement astgen.expr for struct literals", .{}),
637
638 .@"suspend" => return mod.failNode(scope, node, "TODO implement astgen.expr for .suspend", .{}),
639 .@"anytype" => return mod.failNode(scope, node, "TODO implement astgen.expr for .anytype", .{}),
640 .fn_proto_simple,
641 .fn_proto_multi,
642 .fn_proto_one,
643 .fn_proto,
644 => return mod.failNode(scope, node, "TODO implement astgen.expr for function prototypes", .{}),
645
646 .@"nosuspend" => return mod.failNode(scope, node, "TODO implement astgen.expr for .nosuspend", .{}),
647 .error_value => return mod.failNode(scope, node, "TODO implement astgen.expr for .error_value", .{}),
648 }
335649}
336650
337651pub fn comptimeExpr(
338652 mod: *Module,
339653 parent_scope: *Scope,
340654 rl: ResultLoc,
341 node: *ast.Node,
655 node: ast.Node.Index,
342656) InnerError!*zir.Inst {
343657 // If we are already in a comptime scope, no need to make another one.
344658 if (parent_scope.isComptime()) {
345659 return expr(mod, parent_scope, rl, node);
346660 }
347661
348 // Optimization for labeled blocks: don't need to have 2 layers of blocks,
349 // we can reuse the existing one.
350 if (node.castTag(.LabeledBlock)) |block_node| {
351 return labeledBlockExpr(mod, parent_scope, rl, block_node, .block_comptime);
352 }
662 const tree = parent_scope.tree();
663 const token_starts = tree.tokens.items(.start);
353664
354665 // Make a scope to collect generated instructions in the sub-expression.
355666 var block_scope: Scope.GenZIR = .{
......@@ -365,9 +676,7 @@ pub fn comptimeExpr(
365676 // instruction is the block's result value.
366677 _ = try expr(mod, &block_scope.base, rl, node);
367678
368 const tree = parent_scope.tree();
369 const src = tree.token_locs[node.firstToken()].start;
370
679 const src = token_starts[tree.firstToken(node)];
371680 const block = try addZIRInstBlock(mod, parent_scope, src, .block_comptime_flat, .{
372681 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
373682 });
......@@ -378,10 +687,17 @@ pub fn comptimeExpr(
378687fn breakExpr(
379688 mod: *Module,
380689 parent_scope: *Scope,
381 node: *ast.Node.ControlFlowExpression,
690 rl: ResultLoc,
691 node: ast.Node.Index,
382692) InnerError!*zir.Inst {
383693 const tree = parent_scope.tree();
384 const src = tree.token_locs[node.ltoken].start;
694 const node_datas = tree.nodes.items(.data);
695 const main_tokens = tree.nodes.items(.main_token);
696 const token_starts = tree.tokens.items(.start);
697
698 const src = token_starts[main_tokens[node]];
699 const break_label = node_datas[node].lhs;
700 const rhs = node_datas[node].rhs;
385701
386702 // Look for the label in the scope.
387703 var scope = parent_scope;
......@@ -391,7 +707,7 @@ fn breakExpr(
391707 const gen_zir = scope.cast(Scope.GenZIR).?;
392708
393709 const block_inst = blk: {
394 if (node.getLabel()) |break_label| {
710 if (break_label != 0) {
395711 if (gen_zir.label) |*label| {
396712 if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) {
397713 label.used = true;
......@@ -405,11 +721,12 @@ fn breakExpr(
405721 continue;
406722 };
407723
408 const rhs = node.getRHS() orelse {
409 return addZirInstTag(mod, parent_scope, src, .break_void, .{
724 if (rhs == 0) {
725 const result = try addZirInstTag(mod, parent_scope, src, .break_void, .{
410726 .block = block_inst,
411727 });
412 };
728 return rvalue(mod, parent_scope, rl, result);
729 }
413730 gen_zir.break_count += 1;
414731 const prev_rvalue_rl_count = gen_zir.rvalue_rl_count;
415732 const operand = try expr(mod, parent_scope, gen_zir.break_result_loc, rhs);
......@@ -429,11 +746,11 @@ fn breakExpr(
429746 try gen_zir.labeled_store_to_block_ptr_list.append(mod.gpa, store_inst);
430747 }
431748 }
432 return br;
749 return rvalue(mod, parent_scope, rl, br);
433750 },
434751 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
435752 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
436 else => if (node.getLabel()) |break_label| {
753 else => if (break_label != 0) {
437754 const label_name = try mod.identifierTokenString(parent_scope, break_label);
438755 return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name});
439756 } else {
......@@ -443,9 +760,19 @@ fn breakExpr(
443760 }
444761}
445762
446fn continueExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst {
763fn continueExpr(
764 mod: *Module,
765 parent_scope: *Scope,
766 rl: ResultLoc,
767 node: ast.Node.Index,
768) InnerError!*zir.Inst {
447769 const tree = parent_scope.tree();
448 const src = tree.token_locs[node.ltoken].start;
770 const node_datas = tree.nodes.items(.data);
771 const main_tokens = tree.nodes.items(.main_token);
772 const token_starts = tree.tokens.items(.start);
773
774 const src = token_starts[main_tokens[node]];
775 const break_label = node_datas[node].lhs;
449776
450777 // Look for the label in the scope.
451778 var scope = parent_scope;
......@@ -457,7 +784,7 @@ fn continueExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowE
457784 scope = gen_zir.parent;
458785 continue;
459786 };
460 if (node.getLabel()) |break_label| blk: {
787 if (break_label != 0) blk: {
461788 if (gen_zir.label) |*label| {
462789 if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) {
463790 label.used = true;
......@@ -469,13 +796,14 @@ fn continueExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowE
469796 continue;
470797 }
471798
472 return addZirInstTag(mod, parent_scope, src, .break_void, .{
799 const result = try addZirInstTag(mod, parent_scope, src, .break_void, .{
473800 .block = continue_block,
474801 });
802 return rvalue(mod, parent_scope, rl, result);
475803 },
476804 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
477805 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
478 else => if (node.getLabel()) |break_label| {
806 else => if (break_label != 0) {
479807 const label_name = try mod.identifierTokenString(parent_scope, break_label);
480808 return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name});
481809 } else {
......@@ -485,11 +813,27 @@ fn continueExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowE
485813 }
486814}
487815
488pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block) InnerError!void {
816pub fn blockExpr(
817 mod: *Module,
818 scope: *Scope,
819 rl: ResultLoc,
820 block_node: ast.Node.Index,
821 statements: []const ast.Node.Index,
822) InnerError!*zir.Inst {
489823 const tracy = trace(@src());
490824 defer tracy.end();
491825
492 try blockExprStmts(mod, parent_scope, &block_node.base, block_node.statements());
826 const tree = scope.tree();
827 const main_tokens = tree.nodes.items(.main_token);
828 const token_tags = tree.tokens.items(.tag);
829
830 const lbrace = main_tokens[block_node];
831 if (token_tags[lbrace - 1] == .colon) {
832 return labeledBlockExpr(mod, scope, rl, block_node, statements, .block);
833 }
834
835 try blockExprStmts(mod, scope, block_node, statements);
836 return rvalueVoid(mod, scope, rl, block_node, {});
493837}
494838
495839fn checkLabelRedefinition(mod: *Module, parent_scope: *Scope, label: ast.TokenIndex) !void {
......@@ -502,8 +846,11 @@ fn checkLabelRedefinition(mod: *Module, parent_scope: *Scope, label: ast.TokenIn
502846 if (gen_zir.label) |prev_label| {
503847 if (try tokenIdentEql(mod, parent_scope, label, prev_label.token)) {
504848 const tree = parent_scope.tree();
505 const label_src = tree.token_locs[label].start;
506 const prev_label_src = tree.token_locs[prev_label.token].start;
849 const main_tokens = tree.nodes.items(.main_token);
850 const token_starts = tree.tokens.items(.start);
851
852 const label_src = token_starts[label];
853 const prev_label_src = token_starts[prev_label.token];
507854
508855 const label_name = try mod.identifierTokenString(parent_scope, label);
509856 const msg = msg: {
......@@ -539,7 +886,8 @@ fn labeledBlockExpr(
539886 mod: *Module,
540887 parent_scope: *Scope,
541888 rl: ResultLoc,
542 block_node: *ast.Node.LabeledBlock,
889 block_node: ast.Node.Index,
890 statements: []const ast.Node.Index,
543891 zir_tag: zir.Inst.Tag,
544892) InnerError!*zir.Inst {
545893 const tracy = trace(@src());
......@@ -548,9 +896,16 @@ fn labeledBlockExpr(
548896 assert(zir_tag == .block or zir_tag == .block_comptime);
549897
550898 const tree = parent_scope.tree();
551 const src = tree.token_locs[block_node.lbrace].start;
899 const main_tokens = tree.nodes.items(.main_token);
900 const token_starts = tree.tokens.items(.start);
901 const token_tags = tree.tokens.items(.tag);
552902
553 try checkLabelRedefinition(mod, parent_scope, block_node.label);
903 const lbrace = main_tokens[block_node];
904 const label_token = lbrace - 2;
905 assert(token_tags[label_token] == .identifier);
906 const src = token_starts[lbrace];
907
908 try checkLabelRedefinition(mod, parent_scope, label_token);
554909
555910 // Create the Block ZIR instruction so that we can put it into the GenZIR struct
556911 // so that break statements can reference it.
......@@ -575,7 +930,7 @@ fn labeledBlockExpr(
575930 .instructions = .{},
576931 // TODO @as here is working around a stage1 miscompilation bug :(
577932 .label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{
578 .token = block_node.label,
933 .token = label_token,
579934 .block_inst = block_inst,
580935 }),
581936 };
......@@ -584,10 +939,10 @@ fn labeledBlockExpr(
584939 defer block_scope.labeled_breaks.deinit(mod.gpa);
585940 defer block_scope.labeled_store_to_block_ptr_list.deinit(mod.gpa);
586941
587 try blockExprStmts(mod, &block_scope.base, &block_node.base, block_node.statements());
942 try blockExprStmts(mod, &block_scope.base, block_node, statements);
588943
589944 if (!block_scope.label.?.used) {
590 return mod.fail(parent_scope, tree.token_locs[block_node.label].start, "unused block label", .{});
945 return mod.failTok(parent_scope, label_token, "unused block label", .{});
591946 }
592947
593948 try gen_zir.instructions.append(mod.gpa, &block_inst.base);
......@@ -627,37 +982,41 @@ fn labeledBlockExpr(
627982fn blockExprStmts(
628983 mod: *Module,
629984 parent_scope: *Scope,
630 node: *ast.Node,
631 statements: []*ast.Node,
985 node: ast.Node.Index,
986 statements: []const ast.Node.Index,
632987) !void {
633988 const tree = parent_scope.tree();
989 const main_tokens = tree.nodes.items(.main_token);
990 const token_starts = tree.tokens.items(.start);
991 const node_tags = tree.nodes.items(.tag);
634992
635993 var block_arena = std.heap.ArenaAllocator.init(mod.gpa);
636994 defer block_arena.deinit();
637995
638996 var scope = parent_scope;
639997 for (statements) |statement| {
640 const src = tree.token_locs[statement.firstToken()].start;
998 const src = token_starts[tree.firstToken(statement)];
641999 _ = try addZIRNoOp(mod, scope, src, .dbg_stmt);
642 switch (statement.tag) {
643 .VarDecl => {
644 const var_decl_node = statement.castTag(.VarDecl).?;
645 scope = try varDecl(mod, scope, var_decl_node, &block_arena.allocator);
646 },
647 .Assign => try assign(mod, scope, statement.castTag(.Assign).?),
648 .AssignBitAnd => try assignOp(mod, scope, statement.castTag(.AssignBitAnd).?, .bit_and),
649 .AssignBitOr => try assignOp(mod, scope, statement.castTag(.AssignBitOr).?, .bit_or),
650 .AssignBitShiftLeft => try assignOp(mod, scope, statement.castTag(.AssignBitShiftLeft).?, .shl),
651 .AssignBitShiftRight => try assignOp(mod, scope, statement.castTag(.AssignBitShiftRight).?, .shr),
652 .AssignBitXor => try assignOp(mod, scope, statement.castTag(.AssignBitXor).?, .xor),
653 .AssignDiv => try assignOp(mod, scope, statement.castTag(.AssignDiv).?, .div),
654 .AssignSub => try assignOp(mod, scope, statement.castTag(.AssignSub).?, .sub),
655 .AssignSubWrap => try assignOp(mod, scope, statement.castTag(.AssignSubWrap).?, .subwrap),
656 .AssignMod => try assignOp(mod, scope, statement.castTag(.AssignMod).?, .mod_rem),
657 .AssignAdd => try assignOp(mod, scope, statement.castTag(.AssignAdd).?, .add),
658 .AssignAddWrap => try assignOp(mod, scope, statement.castTag(.AssignAddWrap).?, .addwrap),
659 .AssignMul => try assignOp(mod, scope, statement.castTag(.AssignMul).?, .mul),
660 .AssignMulWrap => try assignOp(mod, scope, statement.castTag(.AssignMulWrap).?, .mulwrap),
1000 switch (node_tags[statement]) {
1001 .global_var_decl => scope = try varDecl(mod, scope, &block_arena.allocator, tree.globalVarDecl(statement)),
1002 .local_var_decl => scope = try varDecl(mod, scope, &block_arena.allocator, tree.localVarDecl(statement)),
1003 .simple_var_decl => scope = try varDecl(mod, scope, &block_arena.allocator, tree.simpleVarDecl(statement)),
1004 .aligned_var_decl => scope = try varDecl(mod, scope, &block_arena.allocator, tree.alignedVarDecl(statement)),
1005
1006 .assign => try assign(mod, scope, statement),
1007 .assign_bit_and => try assignOp(mod, scope, statement, .bit_and),
1008 .assign_bit_or => try assignOp(mod, scope, statement, .bit_or),
1009 .assign_bit_shift_left => try assignOp(mod, scope, statement, .shl),
1010 .assign_bit_shift_right => try assignOp(mod, scope, statement, .shr),
1011 .assign_bit_xor => try assignOp(mod, scope, statement, .xor),
1012 .assign_div => try assignOp(mod, scope, statement, .div),
1013 .assign_sub => try assignOp(mod, scope, statement, .sub),
1014 .assign_sub_wrap => try assignOp(mod, scope, statement, .subwrap),
1015 .assign_mod => try assignOp(mod, scope, statement, .mod_rem),
1016 .assign_add => try assignOp(mod, scope, statement, .add),
1017 .assign_add_wrap => try assignOp(mod, scope, statement, .addwrap),
1018 .assign_mul => try assignOp(mod, scope, statement, .mul),
1019 .assign_mul_wrap => try assignOp(mod, scope, statement, .mulwrap),
6611020
6621021 else => {
6631022 const possibly_unused_result = try expr(mod, scope, .none, statement);
......@@ -672,18 +1031,23 @@ fn blockExprStmts(
6721031fn varDecl(
6731032 mod: *Module,
6741033 scope: *Scope,
675 node: *ast.Node.VarDecl,
6761034 block_arena: *Allocator,
1035 var_decl: ast.full.VarDecl,
6771036) InnerError!*Scope {
678 if (node.getComptimeToken()) |comptime_token| {
1037 if (var_decl.comptime_token) |comptime_token| {
6791038 return mod.failTok(scope, comptime_token, "TODO implement comptime locals", .{});
6801039 }
681 if (node.getAlignNode()) |align_node| {
682 return mod.failNode(scope, align_node, "TODO implement alignment on locals", .{});
1040 if (var_decl.ast.align_node != 0) {
1041 return mod.failNode(scope, var_decl.ast.align_node, "TODO implement alignment on locals", .{});
6831042 }
6841043 const tree = scope.tree();
685 const name_src = tree.token_locs[node.name_token].start;
686 const ident_name = try mod.identifierTokenString(scope, node.name_token);
1044 const main_tokens = tree.nodes.items(.main_token);
1045 const token_starts = tree.tokens.items(.start);
1046 const token_tags = tree.tokens.items(.tag);
1047
1048 const name_token = var_decl.ast.mut_token + 1;
1049 const name_src = token_starts[name_token];
1050 const ident_name = try mod.identifierTokenString(scope, name_token);
6871051
6881052 // Local variables shadowing detection, including function parameters.
6891053 {
......@@ -729,20 +1093,21 @@ fn varDecl(
7291093 // TODO add note for other definition
7301094 return mod.fail(scope, name_src, "redefinition of '{s}'", .{ident_name});
7311095 }
732 const init_node = node.getInitNode() orelse
1096 if (var_decl.ast.init_node == 0) {
7331097 return mod.fail(scope, name_src, "variables must be initialized", .{});
1098 }
7341099
735 switch (tree.token_ids[node.mut_token]) {
736 .Keyword_const => {
1100 switch (token_tags[var_decl.ast.mut_token]) {
1101 .keyword_const => {
7371102 // Depending on the type of AST the initialization expression is, we may need an lvalue
7381103 // or an rvalue as a result location. If it is an rvalue, we can use the instruction as
7391104 // the variable, no memory location needed.
740 if (!nodeMayNeedMemoryLocation(init_node, scope)) {
741 const result_loc: ResultLoc = if (node.getTypeNode()) |type_node|
742 .{ .ty = try typeExpr(mod, scope, type_node) }
1105 if (!nodeMayNeedMemoryLocation(scope, var_decl.ast.init_node)) {
1106 const result_loc: ResultLoc = if (var_decl.ast.type_node != 0)
1107 .{ .ty = try typeExpr(mod, scope, var_decl.ast.type_node) }
7431108 else
7441109 .none;
745 const init_inst = try expr(mod, scope, result_loc, init_node);
1110 const init_inst = try expr(mod, scope, result_loc, var_decl.ast.init_node);
7461111 const sub_scope = try block_arena.create(Scope.LocalVal);
7471112 sub_scope.* = .{
7481113 .parent = scope,
......@@ -766,8 +1131,8 @@ fn varDecl(
7661131
7671132 var resolve_inferred_alloc: ?*zir.Inst = null;
7681133 var opt_type_inst: ?*zir.Inst = null;
769 if (node.getTypeNode()) |type_node| {
770 const type_inst = try typeExpr(mod, &init_scope.base, type_node);
1134 if (var_decl.ast.type_node != 0) {
1135 const type_inst = try typeExpr(mod, &init_scope.base, var_decl.ast.type_node);
7711136 opt_type_inst = type_inst;
7721137 init_scope.rl_ptr = try addZIRUnOp(mod, &init_scope.base, name_src, .alloc, type_inst);
7731138 } else {
......@@ -776,7 +1141,7 @@ fn varDecl(
7761141 init_scope.rl_ptr = &alloc.base;
7771142 }
7781143 const init_result_loc: ResultLoc = .{ .block_ptr = &init_scope };
779 const init_inst = try expr(mod, &init_scope.base, init_result_loc, init_node);
1144 const init_inst = try expr(mod, &init_scope.base, init_result_loc, var_decl.ast.init_node);
7801145 const parent_zir = &scope.getGenZIR().instructions;
7811146 if (init_scope.rvalue_rl_count == 1) {
7821147 // Result location pointer not used. We don't need an alloc for this
......@@ -834,10 +1199,13 @@ fn varDecl(
8341199 };
8351200 return &sub_scope.base;
8361201 },
837 .Keyword_var => {
1202 .keyword_var => {
8381203 var resolve_inferred_alloc: ?*zir.Inst = null;
839 const var_data: struct { result_loc: ResultLoc, alloc: *zir.Inst } = if (node.getTypeNode()) |type_node| a: {
840 const type_inst = try typeExpr(mod, scope, type_node);
1204 const var_data: struct {
1205 result_loc: ResultLoc,
1206 alloc: *zir.Inst,
1207 } = if (var_decl.ast.type_node != 0) a: {
1208 const type_inst = try typeExpr(mod, scope, var_decl.ast.type_node);
8411209 const alloc = try addZIRUnOp(mod, scope, name_src, .alloc_mut, type_inst);
8421210 break :a .{ .alloc = alloc, .result_loc = .{ .ptr = alloc } };
8431211 } else a: {
......@@ -845,7 +1213,7 @@ fn varDecl(
8451213 resolve_inferred_alloc = &alloc.base;
8461214 break :a .{ .alloc = &alloc.base, .result_loc = .{ .inferred_ptr = alloc } };
8471215 };
848 const init_inst = try expr(mod, scope, var_data.result_loc, init_node);
1216 const init_inst = try expr(mod, scope, var_data.result_loc, var_decl.ast.init_node);
8491217 if (resolve_inferred_alloc) |inst| {
8501218 _ = try addZIRUnOp(mod, scope, name_src, .resolve_inferred_alloc, inst);
8511219 }
......@@ -862,232 +1230,210 @@ fn varDecl(
8621230 }
8631231}
8641232
865fn assign(mod: *Module, scope: *Scope, infix_node: *ast.Node.SimpleInfixOp) InnerError!void {
866 if (infix_node.lhs.castTag(.Identifier)) |ident| {
867 // This intentionally does not support @"_" syntax.
868 const ident_name = scope.tree().tokenSlice(ident.token);
1233fn assign(mod: *Module, scope: *Scope, infix_node: ast.Node.Index) InnerError!void {
1234 const tree = scope.tree();
1235 const node_datas = tree.nodes.items(.data);
1236 const main_tokens = tree.nodes.items(.main_token);
1237 const node_tags = tree.nodes.items(.tag);
1238
1239 const lhs = node_datas[infix_node].lhs;
1240 const rhs = node_datas[infix_node].rhs;
1241 if (node_tags[lhs] == .identifier) {
1242 // This intentionally does not support `@"_"` syntax.
1243 const ident_name = tree.tokenSlice(main_tokens[lhs]);
8691244 if (mem.eql(u8, ident_name, "_")) {
870 _ = try expr(mod, scope, .discard, infix_node.rhs);
1245 _ = try expr(mod, scope, .discard, rhs);
8711246 return;
8721247 }
8731248 }
874 const lvalue = try lvalExpr(mod, scope, infix_node.lhs);
875 _ = try expr(mod, scope, .{ .ptr = lvalue }, infix_node.rhs);
1249 const lvalue = try lvalExpr(mod, scope, lhs);
1250 _ = try expr(mod, scope, .{ .ptr = lvalue }, rhs);
8761251}
8771252
8781253fn assignOp(
8791254 mod: *Module,
8801255 scope: *Scope,
881 infix_node: *ast.Node.SimpleInfixOp,
1256 infix_node: ast.Node.Index,
8821257 op_inst_tag: zir.Inst.Tag,
8831258) InnerError!void {
884 const lhs_ptr = try lvalExpr(mod, scope, infix_node.lhs);
885 const lhs = try addZIRUnOp(mod, scope, lhs_ptr.src, .deref, lhs_ptr);
886 const lhs_type = try addZIRUnOp(mod, scope, lhs_ptr.src, .typeof, lhs);
887 const rhs = try expr(mod, scope, .{ .ty = lhs_type }, infix_node.rhs);
888
8891259 const tree = scope.tree();
890 const src = tree.token_locs[infix_node.op_token].start;
1260 const node_datas = tree.nodes.items(.data);
1261 const main_tokens = tree.nodes.items(.main_token);
1262 const token_starts = tree.tokens.items(.start);
8911263
1264 const lhs_ptr = try lvalExpr(mod, scope, node_datas[infix_node].lhs);
1265 const lhs = try addZIRUnOp(mod, scope, lhs_ptr.src, .deref, lhs_ptr);
1266 const lhs_type = try addZIRUnOp(mod, scope, lhs_ptr.src, .typeof, lhs);
1267 const rhs = try expr(mod, scope, .{ .ty = lhs_type }, node_datas[infix_node].rhs);
1268 const src = token_starts[main_tokens[infix_node]];
8921269 const result = try addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs);
8931270 _ = try addZIRBinOp(mod, scope, src, .store, lhs_ptr, result);
8941271}
8951272
896fn boolNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
1273fn boolNot(mod: *Module, scope: *Scope, node: ast.Node.Index) InnerError!*zir.Inst {
8971274 const tree = scope.tree();
898 const src = tree.token_locs[node.op_token].start;
1275 const node_datas = tree.nodes.items(.data);
1276 const main_tokens = tree.nodes.items(.main_token);
1277 const token_starts = tree.tokens.items(.start);
1278
1279 const src = token_starts[main_tokens[node]];
8991280 const bool_type = try addZIRInstConst(mod, scope, src, .{
9001281 .ty = Type.initTag(.type),
9011282 .val = Value.initTag(.bool_type),
9021283 });
903 const operand = try expr(mod, scope, .{ .ty = bool_type }, node.rhs);
1284 const operand = try expr(mod, scope, .{ .ty = bool_type }, node_datas[node].lhs);
9041285 return addZIRUnOp(mod, scope, src, .bool_not, operand);
9051286}
9061287
907fn bitNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
1288fn bitNot(mod: *Module, scope: *Scope, node: ast.Node.Index) InnerError!*zir.Inst {
9081289 const tree = scope.tree();
909 const src = tree.token_locs[node.op_token].start;
910 const operand = try expr(mod, scope, .none, node.rhs);
1290 const node_datas = tree.nodes.items(.data);
1291 const main_tokens = tree.nodes.items(.main_token);
1292 const token_starts = tree.tokens.items(.start);
1293
1294 const src = token_starts[main_tokens[node]];
1295 const operand = try expr(mod, scope, .none, node_datas[node].lhs);
9111296 return addZIRUnOp(mod, scope, src, .bit_not, operand);
9121297}
9131298
914fn negation(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp, op_inst_tag: zir.Inst.Tag) InnerError!*zir.Inst {
1299fn negation(
1300 mod: *Module,
1301 scope: *Scope,
1302 node: ast.Node.Index,
1303 op_inst_tag: zir.Inst.Tag,
1304) InnerError!*zir.Inst {
9151305 const tree = scope.tree();
916 const src = tree.token_locs[node.op_token].start;
1306 const node_datas = tree.nodes.items(.data);
1307 const main_tokens = tree.nodes.items(.main_token);
1308 const token_starts = tree.tokens.items(.start);
9171309
1310 const src = token_starts[main_tokens[node]];
9181311 const lhs = try addZIRInstConst(mod, scope, src, .{
9191312 .ty = Type.initTag(.comptime_int),
9201313 .val = Value.initTag(.zero),
9211314 });
922 const rhs = try expr(mod, scope, .none, node.rhs);
923
1315 const rhs = try expr(mod, scope, .none, node_datas[node].lhs);
9241316 return addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs);
9251317}
9261318
927fn addressOf(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
928 return expr(mod, scope, .ref, node.rhs);
929}
930
931fn optionalType(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
932 const tree = scope.tree();
933 const src = tree.token_locs[node.op_token].start;
934 const operand = try typeExpr(mod, scope, node.rhs);
935 return addZIRUnOp(mod, scope, src, .optional_type, operand);
936}
937
938fn sliceType(mod: *Module, scope: *Scope, node: *ast.Node.SliceType) InnerError!*zir.Inst {
1319fn ptrType(
1320 mod: *Module,
1321 scope: *Scope,
1322 rl: ResultLoc,
1323 ptr_info: ast.full.PtrType,
1324) InnerError!*zir.Inst {
9391325 const tree = scope.tree();
940 const src = tree.token_locs[node.op_token].start;
941 return ptrSliceType(mod, scope, src, &node.ptr_info, node.rhs, .Slice);
942}
1326 const token_starts = tree.tokens.items(.start);
9431327
944fn ptrType(mod: *Module, scope: *Scope, node: *ast.Node.PtrType) InnerError!*zir.Inst {
945 const tree = scope.tree();
946 const src = tree.token_locs[node.op_token].start;
947 return ptrSliceType(mod, scope, src, &node.ptr_info, node.rhs, switch (tree.token_ids[node.op_token]) {
948 .Asterisk, .AsteriskAsterisk => .One,
949 // TODO stage1 type inference bug
950 .LBracket => @as(std.builtin.TypeInfo.Pointer.Size, switch (tree.token_ids[node.op_token + 2]) {
951 .Identifier => .C,
952 else => .Many,
953 }),
954 else => unreachable,
955 });
956}
1328 const src = token_starts[ptr_info.ast.main_token];
9571329
958fn ptrSliceType(mod: *Module, scope: *Scope, src: usize, ptr_info: *ast.PtrInfo, rhs: *ast.Node, size: std.builtin.TypeInfo.Pointer.Size) InnerError!*zir.Inst {
9591330 const simple = ptr_info.allowzero_token == null and
960 ptr_info.align_info == null and
1331 ptr_info.ast.align_node == 0 and
9611332 ptr_info.volatile_token == null and
962 ptr_info.sentinel == null;
1333 ptr_info.ast.sentinel == 0;
9631334
9641335 if (simple) {
965 const child_type = try typeExpr(mod, scope, rhs);
1336 const child_type = try typeExpr(mod, scope, ptr_info.ast.child_type);
9661337 const mutable = ptr_info.const_token == null;
967 // TODO stage1 type inference bug
9681338 const T = zir.Inst.Tag;
969 return addZIRUnOp(mod, scope, src, switch (size) {
1339 const result = try addZIRUnOp(mod, scope, src, switch (ptr_info.size) {
9701340 .One => if (mutable) T.single_mut_ptr_type else T.single_const_ptr_type,
9711341 .Many => if (mutable) T.many_mut_ptr_type else T.many_const_ptr_type,
9721342 .C => if (mutable) T.c_mut_ptr_type else T.c_const_ptr_type,
9731343 .Slice => if (mutable) T.mut_slice_type else T.const_slice_type,
9741344 }, child_type);
1345 return rvalue(mod, scope, rl, result);
9751346 }
9761347
9771348 var kw_args: std.meta.fieldInfo(zir.Inst.PtrType, .kw_args).field_type = .{};
978 kw_args.size = size;
1349 kw_args.size = ptr_info.size;
9791350 kw_args.@"allowzero" = ptr_info.allowzero_token != null;
980 if (ptr_info.align_info) |some| {
981 kw_args.@"align" = try expr(mod, scope, .none, some.node);
982 if (some.bit_range) |bit_range| {
983 kw_args.align_bit_start = try expr(mod, scope, .none, bit_range.start);
984 kw_args.align_bit_end = try expr(mod, scope, .none, bit_range.end);
1351 if (ptr_info.ast.align_node != 0) {
1352 kw_args.@"align" = try expr(mod, scope, .none, ptr_info.ast.align_node);
1353 if (ptr_info.ast.bit_range_start != 0) {
1354 kw_args.align_bit_start = try expr(mod, scope, .none, ptr_info.ast.bit_range_start);
1355 kw_args.align_bit_end = try expr(mod, scope, .none, ptr_info.ast.bit_range_end);
9851356 }
9861357 }
9871358 kw_args.mutable = ptr_info.const_token == null;
9881359 kw_args.@"volatile" = ptr_info.volatile_token != null;
989 if (ptr_info.sentinel) |some| {
990 kw_args.sentinel = try expr(mod, scope, .none, some);
991 }
992
993 const child_type = try typeExpr(mod, scope, rhs);
994 if (kw_args.sentinel) |some| {
995 kw_args.sentinel = try addZIRBinOp(mod, scope, some.src, .as, child_type, some);
1360 const child_type = try typeExpr(mod, scope, ptr_info.ast.child_type);
1361 if (ptr_info.ast.sentinel != 0) {
1362 kw_args.sentinel = try expr(mod, scope, .{ .ty = child_type }, ptr_info.ast.sentinel);
9961363 }
997
998 return addZIRInst(mod, scope, src, zir.Inst.PtrType, .{ .child_type = child_type }, kw_args);
1364 const result = try addZIRInst(mod, scope, src, zir.Inst.PtrType, .{ .child_type = child_type }, kw_args);
1365 return rvalue(mod, scope, rl, result);
9991366}
10001367
1001fn arrayType(mod: *Module, scope: *Scope, node: *ast.Node.ArrayType) !*zir.Inst {
1368fn arrayType(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !*zir.Inst {
10021369 const tree = scope.tree();
1003 const src = tree.token_locs[node.op_token].start;
1370 const main_tokens = tree.nodes.items(.main_token);
1371 const node_datas = tree.nodes.items(.data);
1372 const token_starts = tree.tokens.items(.start);
1373
1374 const src = token_starts[main_tokens[node]];
10041375 const usize_type = try addZIRInstConst(mod, scope, src, .{
10051376 .ty = Type.initTag(.type),
10061377 .val = Value.initTag(.usize_type),
10071378 });
1379 const len_node = node_datas[node].lhs;
1380 const elem_node = node_datas[node].rhs;
1381 if (len_node == 0) {
1382 const elem_type = try typeExpr(mod, scope, elem_node);
1383 const result = try addZIRUnOp(mod, scope, src, .mut_slice_type, elem_type);
1384 return rvalue(mod, scope, rl, result);
1385 } else {
1386 // TODO check for [_]T
1387 const len = try expr(mod, scope, .{ .ty = usize_type }, len_node);
1388 const elem_type = try typeExpr(mod, scope, elem_node);
10081389
1009 // TODO check for [_]T
1010 const len = try expr(mod, scope, .{ .ty = usize_type }, node.len_expr);
1011 const elem_type = try typeExpr(mod, scope, node.rhs);
1012
1013 return addZIRBinOp(mod, scope, src, .array_type, len, elem_type);
1390 const result = try addZIRBinOp(mod, scope, src, .array_type, len, elem_type);
1391 return rvalue(mod, scope, rl, result);
1392 }
10141393}
10151394
1016fn arrayTypeSentinel(mod: *Module, scope: *Scope, node: *ast.Node.ArrayTypeSentinel) !*zir.Inst {
1395fn arrayTypeSentinel(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !*zir.Inst {
10171396 const tree = scope.tree();
1018 const src = tree.token_locs[node.op_token].start;
1397 const main_tokens = tree.nodes.items(.main_token);
1398 const token_starts = tree.tokens.items(.start);
1399 const node_datas = tree.nodes.items(.data);
1400
1401 const len_node = node_datas[node].lhs;
1402 const extra = tree.extraData(node_datas[node].rhs, ast.Node.ArrayTypeSentinel);
1403 const src = token_starts[main_tokens[node]];
10191404 const usize_type = try addZIRInstConst(mod, scope, src, .{
10201405 .ty = Type.initTag(.type),
10211406 .val = Value.initTag(.usize_type),
10221407 });
10231408
10241409 // TODO check for [_]T
1025 const len = try expr(mod, scope, .{ .ty = usize_type }, node.len_expr);
1026 const sentinel_uncasted = try expr(mod, scope, .none, node.sentinel);
1027 const elem_type = try typeExpr(mod, scope, node.rhs);
1410 const len = try expr(mod, scope, .{ .ty = usize_type }, len_node);
1411 const sentinel_uncasted = try expr(mod, scope, .none, extra.sentinel);
1412 const elem_type = try typeExpr(mod, scope, extra.elem_type);
10281413 const sentinel = try addZIRBinOp(mod, scope, src, .as, elem_type, sentinel_uncasted);
10291414
1030 return addZIRInst(mod, scope, src, zir.Inst.ArrayTypeSentinel, .{
1415 const result = try addZIRInst(mod, scope, src, zir.Inst.ArrayTypeSentinel, .{
10311416 .len = len,
10321417 .sentinel = sentinel,
10331418 .elem_type = elem_type,
10341419 }, .{});
1035}
1036
1037fn anyFrameType(mod: *Module, scope: *Scope, node: *ast.Node.AnyFrameType) InnerError!*zir.Inst {
1038 const tree = scope.tree();
1039 const src = tree.token_locs[node.anyframe_token].start;
1040 if (node.result) |some| {
1041 const return_type = try typeExpr(mod, scope, some.return_type);
1042 return addZIRUnOp(mod, scope, src, .anyframe_type, return_type);
1043 } else {
1044 return addZIRInstConst(mod, scope, src, .{
1045 .ty = Type.initTag(.type),
1046 .val = Value.initTag(.anyframe_type),
1047 });
1048 }
1049}
1050
1051fn typeInixOp(mod: *Module, scope: *Scope, node: *ast.Node.SimpleInfixOp, op_inst_tag: zir.Inst.Tag) InnerError!*zir.Inst {
1052 const tree = scope.tree();
1053 const src = tree.token_locs[node.op_token].start;
1054 const error_set = try typeExpr(mod, scope, node.lhs);
1055 const payload = try typeExpr(mod, scope, node.rhs);
1056 return addZIRBinOp(mod, scope, src, op_inst_tag, error_set, payload);
1057}
1058
1059fn enumLiteral(mod: *Module, scope: *Scope, node: *ast.Node.EnumLiteral) !*zir.Inst {
1060 const tree = scope.tree();
1061 const src = tree.token_locs[node.name].start;
1062 const name = try mod.identifierTokenString(scope, node.name);
1063
1064 return addZIRInst(mod, scope, src, zir.Inst.EnumLiteral, .{ .name = name }, .{});
1065}
1066
1067fn unwrapOptional(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst {
1068 const tree = scope.tree();
1069 const src = tree.token_locs[node.rtoken].start;
1070
1071 const operand = try expr(mod, scope, rl, node.lhs);
1072 const op: zir.Inst.Tag = switch (rl) {
1073 .ref => .optional_payload_safe_ptr,
1074 else => .optional_payload_safe,
1075 };
1076 return addZIRUnOp(mod, scope, src, op, operand);
1420 return rvalue(mod, scope, rl, result);
10771421}
10781422
10791423fn containerField(
10801424 mod: *Module,
10811425 scope: *Scope,
1082 node: *ast.Node.ContainerField,
1426 field: ast.full.ContainerField,
10831427) InnerError!*zir.Inst {
10841428 const tree = scope.tree();
1085 const src = tree.token_locs[node.firstToken()].start;
1086 const name = try mod.identifierTokenString(scope, node.name_token);
1429 const token_starts = tree.tokens.items(.start);
1430
1431 const src = token_starts[field.ast.name_token];
1432 const name = try mod.identifierTokenString(scope, field.ast.name_token);
10871433
1088 if (node.comptime_token == null and node.value_expr == null and node.align_expr == null) {
1089 if (node.type_expr) |some| {
1090 const ty = try typeExpr(mod, scope, some);
1434 if (field.comptime_token == null and field.ast.value_expr == 0 and field.ast.align_expr == 0) {
1435 if (field.ast.type_expr != 0) {
1436 const ty = try typeExpr(mod, scope, field.ast.type_expr);
10911437 return addZIRInst(mod, scope, src, zir.Inst.ContainerFieldTyped, .{
10921438 .bytes = name,
10931439 .ty = ty,
......@@ -1099,9 +1445,11 @@ fn containerField(
10991445 }
11001446 }
11011447
1102 const ty = if (node.type_expr) |some| try typeExpr(mod, scope, some) else null;
1103 const alignment = if (node.align_expr) |some| try expr(mod, scope, .none, some) else null;
1104 const init = if (node.value_expr) |some| try expr(mod, scope, .none, some) else null;
1448 const ty = if (field.ast.type_expr != 0) try typeExpr(mod, scope, field.ast.type_expr) else null;
1449 // TODO result location should be alignment type
1450 const alignment = if (field.ast.align_expr != 0) try expr(mod, scope, .none, field.ast.align_expr) else null;
1451 // TODO result location should be the field type
1452 const init = if (field.ast.value_expr != 0) try expr(mod, scope, .none, field.ast.value_expr) else null;
11051453
11061454 return addZIRInst(mod, scope, src, zir.Inst.ContainerField, .{
11071455 .bytes = name,
......@@ -1109,13 +1457,22 @@ fn containerField(
11091457 .ty = ty,
11101458 .init = init,
11111459 .alignment = alignment,
1112 .is_comptime = node.comptime_token != null,
1460 .is_comptime = field.comptime_token != null,
11131461 });
11141462}
11151463
1116fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.ContainerDecl) InnerError!*zir.Inst {
1464fn containerDecl(
1465 mod: *Module,
1466 scope: *Scope,
1467 rl: ResultLoc,
1468 container_decl: ast.full.ContainerDecl,
1469) InnerError!*zir.Inst {
11171470 const tree = scope.tree();
1118 const src = tree.token_locs[node.kind_token].start;
1471 const token_starts = tree.tokens.items(.start);
1472 const node_tags = tree.nodes.items(.tag);
1473 const token_tags = tree.tokens.items(.tag);
1474
1475 const src = token_starts[container_decl.ast.main_token];
11191476
11201477 var gen_scope: Scope.GenZIR = .{
11211478 .parent = scope,
......@@ -1129,10 +1486,16 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con
11291486 var fields = std.ArrayList(*zir.Inst).init(mod.gpa);
11301487 defer fields.deinit();
11311488
1132 for (node.fieldsAndDecls()) |fd| {
1133 if (fd.castTag(.ContainerField)) |f| {
1134 try fields.append(try containerField(mod, &gen_scope.base, f));
1135 }
1489 for (container_decl.ast.members) |member| {
1490 // TODO just handle these cases differently since they end up with different ZIR
1491 // instructions anyway. It will be simpler & have fewer branches.
1492 const field = switch (node_tags[member]) {
1493 .container_field_init => try containerField(mod, &gen_scope.base, tree.containerFieldInit(member)),
1494 .container_field_align => try containerField(mod, &gen_scope.base, tree.containerFieldAlign(member)),
1495 .container_field => try containerField(mod, &gen_scope.base, tree.containerField(member)),
1496 else => continue,
1497 };
1498 try fields.append(field);
11361499 }
11371500
11381501 var decl_arena = std.heap.ArenaAllocator.init(mod.gpa);
......@@ -1140,19 +1503,22 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con
11401503 const arena = &decl_arena.allocator;
11411504
11421505 var layout: std.builtin.TypeInfo.ContainerLayout = .Auto;
1143 if (node.layout_token) |some| switch (tree.token_ids[some]) {
1144 .Keyword_extern => layout = .Extern,
1145 .Keyword_packed => layout = .Packed,
1506 if (container_decl.layout_token) |some| switch (token_tags[some]) {
1507 .keyword_extern => layout = .Extern,
1508 .keyword_packed => layout = .Packed,
11461509 else => unreachable,
11471510 };
11481511
1149 const container_type = switch (tree.token_ids[node.kind_token]) {
1150 .Keyword_enum => blk: {
1151 const tag_type: ?*zir.Inst = switch (node.init_arg_expr) {
1152 .Type => |t| try typeExpr(mod, &gen_scope.base, t),
1153 .None => null,
1154 .Enum => unreachable,
1155 };
1512 // TODO this implementation is incorrect. The types must be created in semantic
1513 // analysis, not astgen, because the same ZIR is re-used for multiple inline function calls,
1514 // comptime function calls, and generic function instantiations, and these
1515 // must result in different instances of container types.
1516 const container_type = switch (token_tags[container_decl.ast.main_token]) {
1517 .keyword_enum => blk: {
1518 const tag_type: ?*zir.Inst = if (container_decl.ast.arg != 0)
1519 try typeExpr(mod, &gen_scope.base, container_decl.ast.arg)
1520 else
1521 null;
11561522 const inst = try addZIRInst(mod, &gen_scope.base, src, zir.Inst.EnumType, .{
11571523 .fields = try arena.dupe(*zir.Inst, fields.items),
11581524 }, .{
......@@ -1174,8 +1540,8 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con
11741540 };
11751541 break :blk Type.initPayload(&enum_type.base);
11761542 },
1177 .Keyword_struct => blk: {
1178 assert(node.init_arg_expr == .None);
1543 .keyword_struct => blk: {
1544 assert(container_decl.ast.arg == 0);
11791545 const inst = try addZIRInst(mod, &gen_scope.base, src, zir.Inst.StructType, .{
11801546 .fields = try arena.dupe(*zir.Inst, fields.items),
11811547 }, .{
......@@ -1196,22 +1562,17 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con
11961562 };
11971563 break :blk Type.initPayload(&struct_type.base);
11981564 },
1199 .Keyword_union => blk: {
1200 const init_inst = switch (node.init_arg_expr) {
1201 .Enum => |e| if (e) |t| try typeExpr(mod, &gen_scope.base, t) else null,
1202 .None => null,
1203 .Type => |t| try typeExpr(mod, &gen_scope.base, t),
1204 };
1205 const init_kind: zir.Inst.UnionType.InitKind = switch (node.init_arg_expr) {
1206 .Enum => .enum_type,
1207 .None => .none,
1208 .Type => .tag_type,
1209 };
1565 .keyword_union => blk: {
1566 const init_inst: ?*zir.Inst = if (container_decl.ast.arg != 0)
1567 try typeExpr(mod, &gen_scope.base, container_decl.ast.arg)
1568 else
1569 null;
1570 const has_enum_token = container_decl.ast.enum_token != null;
12101571 const inst = try addZIRInst(mod, &gen_scope.base, src, zir.Inst.UnionType, .{
12111572 .fields = try arena.dupe(*zir.Inst, fields.items),
12121573 }, .{
12131574 .layout = layout,
1214 .init_kind = init_kind,
1575 .has_enum_token = has_enum_token,
12151576 .init_inst = init_inst,
12161577 });
12171578 const union_type = try arena.create(Type.Payload.Union);
......@@ -1229,7 +1590,7 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con
12291590 };
12301591 break :blk Type.initPayload(&union_type.base);
12311592 },
1232 .Keyword_opaque => blk: {
1593 .keyword_opaque => blk: {
12331594 if (fields.items.len > 0) {
12341595 return mod.fail(scope, fields.items[0].src, "opaque types cannot have fields", .{});
12351596 }
......@@ -1245,7 +1606,7 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con
12451606 else => unreachable,
12461607 };
12471608 const val = try Value.Tag.ty.create(arena, container_type);
1248 const decl = try mod.createContainerDecl(scope, node.kind_token, &decl_arena, .{
1609 const decl = try mod.createContainerDecl(scope, container_decl.ast.main_token, &decl_arena, .{
12491610 .ty = Type.initTag(.type),
12501611 .val = val,
12511612 });
......@@ -1258,101 +1619,69 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con
12581619 }
12591620}
12601621
1261fn errorSetDecl(mod: *Module, scope: *Scope, node: *ast.Node.ErrorSetDecl) InnerError!*zir.Inst {
1262 const tree = scope.tree();
1263 const src = tree.token_locs[node.error_token].start;
1264 const decls = node.decls();
1265 const fields = try scope.arena().alloc([]const u8, decls.len);
1266
1267 for (decls) |decl, i| {
1268 const tag = decl.castTag(.ErrorTag).?;
1269 fields[i] = try mod.identifierTokenString(scope, tag.name_token);
1270 }
1271
1272 return addZIRInst(mod, scope, src, zir.Inst.ErrorSet, .{ .fields = fields }, .{});
1273}
1274
1275fn errorType(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
1622fn errorSetDecl(
1623 mod: *Module,
1624 scope: *Scope,
1625 rl: ResultLoc,
1626 node: ast.Node.Index,
1627) InnerError!*zir.Inst {
12761628 const tree = scope.tree();
1277 const src = tree.token_locs[node.token].start;
1278 return addZIRInstConst(mod, scope, src, .{
1279 .ty = Type.initTag(.type),
1280 .val = Value.initTag(.anyerror_type),
1281 });
1282}
1629 const main_tokens = tree.nodes.items(.main_token);
1630 const token_tags = tree.tokens.items(.tag);
1631 const token_starts = tree.tokens.items(.start);
1632
1633 // Count how many fields there are.
1634 const error_token = main_tokens[node];
1635 const count: usize = count: {
1636 var tok_i = error_token + 2;
1637 var count: usize = 0;
1638 while (true) : (tok_i += 1) {
1639 switch (token_tags[tok_i]) {
1640 .doc_comment, .comma => {},
1641 .identifier => count += 1,
1642 .r_paren => break :count count,
1643 else => unreachable,
1644 }
1645 } else unreachable; // TODO should not need else unreachable here
1646 };
12831647
1284fn catchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Catch) InnerError!*zir.Inst {
1285 switch (rl) {
1286 .ref => return orelseCatchExpr(
1287 mod,
1288 scope,
1289 rl,
1290 node.lhs,
1291 node.op_token,
1292 .is_err_ptr,
1293 .err_union_payload_unsafe_ptr,
1294 .err_union_code_ptr,
1295 node.rhs,
1296 node.payload,
1297 ),
1298 else => return orelseCatchExpr(
1299 mod,
1300 scope,
1301 rl,
1302 node.lhs,
1303 node.op_token,
1304 .is_err,
1305 .err_union_payload_unsafe,
1306 .err_union_code,
1307 node.rhs,
1308 node.payload,
1309 ),
1310 }
1311}
1312
1313fn orelseExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst {
1314 switch (rl) {
1315 .ref => return orelseCatchExpr(
1316 mod,
1317 scope,
1318 rl,
1319 node.lhs,
1320 node.op_token,
1321 .is_null_ptr,
1322 .optional_payload_unsafe_ptr,
1323 undefined,
1324 node.rhs,
1325 null,
1326 ),
1327 else => return orelseCatchExpr(
1328 mod,
1329 scope,
1330 rl,
1331 node.lhs,
1332 node.op_token,
1333 .is_null,
1334 .optional_payload_unsafe,
1335 undefined,
1336 node.rhs,
1337 null,
1338 ),
1648 const fields = try scope.arena().alloc([]const u8, count);
1649 {
1650 var tok_i = error_token + 2;
1651 var field_i: usize = 0;
1652 while (true) : (tok_i += 1) {
1653 switch (token_tags[tok_i]) {
1654 .doc_comment, .comma => {},
1655 .identifier => {
1656 fields[field_i] = try mod.identifierTokenString(scope, tok_i);
1657 field_i += 1;
1658 },
1659 .r_paren => break,
1660 else => unreachable,
1661 }
1662 }
13391663 }
1664 const src = token_starts[error_token];
1665 const result = try addZIRInst(mod, scope, src, zir.Inst.ErrorSet, .{ .fields = fields }, .{});
1666 return rvalue(mod, scope, rl, result);
13401667}
13411668
13421669fn orelseCatchExpr(
13431670 mod: *Module,
13441671 scope: *Scope,
13451672 rl: ResultLoc,
1346 lhs: *ast.Node,
1673 lhs: ast.Node.Index,
13471674 op_token: ast.TokenIndex,
13481675 cond_op: zir.Inst.Tag,
13491676 unwrap_op: zir.Inst.Tag,
13501677 unwrap_code_op: zir.Inst.Tag,
1351 rhs: *ast.Node,
1352 payload_node: ?*ast.Node,
1678 rhs: ast.Node.Index,
1679 payload_token: ?ast.TokenIndex,
13531680) InnerError!*zir.Inst {
13541681 const tree = scope.tree();
1355 const src = tree.token_locs[op_token].start;
1682 const token_starts = tree.tokens.items(.start);
1683
1684 const src = token_starts[op_token];
13561685
13571686 var block_scope: Scope.GenZIR = .{
13581687 .parent = scope,
......@@ -1390,12 +1719,11 @@ fn orelseCatchExpr(
13901719
13911720 var err_val_scope: Scope.LocalVal = undefined;
13921721 const then_sub_scope = blk: {
1393 const payload = payload_node orelse break :blk &then_scope.base;
1394
1395 const err_name = tree.tokenSlice(payload.castTag(.Payload).?.error_symbol.firstToken());
1396 if (mem.eql(u8, err_name, "_"))
1397 break :blk &then_scope.base;
1398
1722 const payload = payload_token orelse break :blk &then_scope.base;
1723 if (mem.eql(u8, tree.tokenSlice(payload), "_")) {
1724 return mod.failTok(&then_scope.base, payload, "discard of error capture; omit it instead", .{});
1725 }
1726 const err_name = try mod.identifierTokenString(scope, payload);
13991727 err_val_scope = .{
14001728 .parent = &then_scope.base,
14011729 .gen_zir = &then_scope,
......@@ -1524,124 +1852,121 @@ fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: as
15241852 return mem.eql(u8, ident_name_1, ident_name_2);
15251853}
15261854
1527pub fn field(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst {
1855pub fn fieldAccess(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!*zir.Inst {
15281856 const tree = scope.tree();
1529 const src = tree.token_locs[node.op_token].start;
1530 // TODO custom AST node for field access so that we don't have to go through a node cast here
1531 const field_name = try mod.identifierTokenString(scope, node.rhs.castTag(.Identifier).?.token);
1857 const token_starts = tree.tokens.items(.start);
1858 const main_tokens = tree.nodes.items(.main_token);
1859 const node_datas = tree.nodes.items(.data);
1860
1861 const dot_token = main_tokens[node];
1862 const src = token_starts[dot_token];
1863 const field_ident = dot_token + 1;
1864 const field_name = try mod.identifierTokenString(scope, field_ident);
15321865 if (rl == .ref) {
15331866 return addZirInstTag(mod, scope, src, .field_ptr, .{
1534 .object = try expr(mod, scope, .ref, node.lhs),
1867 .object = try expr(mod, scope, .ref, node_datas[node].lhs),
15351868 .field_name = field_name,
15361869 });
1870 } else {
1871 return rvalue(mod, scope, rl, try addZirInstTag(mod, scope, src, .field_val, .{
1872 .object = try expr(mod, scope, .none, node_datas[node].lhs),
1873 .field_name = field_name,
1874 }));
15371875 }
1538 return rvalue(mod, scope, rl, try addZirInstTag(mod, scope, src, .field_val, .{
1539 .object = try expr(mod, scope, .none, node.lhs),
1540 .field_name = field_name,
1541 }));
15421876}
15431877
1544fn namedField(
1878fn arrayAccess(
15451879 mod: *Module,
15461880 scope: *Scope,
15471881 rl: ResultLoc,
1548 call: *ast.Node.BuiltinCall,
1882 node: ast.Node.Index,
15491883) InnerError!*zir.Inst {
1550 try ensureBuiltinParamCount(mod, scope, call, 2);
1551
15521884 const tree = scope.tree();
1553 const src = tree.token_locs[call.builtin_token].start;
1554 const params = call.params();
1555
1556 const string_type = try addZIRInstConst(mod, scope, src, .{
1557 .ty = Type.initTag(.type),
1558 .val = Value.initTag(.const_slice_u8_type),
1559 });
1560 const string_rl: ResultLoc = .{ .ty = string_type };
1561
1562 if (rl == .ref) {
1563 return addZirInstTag(mod, scope, src, .field_ptr_named, .{
1564 .object = try expr(mod, scope, .ref, params[0]),
1565 .field_name = try comptimeExpr(mod, scope, string_rl, params[1]),
1566 });
1567 }
1568 return rvalue(mod, scope, rl, try addZirInstTag(mod, scope, src, .field_val_named, .{
1569 .object = try expr(mod, scope, .none, params[0]),
1570 .field_name = try comptimeExpr(mod, scope, string_rl, params[1]),
1571 }));
1572}
1885 const main_tokens = tree.nodes.items(.main_token);
1886 const token_starts = tree.tokens.items(.start);
1887 const node_datas = tree.nodes.items(.data);
15731888
1574fn arrayAccess(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.ArrayAccess) InnerError!*zir.Inst {
1575 const tree = scope.tree();
1576 const src = tree.token_locs[node.rtoken].start;
1889 const src = token_starts[main_tokens[node]];
15771890 const usize_type = try addZIRInstConst(mod, scope, src, .{
15781891 .ty = Type.initTag(.type),
15791892 .val = Value.initTag(.usize_type),
15801893 });
15811894 const index_rl: ResultLoc = .{ .ty = usize_type };
1582
1583 if (rl == .ref) {
1584 return addZirInstTag(mod, scope, src, .elem_ptr, .{
1585 .array = try expr(mod, scope, .ref, node.lhs),
1586 .index = try expr(mod, scope, index_rl, node.index_expr),
1587 });
1895 switch (rl) {
1896 .ref => return addZirInstTag(mod, scope, src, .elem_ptr, .{
1897 .array = try expr(mod, scope, .ref, node_datas[node].lhs),
1898 .index = try expr(mod, scope, index_rl, node_datas[node].rhs),
1899 }),
1900 else => return rvalue(mod, scope, rl, try addZirInstTag(mod, scope, src, .elem_val, .{
1901 .array = try expr(mod, scope, .none, node_datas[node].lhs),
1902 .index = try expr(mod, scope, index_rl, node_datas[node].rhs),
1903 })),
15881904 }
1589 return rvalue(mod, scope, rl, try addZirInstTag(mod, scope, src, .elem_val, .{
1590 .array = try expr(mod, scope, .none, node.lhs),
1591 .index = try expr(mod, scope, index_rl, node.index_expr),
1592 }));
15931905}
15941906
1595fn sliceExpr(mod: *Module, scope: *Scope, node: *ast.Node.Slice) InnerError!*zir.Inst {
1907fn sliceExpr(
1908 mod: *Module,
1909 scope: *Scope,
1910 rl: ResultLoc,
1911 slice: ast.full.Slice,
1912) InnerError!*zir.Inst {
15961913 const tree = scope.tree();
1597 const src = tree.token_locs[node.rtoken].start;
1914 const token_starts = tree.tokens.items(.start);
1915
1916 const src = token_starts[slice.ast.lbracket];
15981917
15991918 const usize_type = try addZIRInstConst(mod, scope, src, .{
16001919 .ty = Type.initTag(.type),
16011920 .val = Value.initTag(.usize_type),
16021921 });
16031922
1604 const array_ptr = try expr(mod, scope, .ref, node.lhs);
1605 const start = try expr(mod, scope, .{ .ty = usize_type }, node.start);
1923 const array_ptr = try expr(mod, scope, .ref, slice.ast.sliced);
1924 const start = try expr(mod, scope, .{ .ty = usize_type }, slice.ast.start);
16061925
1607 if (node.end == null and node.sentinel == null) {
1608 return try addZIRBinOp(mod, scope, src, .slice_start, array_ptr, start);
1926 if (slice.ast.sentinel == 0) {
1927 if (slice.ast.end == 0) {
1928 const result = try addZIRBinOp(mod, scope, src, .slice_start, array_ptr, start);
1929 return rvalue(mod, scope, rl, result);
1930 } else {
1931 const end = try expr(mod, scope, .{ .ty = usize_type }, slice.ast.end);
1932 // TODO a ZIR slice_open instruction
1933 const result = try addZIRInst(mod, scope, src, zir.Inst.Slice, .{
1934 .array_ptr = array_ptr,
1935 .start = start,
1936 }, .{ .end = end });
1937 return rvalue(mod, scope, rl, result);
1938 }
16091939 }
16101940
1611 const end = if (node.end) |end| try expr(mod, scope, .{ .ty = usize_type }, end) else null;
1612 // we could get the child type here, but it is easier to just do it in semantic analysis.
1613 const sentinel = if (node.sentinel) |sentinel| try expr(mod, scope, .none, sentinel) else null;
1614
1615 return try addZIRInst(
1616 mod,
1617 scope,
1618 src,
1619 zir.Inst.Slice,
1620 .{ .array_ptr = array_ptr, .start = start },
1621 .{ .end = end, .sentinel = sentinel },
1622 );
1623}
1624
1625fn deref(mod: *Module, scope: *Scope, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst {
1626 const tree = scope.tree();
1627 const src = tree.token_locs[node.rtoken].start;
1628 const lhs = try expr(mod, scope, .none, node.lhs);
1629 return addZIRUnOp(mod, scope, src, .deref, lhs);
1941 const end = try expr(mod, scope, .{ .ty = usize_type }, slice.ast.end);
1942 // TODO pass the proper result loc to this expression using a ZIR instruction
1943 // "get the child element type for a slice target".
1944 const sentinel = try expr(mod, scope, .none, slice.ast.sentinel);
1945 const result = try addZIRInst(mod, scope, src, zir.Inst.Slice, .{
1946 .array_ptr = array_ptr,
1947 .start = start,
1948 }, .{
1949 .end = end,
1950 .sentinel = sentinel,
1951 });
1952 return rvalue(mod, scope, rl, result);
16301953}
16311954
16321955fn simpleBinOp(
16331956 mod: *Module,
16341957 scope: *Scope,
16351958 rl: ResultLoc,
1636 infix_node: *ast.Node.SimpleInfixOp,
1959 infix_node: ast.Node.Index,
16371960 op_inst_tag: zir.Inst.Tag,
16381961) InnerError!*zir.Inst {
16391962 const tree = scope.tree();
1640 const src = tree.token_locs[infix_node.op_token].start;
1641
1642 const lhs = try expr(mod, scope, .none, infix_node.lhs);
1643 const rhs = try expr(mod, scope, .none, infix_node.rhs);
1963 const node_datas = tree.nodes.items(.data);
1964 const main_tokens = tree.nodes.items(.main_token);
1965 const token_starts = tree.tokens.items(.start);
16441966
1967 const lhs = try expr(mod, scope, .none, node_datas[infix_node].lhs);
1968 const rhs = try expr(mod, scope, .none, node_datas[infix_node].rhs);
1969 const src = token_starts[main_tokens[infix_node]];
16451970 const result = try addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs);
16461971 return rvalue(mod, scope, rl, result);
16471972}
......@@ -1650,10 +1975,15 @@ fn boolBinOp(
16501975 mod: *Module,
16511976 scope: *Scope,
16521977 rl: ResultLoc,
1653 infix_node: *ast.Node.SimpleInfixOp,
1978 infix_node: ast.Node.Index,
1979 is_bool_and: bool,
16541980) InnerError!*zir.Inst {
16551981 const tree = scope.tree();
1656 const src = tree.token_locs[infix_node.op_token].start;
1982 const node_datas = tree.nodes.items(.data);
1983 const main_tokens = tree.nodes.items(.main_token);
1984 const token_starts = tree.tokens.items(.start);
1985
1986 const src = token_starts[main_tokens[infix_node]];
16571987 const bool_type = try addZIRInstConst(mod, scope, src, .{
16581988 .ty = Type.initTag(.type),
16591989 .val = Value.initTag(.bool_type),
......@@ -1668,7 +1998,7 @@ fn boolBinOp(
16681998 };
16691999 defer block_scope.instructions.deinit(mod.gpa);
16702000
1671 const lhs = try expr(mod, scope, .{ .ty = bool_type }, infix_node.lhs);
2001 const lhs = try expr(mod, scope, .{ .ty = bool_type }, node_datas[infix_node].lhs);
16722002 const condbr = try addZIRInstSpecial(mod, &block_scope.base, src, zir.Inst.CondBr, .{
16732003 .condition = lhs,
16742004 .then_body = undefined, // populated below
......@@ -1688,7 +2018,7 @@ fn boolBinOp(
16882018 };
16892019 defer rhs_scope.instructions.deinit(mod.gpa);
16902020
1691 const rhs = try expr(mod, &rhs_scope.base, .{ .ty = bool_type }, infix_node.rhs);
2021 const rhs = try expr(mod, &rhs_scope.base, .{ .ty = bool_type }, node_datas[infix_node].rhs);
16922022 _ = try addZIRInst(mod, &rhs_scope.base, src, zir.Inst.Break, .{
16932023 .block = block,
16942024 .operand = rhs,
......@@ -1703,7 +2033,6 @@ fn boolBinOp(
17032033 };
17042034 defer const_scope.instructions.deinit(mod.gpa);
17052035
1706 const is_bool_and = infix_node.base.tag == .BoolAnd;
17072036 _ = try addZIRInst(mod, &const_scope.base, src, zir.Inst.Break, .{
17082037 .block = block,
17092038 .operand = try addZIRInstConst(mod, &const_scope.base, src, .{
......@@ -1731,96 +2060,42 @@ fn boolBinOp(
17312060 return rvalue(mod, scope, rl, &block.base);
17322061}
17332062
1734const CondKind = union(enum) {
1735 bool,
1736 optional: ?*zir.Inst,
1737 err_union: ?*zir.Inst,
2063fn ifExpr(
2064 mod: *Module,
2065 scope: *Scope,
2066 rl: ResultLoc,
2067 if_full: ast.full.If,
2068) InnerError!*zir.Inst {
2069 var block_scope: Scope.GenZIR = .{
2070 .parent = scope,
2071 .decl = scope.ownerDecl().?,
2072 .arena = scope.arena(),
2073 .force_comptime = scope.isComptime(),
2074 .instructions = .{},
2075 };
2076 setBlockResultLoc(&block_scope, rl);
2077 defer block_scope.instructions.deinit(mod.gpa);
17382078
1739 fn cond(self: *CondKind, mod: *Module, block_scope: *Scope.GenZIR, src: usize, cond_node: *ast.Node) !*zir.Inst {
1740 switch (self.*) {
1741 .bool => {
1742 const bool_type = try addZIRInstConst(mod, &block_scope.base, src, .{
1743 .ty = Type.initTag(.type),
1744 .val = Value.initTag(.bool_type),
1745 });
1746 return try expr(mod, &block_scope.base, .{ .ty = bool_type }, cond_node);
1747 },
1748 .optional => {
1749 const cond_ptr = try expr(mod, &block_scope.base, .ref, cond_node);
1750 self.* = .{ .optional = cond_ptr };
1751 const result = try addZIRUnOp(mod, &block_scope.base, src, .deref, cond_ptr);
1752 return try addZIRUnOp(mod, &block_scope.base, src, .is_non_null, result);
1753 },
1754 .err_union => {
1755 const err_ptr = try expr(mod, &block_scope.base, .ref, cond_node);
1756 self.* = .{ .err_union = err_ptr };
1757 const result = try addZIRUnOp(mod, &block_scope.base, src, .deref, err_ptr);
1758 return try addZIRUnOp(mod, &block_scope.base, src, .is_err, result);
1759 },
1760 }
1761 }
1762
1763 fn thenSubScope(self: CondKind, mod: *Module, then_scope: *Scope.GenZIR, src: usize, payload_node: ?*ast.Node) !*Scope {
1764 if (self == .bool) return &then_scope.base;
1765
1766 const payload = payload_node.?.castTag(.PointerPayload) orelse {
1767 // condition is error union and payload is not explicitly ignored
1768 _ = try addZIRUnOp(mod, &then_scope.base, src, .ensure_err_payload_void, self.err_union.?);
1769 return &then_scope.base;
1770 };
1771 const is_ptr = payload.ptr_token != null;
1772 const ident_node = payload.value_symbol.castTag(.Identifier).?;
1773
1774 // This intentionally does not support @"_" syntax.
1775 const ident_name = then_scope.base.tree().tokenSlice(ident_node.token);
1776 if (mem.eql(u8, ident_name, "_")) {
1777 if (is_ptr)
1778 return mod.failTok(&then_scope.base, payload.ptr_token.?, "pointer modifier invalid on discard", .{});
1779 return &then_scope.base;
1780 }
1781
1782 return mod.failNode(&then_scope.base, payload.value_symbol, "TODO implement payload symbols", .{});
1783 }
1784
1785 fn elseSubScope(self: CondKind, mod: *Module, else_scope: *Scope.GenZIR, src: usize, payload_node: ?*ast.Node) !*Scope {
1786 if (self != .err_union) return &else_scope.base;
1787
1788 const payload_ptr = try addZIRUnOp(mod, &else_scope.base, src, .err_union_payload_unsafe_ptr, self.err_union.?);
1789
1790 const payload = payload_node.?.castTag(.Payload).?;
1791 const ident_node = payload.error_symbol.castTag(.Identifier).?;
1792
1793 // This intentionally does not support @"_" syntax.
1794 const ident_name = else_scope.base.tree().tokenSlice(ident_node.token);
1795 if (mem.eql(u8, ident_name, "_")) {
1796 return &else_scope.base;
1797 }
2079 const tree = scope.tree();
2080 const main_tokens = tree.nodes.items(.main_token);
2081 const token_starts = tree.tokens.items(.start);
17982082
1799 return mod.failNode(&else_scope.base, payload.error_symbol, "TODO implement payload symbols", .{});
1800 }
1801};
2083 const if_src = token_starts[if_full.ast.if_token];
18022084
1803fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) InnerError!*zir.Inst {
1804 var cond_kind: CondKind = .bool;
1805 if (if_node.payload) |_| cond_kind = .{ .optional = null };
1806 if (if_node.@"else") |else_node| {
1807 if (else_node.payload) |payload| {
1808 cond_kind = .{ .err_union = null };
2085 const cond = c: {
2086 // TODO https://github.com/ziglang/zig/issues/7929
2087 if (if_full.error_token) |error_token| {
2088 return mod.failTok(scope, error_token, "TODO implement if error union", .{});
2089 } else if (if_full.payload_token) |payload_token| {
2090 return mod.failTok(scope, payload_token, "TODO implement if optional", .{});
2091 } else {
2092 const bool_type = try addZIRInstConst(mod, &block_scope.base, if_src, .{
2093 .ty = Type.initTag(.type),
2094 .val = Value.initTag(.bool_type),
2095 });
2096 break :c try expr(mod, &block_scope.base, .{ .ty = bool_type }, if_full.ast.cond_expr);
18092097 }
1810 }
1811 var block_scope: Scope.GenZIR = .{
1812 .parent = scope,
1813 .decl = scope.ownerDecl().?,
1814 .arena = scope.arena(),
1815 .force_comptime = scope.isComptime(),
1816 .instructions = .{},
18172098 };
1818 setBlockResultLoc(&block_scope, rl);
1819 defer block_scope.instructions.deinit(mod.gpa);
1820
1821 const tree = scope.tree();
1822 const if_src = tree.token_locs[if_node.if_token].start;
1823 const cond = try cond_kind.cond(mod, &block_scope, if_src, if_node.condition);
18242099
18252100 const condbr = try addZIRInstSpecial(mod, &block_scope.base, if_src, zir.Inst.CondBr, .{
18262101 .condition = cond,
......@@ -1832,7 +2107,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
18322107 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
18332108 });
18342109
1835 const then_src = tree.token_locs[if_node.body.lastToken()].start;
2110 const then_src = token_starts[tree.lastToken(if_full.ast.then_expr)];
18362111 var then_scope: Scope.GenZIR = .{
18372112 .parent = scope,
18382113 .decl = block_scope.decl,
......@@ -1843,10 +2118,10 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
18432118 defer then_scope.instructions.deinit(mod.gpa);
18442119
18452120 // declare payload to the then_scope
1846 const then_sub_scope = try cond_kind.thenSubScope(mod, &then_scope, then_src, if_node.payload);
2121 const then_sub_scope = &then_scope.base;
18472122
18482123 block_scope.break_count += 1;
1849 const then_result = try expr(mod, then_sub_scope, block_scope.break_result_loc, if_node.body);
2124 const then_result = try expr(mod, then_sub_scope, block_scope.break_result_loc, if_full.ast.then_expr);
18502125 // We hold off on the break instructions as well as copying the then/else
18512126 // instructions into place until we know whether to keep store_to_block_ptr
18522127 // instructions or not.
......@@ -1860,20 +2135,19 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
18602135 };
18612136 defer else_scope.instructions.deinit(mod.gpa);
18622137
1863 var else_src: usize = undefined;
1864 var else_sub_scope: *Module.Scope = undefined;
1865 const else_result: ?*zir.Inst = if (if_node.@"else") |else_node| blk: {
1866 else_src = tree.token_locs[else_node.body.lastToken()].start;
1867 // declare payload to the then_scope
1868 else_sub_scope = try cond_kind.elseSubScope(mod, &else_scope, else_src, else_node.payload);
1869
2138 const else_node = if_full.ast.else_expr;
2139 const else_info: struct { src: usize, result: ?*zir.Inst } = if (else_node != 0) blk: {
18702140 block_scope.break_count += 1;
1871 break :blk try expr(mod, else_sub_scope, block_scope.break_result_loc, else_node.body);
1872 } else blk: {
1873 else_src = tree.token_locs[if_node.lastToken()].start;
1874 else_sub_scope = &else_scope.base;
1875 break :blk null;
1876 };
2141 const sub_scope = &else_scope.base;
2142 break :blk .{
2143 .src = token_starts[tree.lastToken(else_node)],
2144 .result = try expr(mod, sub_scope, block_scope.break_result_loc, else_node),
2145 };
2146 } else
2147 .{
2148 .src = token_starts[tree.lastToken(if_full.ast.then_expr)],
2149 .result = null,
2150 };
18772151
18782152 return finishThenElseBlock(
18792153 mod,
......@@ -1885,9 +2159,9 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
18852159 &condbr.positionals.then_body,
18862160 &condbr.positionals.else_body,
18872161 then_src,
1888 else_src,
2162 else_info.src,
18892163 then_result,
1890 else_result,
2164 else_info.result,
18912165 block,
18922166 block,
18932167 );
......@@ -1918,23 +2192,15 @@ fn whileExpr(
19182192 mod: *Module,
19192193 scope: *Scope,
19202194 rl: ResultLoc,
1921 while_node: *ast.Node.While,
2195 while_full: ast.full.While,
19222196) InnerError!*zir.Inst {
1923 var cond_kind: CondKind = .bool;
1924 if (while_node.payload) |_| cond_kind = .{ .optional = null };
1925 if (while_node.@"else") |else_node| {
1926 if (else_node.payload) |payload| {
1927 cond_kind = .{ .err_union = null };
1928 }
2197 if (while_full.label_token) |label_token| {
2198 try checkLabelRedefinition(mod, scope, label_token);
19292199 }
1930
1931 if (while_node.label) |label| {
1932 try checkLabelRedefinition(mod, scope, label);
2200 if (while_full.inline_token) |inline_token| {
2201 return mod.failTok(scope, inline_token, "TODO inline while", .{});
19332202 }
19342203
1935 if (while_node.inline_token) |tok|
1936 return mod.failTok(scope, tok, "TODO inline while", .{});
1937
19382204 var loop_scope: Scope.GenZIR = .{
19392205 .parent = scope,
19402206 .decl = scope.ownerDecl().?,
......@@ -1955,12 +2221,28 @@ fn whileExpr(
19552221 defer continue_scope.instructions.deinit(mod.gpa);
19562222
19572223 const tree = scope.tree();
1958 const while_src = tree.token_locs[while_node.while_token].start;
2224 const main_tokens = tree.nodes.items(.main_token);
2225 const token_starts = tree.tokens.items(.start);
2226
2227 const while_src = token_starts[while_full.ast.while_token];
19592228 const void_type = try addZIRInstConst(mod, scope, while_src, .{
19602229 .ty = Type.initTag(.type),
19612230 .val = Value.initTag(.void_type),
19622231 });
1963 const cond = try cond_kind.cond(mod, &continue_scope, while_src, while_node.condition);
2232 const cond = c: {
2233 // TODO https://github.com/ziglang/zig/issues/7929
2234 if (while_full.error_token) |error_token| {
2235 return mod.failTok(scope, error_token, "TODO implement while error union", .{});
2236 } else if (while_full.payload_token) |payload_token| {
2237 return mod.failTok(scope, payload_token, "TODO implement while optional", .{});
2238 } else {
2239 const bool_type = try addZIRInstConst(mod, &continue_scope.base, while_src, .{
2240 .ty = Type.initTag(.type),
2241 .val = Value.initTag(.bool_type),
2242 });
2243 break :c try expr(mod, &continue_scope.base, .{ .ty = bool_type }, while_full.ast.cond_expr);
2244 }
2245 };
19642246
19652247 const condbr = try addZIRInstSpecial(mod, &continue_scope.base, while_src, zir.Inst.CondBr, .{
19662248 .condition = cond,
......@@ -1974,8 +2256,8 @@ fn whileExpr(
19742256 // are no jumps to it. This happens when the last statement of a while body is noreturn
19752257 // and there are no `continue` statements.
19762258 // The "repeat" at the end of a loop body is implied.
1977 if (while_node.continue_expr) |cont_expr| {
1978 _ = try expr(mod, &loop_scope.base, .{ .ty = void_type }, cont_expr);
2259 if (while_full.ast.cont_expr != 0) {
2260 _ = try expr(mod, &loop_scope.base, .{ .ty = void_type }, while_full.ast.cont_expr);
19792261 }
19802262 const loop = try scope.arena().create(zir.Inst.Loop);
19812263 loop.* = .{
......@@ -1995,14 +2277,14 @@ fn whileExpr(
19952277 });
19962278 loop_scope.break_block = while_block;
19972279 loop_scope.continue_block = cond_block;
1998 if (while_node.label) |some| {
2280 if (while_full.label_token) |label_token| {
19992281 loop_scope.label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{
2000 .token = some,
2282 .token = label_token,
20012283 .block_inst = while_block,
20022284 });
20032285 }
20042286
2005 const then_src = tree.token_locs[while_node.body.lastToken()].start;
2287 const then_src = token_starts[tree.lastToken(while_full.ast.then_expr)];
20062288 var then_scope: Scope.GenZIR = .{
20072289 .parent = &continue_scope.base,
20082290 .decl = continue_scope.decl,
......@@ -2012,11 +2294,10 @@ fn whileExpr(
20122294 };
20132295 defer then_scope.instructions.deinit(mod.gpa);
20142296
2015 // declare payload to the then_scope
2016 const then_sub_scope = try cond_kind.thenSubScope(mod, &then_scope, then_src, while_node.payload);
2297 const then_sub_scope = &then_scope.base;
20172298
20182299 loop_scope.break_count += 1;
2019 const then_result = try expr(mod, then_sub_scope, loop_scope.break_result_loc, while_node.body);
2300 const then_result = try expr(mod, then_sub_scope, loop_scope.break_result_loc, while_full.ast.then_expr);
20202301
20212302 var else_scope: Scope.GenZIR = .{
20222303 .parent = &continue_scope.base,
......@@ -2027,21 +2308,23 @@ fn whileExpr(
20272308 };
20282309 defer else_scope.instructions.deinit(mod.gpa);
20292310
2030 var else_src: usize = undefined;
2031 const else_result: ?*zir.Inst = if (while_node.@"else") |else_node| blk: {
2032 else_src = tree.token_locs[else_node.body.lastToken()].start;
2033 // declare payload to the then_scope
2034 const else_sub_scope = try cond_kind.elseSubScope(mod, &else_scope, else_src, else_node.payload);
2035
2311 const else_node = while_full.ast.else_expr;
2312 const else_info: struct { src: usize, result: ?*zir.Inst } = if (else_node != 0) blk: {
20362313 loop_scope.break_count += 1;
2037 break :blk try expr(mod, else_sub_scope, loop_scope.break_result_loc, else_node.body);
2038 } else blk: {
2039 else_src = tree.token_locs[while_node.lastToken()].start;
2040 break :blk null;
2041 };
2314 const sub_scope = &else_scope.base;
2315 break :blk .{
2316 .src = token_starts[tree.lastToken(else_node)],
2317 .result = try expr(mod, sub_scope, loop_scope.break_result_loc, else_node),
2318 };
2319 } else
2320 .{
2321 .src = token_starts[tree.lastToken(while_full.ast.then_expr)],
2322 .result = null,
2323 };
2324
20422325 if (loop_scope.label) |some| {
20432326 if (!some.used) {
2044 return mod.fail(scope, tree.token_locs[some.token].start, "unused while label", .{});
2327 return mod.fail(scope, token_starts[some.token], "unused while loop label", .{});
20452328 }
20462329 }
20472330 return finishThenElseBlock(
......@@ -2054,9 +2337,9 @@ fn whileExpr(
20542337 &condbr.positionals.then_body,
20552338 &condbr.positionals.else_body,
20562339 then_src,
2057 else_src,
2340 else_info.src,
20582341 then_result,
2059 else_result,
2342 else_info.result,
20602343 while_block,
20612344 cond_block,
20622345 );
......@@ -2066,18 +2349,23 @@ fn forExpr(
20662349 mod: *Module,
20672350 scope: *Scope,
20682351 rl: ResultLoc,
2069 for_node: *ast.Node.For,
2352 for_full: ast.full.While,
20702353) InnerError!*zir.Inst {
2071 if (for_node.label) |label| {
2072 try checkLabelRedefinition(mod, scope, label);
2354 if (for_full.label_token) |label_token| {
2355 try checkLabelRedefinition(mod, scope, label_token);
20732356 }
20742357
2075 if (for_node.inline_token) |tok|
2076 return mod.failTok(scope, tok, "TODO inline for", .{});
2358 if (for_full.inline_token) |inline_token| {
2359 return mod.failTok(scope, inline_token, "TODO inline for", .{});
2360 }
20772361
2078 // setup variables and constants
2362 // Set up variables and constants.
20792363 const tree = scope.tree();
2080 const for_src = tree.token_locs[for_node.for_token].start;
2364 const main_tokens = tree.nodes.items(.main_token);
2365 const token_starts = tree.tokens.items(.start);
2366 const token_tags = tree.tokens.items(.tag);
2367
2368 const for_src = token_starts[for_full.ast.while_token];
20812369 const index_ptr = blk: {
20822370 const usize_type = try addZIRInstConst(mod, scope, for_src, .{
20832371 .ty = Type.initTag(.type),
......@@ -2092,8 +2380,8 @@ fn forExpr(
20922380 _ = try addZIRBinOp(mod, scope, for_src, .store, index_ptr, zero);
20932381 break :blk index_ptr;
20942382 };
2095 const array_ptr = try expr(mod, scope, .ref, for_node.array_expr);
2096 const cond_src = tree.token_locs[for_node.array_expr.firstToken()].start;
2383 const array_ptr = try expr(mod, scope, .ref, for_full.ast.cond_expr);
2384 const cond_src = token_starts[tree.firstToken(for_full.ast.cond_expr)];
20972385 const len = try addZIRUnOp(mod, scope, cond_src, .indexable_ptr_len, array_ptr);
20982386
20992387 var loop_scope: Scope.GenZIR = .{
......@@ -2155,15 +2443,15 @@ fn forExpr(
21552443 });
21562444 loop_scope.break_block = for_block;
21572445 loop_scope.continue_block = cond_block;
2158 if (for_node.label) |some| {
2446 if (for_full.label_token) |label_token| {
21592447 loop_scope.label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{
2160 .token = some,
2448 .token = label_token,
21612449 .block_inst = for_block,
21622450 });
21632451 }
21642452
21652453 // while body
2166 const then_src = tree.token_locs[for_node.body.lastToken()].start;
2454 const then_src = token_starts[tree.lastToken(for_full.ast.then_expr)];
21672455 var then_scope: Scope.GenZIR = .{
21682456 .parent = &cond_scope.base,
21692457 .decl = cond_scope.decl,
......@@ -2175,23 +2463,27 @@ fn forExpr(
21752463
21762464 var index_scope: Scope.LocalPtr = undefined;
21772465 const then_sub_scope = blk: {
2178 const payload = for_node.payload.castTag(.PointerIndexPayload).?;
2179 const is_ptr = payload.ptr_token != null;
2180 const value_name = tree.tokenSlice(payload.value_symbol.firstToken());
2466 const payload_token = for_full.payload_token.?;
2467 const ident = if (token_tags[payload_token] == .asterisk)
2468 payload_token + 1
2469 else
2470 payload_token;
2471 const is_ptr = ident != payload_token;
2472 const value_name = tree.tokenSlice(ident);
21812473 if (!mem.eql(u8, value_name, "_")) {
2182 return mod.failNode(&then_scope.base, payload.value_symbol, "TODO implement for value payload", .{});
2474 return mod.failNode(&then_scope.base, ident, "TODO implement for loop value payload", .{});
21832475 } else if (is_ptr) {
2184 return mod.failTok(&then_scope.base, payload.ptr_token.?, "pointer modifier invalid on discard", .{});
2476 return mod.failTok(&then_scope.base, payload_token, "pointer modifier invalid on discard", .{});
21852477 }
21862478
2187 const index_symbol_node = payload.index_symbol orelse
2188 break :blk &then_scope.base;
2189
2190 const index_name = tree.tokenSlice(index_symbol_node.firstToken());
2191 if (mem.eql(u8, index_name, "_")) {
2479 const index_token = if (token_tags[ident + 1] == .comma)
2480 ident + 2
2481 else
21922482 break :blk &then_scope.base;
2483 if (mem.eql(u8, tree.tokenSlice(index_token), "_")) {
2484 return mod.failTok(&then_scope.base, index_token, "discard of index capture; omit it instead", .{});
21932485 }
2194 // TODO make this const without an extra copy?
2486 const index_name = try mod.identifierTokenString(&then_scope.base, index_token);
21952487 index_scope = .{
21962488 .parent = &then_scope.base,
21972489 .gen_zir = &then_scope,
......@@ -2202,7 +2494,7 @@ fn forExpr(
22022494 };
22032495
22042496 loop_scope.break_count += 1;
2205 const then_result = try expr(mod, then_sub_scope, loop_scope.break_result_loc, for_node.body);
2497 const then_result = try expr(mod, then_sub_scope, loop_scope.break_result_loc, for_full.ast.then_expr);
22062498
22072499 // else branch
22082500 var else_scope: Scope.GenZIR = .{
......@@ -2214,18 +2506,23 @@ fn forExpr(
22142506 };
22152507 defer else_scope.instructions.deinit(mod.gpa);
22162508
2217 var else_src: usize = undefined;
2218 const else_result: ?*zir.Inst = if (for_node.@"else") |else_node| blk: {
2219 else_src = tree.token_locs[else_node.body.lastToken()].start;
2509 const else_node = for_full.ast.else_expr;
2510 const else_info: struct { src: usize, result: ?*zir.Inst } = if (else_node != 0) blk: {
22202511 loop_scope.break_count += 1;
2221 break :blk try expr(mod, &else_scope.base, loop_scope.break_result_loc, else_node.body);
2222 } else blk: {
2223 else_src = tree.token_locs[for_node.lastToken()].start;
2224 break :blk null;
2225 };
2512 const sub_scope = &else_scope.base;
2513 break :blk .{
2514 .src = token_starts[tree.lastToken(else_node)],
2515 .result = try expr(mod, sub_scope, loop_scope.break_result_loc, else_node),
2516 };
2517 } else
2518 .{
2519 .src = token_starts[tree.lastToken(for_full.ast.then_expr)],
2520 .result = null,
2521 };
2522
22262523 if (loop_scope.label) |some| {
22272524 if (!some.used) {
2228 return mod.fail(scope, tree.token_locs[some.token].start, "unused for label", .{});
2525 return mod.fail(scope, token_starts[some.token], "unused for loop label", .{});
22292526 }
22302527 }
22312528 return finishThenElseBlock(
......@@ -2238,39 +2535,48 @@ fn forExpr(
22382535 &condbr.positionals.then_body,
22392536 &condbr.positionals.else_body,
22402537 then_src,
2241 else_src,
2538 else_info.src,
22422539 then_result,
2243 else_result,
2540 else_info.result,
22442541 for_block,
22452542 cond_block,
22462543 );
22472544}
22482545
2249fn switchCaseUsesRef(node: *ast.Node.Switch) bool {
2250 for (node.cases()) |uncasted_case| {
2251 const case = uncasted_case.castTag(.SwitchCase).?;
2252 const uncasted_payload = case.payload orelse continue;
2253 const payload = uncasted_payload.castTag(.PointerPayload).?;
2254 if (payload.ptr_token) |_| return true;
2255 }
2256 return false;
2257}
2258
2259fn getRangeNode(node: *ast.Node) ?*ast.Node.SimpleInfixOp {
2260 var cur = node;
2546fn getRangeNode(
2547 node_tags: []const ast.Node.Tag,
2548 node_datas: []const ast.Node.Data,
2549 start_node: ast.Node.Index,
2550) ?ast.Node.Index {
2551 var node = start_node;
22612552 while (true) {
2262 switch (cur.tag) {
2263 .Range => return @fieldParentPtr(ast.Node.SimpleInfixOp, "base", cur),
2264 .GroupedExpression => cur = @fieldParentPtr(ast.Node.GroupedExpression, "base", cur).expr,
2553 switch (node_tags[node]) {
2554 .switch_range => return node,
2555 .grouped_expression => node = node_datas[node].lhs,
22652556 else => return null,
22662557 }
22672558 }
22682559}
22692560
2270fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node.Switch) InnerError!*zir.Inst {
2561fn switchExpr(
2562 mod: *Module,
2563 scope: *Scope,
2564 rl: ResultLoc,
2565 switch_node: ast.Node.Index,
2566) InnerError!*zir.Inst {
22712567 const tree = scope.tree();
2272 const switch_src = tree.token_locs[switch_node.switch_token].start;
2273 const use_ref = switchCaseUsesRef(switch_node);
2568 const node_datas = tree.nodes.items(.data);
2569 const main_tokens = tree.nodes.items(.main_token);
2570 const token_tags = tree.tokens.items(.tag);
2571 const token_starts = tree.tokens.items(.start);
2572 const node_tags = tree.nodes.items(.tag);
2573
2574 const switch_token = main_tokens[switch_node];
2575 const target_node = node_datas[switch_node].lhs;
2576 const extra = tree.extraData(node_datas[switch_node].rhs, ast.Node.SubRange);
2577 const case_nodes = tree.extra_data[extra.start..extra.end];
2578
2579 const switch_src = token_starts[switch_token];
22742580
22752581 var block_scope: Scope.GenZIR = .{
22762582 .parent = scope,
......@@ -2285,18 +2591,26 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
22852591 var items = std.ArrayList(*zir.Inst).init(mod.gpa);
22862592 defer items.deinit();
22872593
2288 // first we gather all the switch items and check else/'_' prongs
2594 // First we gather all the switch items and check else/'_' prongs.
22892595 var else_src: ?usize = null;
22902596 var underscore_src: ?usize = null;
22912597 var first_range: ?*zir.Inst = null;
22922598 var simple_case_count: usize = 0;
2293 for (switch_node.cases()) |uncasted_case| {
2294 const case = uncasted_case.castTag(.SwitchCase).?;
2295 const case_src = tree.token_locs[case.firstToken()].start;
2296 assert(case.items_len != 0);
2297
2599 var any_payload_is_ref = false;
2600 for (case_nodes) |case_node| {
2601 const case = switch (node_tags[case_node]) {
2602 .switch_case_one => tree.switchCaseOne(case_node),
2603 .switch_case => tree.switchCase(case_node),
2604 else => unreachable,
2605 };
2606 if (case.payload_token) |payload_token| {
2607 if (token_tags[payload_token] == .asterisk) {
2608 any_payload_is_ref = true;
2609 }
2610 }
22982611 // Check for else/_ prong, those are handled last.
2299 if (case.items_len == 1 and case.items()[0].tag == .SwitchElse) {
2612 if (case.ast.values.len == 0) {
2613 const case_src = token_starts[case.ast.arrow_token - 1];
23002614 if (else_src) |src| {
23012615 const msg = msg: {
23022616 const msg = try mod.errMsg(
......@@ -2313,9 +2627,11 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
23132627 }
23142628 else_src = case_src;
23152629 continue;
2316 } else if (case.items_len == 1 and case.items()[0].tag == .Identifier and
2317 mem.eql(u8, tree.tokenSlice(case.items()[0].firstToken()), "_"))
2630 } else if (case.ast.values.len == 1 and
2631 node_tags[case.ast.values[0]] == .identifier and
2632 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"))
23182633 {
2634 const case_src = token_starts[case.ast.arrow_token - 1];
23192635 if (underscore_src) |src| {
23202636 const msg = msg: {
23212637 const msg = try mod.errMsg(
......@@ -2352,14 +2668,18 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
23522668 }
23532669 }
23542670
2355 if (case.items_len == 1 and getRangeNode(case.items()[0]) == null) simple_case_count += 1;
2671 if (case.ast.values.len == 1 and
2672 getRangeNode(node_tags, node_datas, case.ast.values[0]) == null)
2673 {
2674 simple_case_count += 1;
2675 }
23562676
2357 // generate all the switch items as comptime expressions
2358 for (case.items()) |item| {
2359 if (getRangeNode(item)) |range| {
2360 const start = try comptimeExpr(mod, &block_scope.base, .none, range.lhs);
2361 const end = try comptimeExpr(mod, &block_scope.base, .none, range.rhs);
2362 const range_src = tree.token_locs[range.op_token].start;
2677 // Generate all the switch items as comptime expressions.
2678 for (case.ast.values) |item| {
2679 if (getRangeNode(node_tags, node_datas, item)) |range| {
2680 const start = try comptimeExpr(mod, &block_scope.base, .none, node_datas[range].lhs);
2681 const end = try comptimeExpr(mod, &block_scope.base, .none, node_datas[range].rhs);
2682 const range_src = token_starts[main_tokens[range]];
23632683 const range_inst = try addZIRBinOp(mod, &block_scope.base, range_src, .switch_range, start, end);
23642684 try items.append(range_inst);
23652685 } else {
......@@ -2374,21 +2694,25 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
23742694 if (underscore_src != null) special_prong = .underscore;
23752695 var cases = try block_scope.arena.alloc(zir.Inst.SwitchBr.Case, simple_case_count);
23762696
2377 const target_ptr = if (use_ref) try expr(mod, &block_scope.base, .ref, switch_node.expr) else null;
2378 const target = if (target_ptr) |some|
2379 try addZIRUnOp(mod, &block_scope.base, some.src, .deref, some)
2697 const rl_and_tag: struct { rl: ResultLoc, tag: zir.Inst.Tag } = if (any_payload_is_ref)
2698 .{
2699 .rl = .ref,
2700 .tag = .switchbr_ref,
2701 }
23802702 else
2381 try expr(mod, &block_scope.base, .none, switch_node.expr);
2382 const switch_inst = try addZIRInst(mod, &block_scope.base, switch_src, zir.Inst.SwitchBr, .{
2703 .{
2704 .rl = .none,
2705 .tag = .switchbr,
2706 };
2707 const target = try expr(mod, &block_scope.base, rl_and_tag.rl, target_node);
2708 const switch_inst = try addZirInstT(mod, &block_scope.base, switch_src, zir.Inst.SwitchBr, rl_and_tag.tag, .{
23832709 .target = target,
23842710 .cases = cases,
23852711 .items = try block_scope.arena.dupe(*zir.Inst, items.items),
23862712 .else_body = undefined, // populated below
2387 }, .{
23882713 .range = first_range,
23892714 .special_prong = special_prong,
23902715 });
2391
23922716 const block = try addZIRInstBlock(mod, scope, switch_src, .block, .{
23932717 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
23942718 });
......@@ -2411,32 +2735,38 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
24112735 };
24122736 defer else_scope.instructions.deinit(mod.gpa);
24132737
2414 // Now generate all but the special cases
2415 var special_case: ?*ast.Node.SwitchCase = null;
2738 // Now generate all but the special cases.
2739 var special_case: ?ast.full.SwitchCase = null;
24162740 var items_index: usize = 0;
24172741 var case_index: usize = 0;
2418 for (switch_node.cases()) |uncasted_case| {
2419 const case = uncasted_case.castTag(.SwitchCase).?;
2420 const case_src = tree.token_locs[case.firstToken()].start;
2421 // reset without freeing to reduce allocations.
2422 case_scope.instructions.items.len = 0;
2742 for (case_nodes) |case_node| {
2743 const case = switch (node_tags[case_node]) {
2744 .switch_case_one => tree.switchCaseOne(case_node),
2745 .switch_case => tree.switchCase(case_node),
2746 else => unreachable,
2747 };
2748 const case_src = token_starts[main_tokens[case_node]];
2749 case_scope.instructions.shrinkRetainingCapacity(0);
24232750
24242751 // Check for else/_ prong, those are handled last.
2425 if (case.items_len == 1 and case.items()[0].tag == .SwitchElse) {
2752 if (case.ast.values.len == 0) {
24262753 special_case = case;
24272754 continue;
2428 } else if (case.items_len == 1 and case.items()[0].tag == .Identifier and
2429 mem.eql(u8, tree.tokenSlice(case.items()[0].firstToken()), "_"))
2755 } else if (case.ast.values.len == 1 and
2756 node_tags[case.ast.values[0]] == .identifier and
2757 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"))
24302758 {
24312759 special_case = case;
24322760 continue;
24332761 }
24342762
24352763 // If this is a simple one item prong then it is handled by the switchbr.
2436 if (case.items_len == 1 and getRangeNode(case.items()[0]) == null) {
2764 if (case.ast.values.len == 1 and
2765 getRangeNode(node_tags, node_datas, case.ast.values[0]) == null)
2766 {
24372767 const item = items.items[items_index];
24382768 items_index += 1;
2439 try switchCaseExpr(mod, &case_scope.base, block_scope.break_result_loc, block, case, target, target_ptr);
2769 try switchCaseExpr(mod, &case_scope.base, block_scope.break_result_loc, block, case, target);
24402770
24412771 cases[case_index] = .{
24422772 .item = item,
......@@ -2446,16 +2776,14 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
24462776 continue;
24472777 }
24482778
2449 // TODO if the case has few items and no ranges it might be better
2450 // to just handle them as switch prongs.
2451
24522779 // Check if the target matches any of the items.
24532780 // 1, 2, 3..6 will result in
24542781 // target == 1 or target == 2 or (target >= 3 and target <= 6)
2782 // TODO handle multiple items as switch prongs rather than along with ranges.
24552783 var any_ok: ?*zir.Inst = null;
2456 for (case.items()) |item| {
2457 if (getRangeNode(item)) |range| {
2458 const range_src = tree.token_locs[range.op_token].start;
2784 for (case.ast.values) |item| {
2785 if (getRangeNode(node_tags, node_datas, item)) |range| {
2786 const range_src = token_starts[main_tokens[range]];
24592787 const range_inst = items.items[items_index].castTag(.switch_range).?;
24602788 items_index += 1;
24612789
......@@ -2494,7 +2822,7 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
24942822
24952823 // reset cond_scope for then_body
24962824 case_scope.instructions.items.len = 0;
2497 try switchCaseExpr(mod, &case_scope.base, block_scope.break_result_loc, block, case, target, target_ptr);
2825 try switchCaseExpr(mod, &case_scope.base, block_scope.break_result_loc, block, case, target);
24982826 condbr.positionals.then_body = .{
24992827 .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items),
25002828 };
......@@ -2511,12 +2839,12 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
25112839
25122840 // Finally generate else block or a break.
25132841 if (special_case) |case| {
2514 try switchCaseExpr(mod, &else_scope.base, block_scope.break_result_loc, block, case, target, target_ptr);
2842 try switchCaseExpr(mod, &else_scope.base, block_scope.break_result_loc, block, case, target);
25152843 } else {
25162844 // Not handling all possible cases is a compile error.
25172845 _ = try addZIRNoOp(mod, &else_scope.base, switch_src, .unreachable_unsafe);
25182846 }
2519 switch_inst.castTag(.switchbr).?.positionals.else_body = .{
2847 switch_inst.positionals.else_body = .{
25202848 .instructions = try block_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),
25212849 };
25222850
......@@ -2528,27 +2856,34 @@ fn switchCaseExpr(
25282856 scope: *Scope,
25292857 rl: ResultLoc,
25302858 block: *zir.Inst.Block,
2531 case: *ast.Node.SwitchCase,
2859 case: ast.full.SwitchCase,
25322860 target: *zir.Inst,
2533 target_ptr: ?*zir.Inst,
25342861) !void {
25352862 const tree = scope.tree();
2536 const case_src = tree.token_locs[case.firstToken()].start;
2863 const node_datas = tree.nodes.items(.data);
2864 const main_tokens = tree.nodes.items(.main_token);
2865 const token_starts = tree.tokens.items(.start);
2866 const token_tags = tree.tokens.items(.tag);
2867
2868 const case_src = token_starts[case.ast.arrow_token];
25372869 const sub_scope = blk: {
2538 const uncasted_payload = case.payload orelse break :blk scope;
2539 const payload = uncasted_payload.castTag(.PointerPayload).?;
2540 const is_ptr = payload.ptr_token != null;
2541 const value_name = tree.tokenSlice(payload.value_symbol.firstToken());
2870 const payload_token = case.payload_token orelse break :blk scope;
2871 const ident = if (token_tags[payload_token] == .asterisk)
2872 payload_token + 1
2873 else
2874 payload_token;
2875 const is_ptr = ident != payload_token;
2876 const value_name = tree.tokenSlice(ident);
25422877 if (mem.eql(u8, value_name, "_")) {
25432878 if (is_ptr) {
2544 return mod.failTok(scope, payload.ptr_token.?, "pointer modifier invalid on discard", .{});
2879 return mod.failTok(scope, payload_token, "pointer modifier invalid on discard", .{});
25452880 }
25462881 break :blk scope;
25472882 }
2548 return mod.failNode(scope, payload.value_symbol, "TODO implement switch value payload", .{});
2883 return mod.failTok(scope, ident, "TODO implement switch value payload", .{});
25492884 };
25502885
2551 const case_body = try expr(mod, sub_scope, rl, case.expr);
2886 const case_body = try expr(mod, sub_scope, rl, case.ast.target_expr);
25522887 if (!case_body.tag.isNoReturn()) {
25532888 _ = try addZIRInst(mod, sub_scope, case_src, zir.Inst.Break, .{
25542889 .block = block,
......@@ -2557,11 +2892,16 @@ fn switchCaseExpr(
25572892 }
25582893}
25592894
2560fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst {
2895fn ret(mod: *Module, scope: *Scope, node: ast.Node.Index) InnerError!*zir.Inst {
25612896 const tree = scope.tree();
2562 const src = tree.token_locs[cfe.ltoken].start;
2563 if (cfe.getRHS()) |rhs_node| {
2564 if (nodeMayNeedMemoryLocation(rhs_node, scope)) {
2897 const node_datas = tree.nodes.items(.data);
2898 const main_tokens = tree.nodes.items(.main_token);
2899 const token_starts = tree.tokens.items(.start);
2900
2901 const src = token_starts[main_tokens[node]];
2902 const rhs_node = node_datas[node].lhs;
2903 if (rhs_node != 0) {
2904 if (nodeMayNeedMemoryLocation(scope, rhs_node)) {
25652905 const ret_ptr = try addZIRNoOp(mod, scope, src, .ret_ptr);
25662906 const operand = try expr(mod, scope, .{ .ptr = ret_ptr }, rhs_node);
25672907 return addZIRUnOp(mod, scope, src, .@"return", operand);
......@@ -2575,19 +2915,31 @@ fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerE
25752915 }
25762916}
25772917
2578fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneToken) InnerError!*zir.Inst {
2918fn identifier(
2919 mod: *Module,
2920 scope: *Scope,
2921 rl: ResultLoc,
2922 ident: ast.Node.Index,
2923) InnerError!*zir.Inst {
25792924 const tracy = trace(@src());
25802925 defer tracy.end();
25812926
25822927 const tree = scope.tree();
2583 const ident_name = try mod.identifierTokenString(scope, ident.token);
2584 const src = tree.token_locs[ident.token].start;
2928 const main_tokens = tree.nodes.items(.main_token);
2929 const token_starts = tree.tokens.items(.start);
2930
2931 const ident_token = main_tokens[ident];
2932 const ident_name = try mod.identifierTokenString(scope, ident_token);
2933 const src = token_starts[ident_token];
25852934 if (mem.eql(u8, ident_name, "_")) {
2586 return mod.failNode(scope, &ident.base, "TODO implement '_' identifier", .{});
2935 return mod.failNode(scope, ident, "TODO implement '_' identifier", .{});
25872936 }
25882937
2589 if (getSimplePrimitiveValue(ident_name)) |typed_value| {
2590 const result = try addZIRInstConst(mod, scope, src, typed_value);
2938 if (simple_types.get(ident_name)) |val_tag| {
2939 const result = try addZIRInstConst(mod, scope, src, TypedValue{
2940 .ty = Type.initTag(.type),
2941 .val = Value.initTag(val_tag),
2942 });
25912943 return rvalue(mod, scope, rl, result);
25922944 }
25932945
......@@ -2598,7 +2950,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo
25982950 const bit_count = std.fmt.parseInt(u16, ident_name[1..], 10) catch |err| switch (err) {
25992951 error.Overflow => return mod.failNode(
26002952 scope,
2601 &ident.base,
2953 ident,
26022954 "primitive integer type '{s}' exceeds maximum bit width of 65535",
26032955 .{ident_name},
26042956 ),
......@@ -2662,59 +3014,104 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo
26623014 }
26633015 }
26643016
2665 return mod.failNode(scope, &ident.base, "use of undeclared identifier '{s}'", .{ident_name});
3017 return mod.failNode(scope, ident, "use of undeclared identifier '{s}'", .{ident_name});
26663018}
26673019
2668fn stringLiteral(mod: *Module, scope: *Scope, str_lit: *ast.Node.OneToken) InnerError!*zir.Inst {
3020fn parseStringLiteral(mod: *Module, scope: *Scope, token: ast.TokenIndex) ![]u8 {
26693021 const tree = scope.tree();
2670 const unparsed_bytes = tree.tokenSlice(str_lit.token);
3022 const token_tags = tree.tokens.items(.tag);
3023 const token_starts = tree.tokens.items(.start);
3024 assert(token_tags[token] == .string_literal);
3025 const unparsed = tree.tokenSlice(token);
26713026 const arena = scope.arena();
2672
26733027 var bad_index: usize = undefined;
2674 const bytes = std.zig.parseStringLiteral(arena, unparsed_bytes, &bad_index) catch |err| switch (err) {
3028 const bytes = std.zig.parseStringLiteral(arena, unparsed, &bad_index) catch |err| switch (err) {
26753029 error.InvalidCharacter => {
2676 const bad_byte = unparsed_bytes[bad_index];
2677 const src = tree.token_locs[str_lit.token].start;
2678 return mod.fail(scope, src + bad_index, "invalid string literal character: '{c}'\n", .{bad_byte});
3030 const bad_byte = unparsed[bad_index];
3031 const src = token_starts[token];
3032 return mod.fail(scope, src + bad_index, "invalid string literal character: '{c}'", .{
3033 bad_byte,
3034 });
26793035 },
26803036 else => |e| return e,
26813037 };
2682
2683 const src = tree.token_locs[str_lit.token].start;
2684 return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{});
3038 return bytes;
26853039}
26863040
2687fn multilineStrLiteral(mod: *Module, scope: *Scope, node: *ast.Node.MultilineStringLiteral) !*zir.Inst {
3041fn stringLiteral(
3042 mod: *Module,
3043 scope: *Scope,
3044 rl: ResultLoc,
3045 str_lit: ast.Node.Index,
3046) InnerError!*zir.Inst {
26883047 const tree = scope.tree();
2689 const lines = node.linesConst();
2690 const src = tree.token_locs[lines[0]].start;
2691
2692 // line lengths and new lines
2693 var len = lines.len - 1;
2694 for (lines) |line| {
2695 // 2 for the '//' + 1 for '\n'
2696 len += tree.tokenSlice(line).len - 3;
2697 }
3048 const main_tokens = tree.nodes.items(.main_token);
3049 const token_starts = tree.tokens.items(.start);
3050
3051 const str_lit_token = main_tokens[str_lit];
3052 const bytes = try parseStringLiteral(mod, scope, str_lit_token);
3053 const src = token_starts[str_lit_token];
3054 const str_inst = try addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{});
3055 return rvalue(mod, scope, rl, str_inst);
3056}
26983057
2699 const bytes = try scope.arena().alloc(u8, len);
2700 var i: usize = 0;
2701 for (lines) |line, line_i| {
2702 if (line_i != 0) {
2703 bytes[i] = '\n';
2704 i += 1;
3058fn multilineStringLiteral(
3059 mod: *Module,
3060 scope: *Scope,
3061 rl: ResultLoc,
3062 str_lit: ast.Node.Index,
3063) InnerError!*zir.Inst {
3064 const tree = scope.tree();
3065 const node_datas = tree.nodes.items(.data);
3066 const main_tokens = tree.nodes.items(.main_token);
3067 const token_starts = tree.tokens.items(.start);
3068
3069 const start = node_datas[str_lit].lhs;
3070 const end = node_datas[str_lit].rhs;
3071
3072 // Count the number of bytes to allocate.
3073 const len: usize = len: {
3074 var tok_i = start;
3075 var len: usize = end - start + 1;
3076 while (tok_i <= end) : (tok_i += 1) {
3077 // 2 for the '//' + 1 for '\n'
3078 len += tree.tokenSlice(tok_i).len - 3;
27053079 }
2706 const slice = tree.tokenSlice(line);
2707 mem.copy(u8, bytes[i..], slice[2 .. slice.len - 1]);
2708 i += slice.len - 3;
3080 break :len len;
3081 };
3082 const bytes = try scope.arena().alloc(u8, len);
3083 // First line: do not append a newline.
3084 var byte_i: usize = 0;
3085 var tok_i = start;
3086 {
3087 const slice = tree.tokenSlice(tok_i);
3088 const line_bytes = slice[2 .. slice.len - 1];
3089 mem.copy(u8, bytes[byte_i..], line_bytes);
3090 byte_i += line_bytes.len;
3091 tok_i += 1;
27093092 }
2710
2711 return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{});
3093 // Following lines: each line prepends a newline.
3094 while (tok_i <= end) : (tok_i += 1) {
3095 bytes[byte_i] = '\n';
3096 byte_i += 1;
3097 const slice = tree.tokenSlice(tok_i);
3098 const line_bytes = slice[2 .. slice.len - 1];
3099 mem.copy(u8, bytes[byte_i..], line_bytes);
3100 byte_i += line_bytes.len;
3101 }
3102 const src = token_starts[start];
3103 const str_inst = try addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{});
3104 return rvalue(mod, scope, rl, str_inst);
27123105}
27133106
2714fn charLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) !*zir.Inst {
3107fn charLiteral(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !*zir.Inst {
27153108 const tree = scope.tree();
2716 const src = tree.token_locs[node.token].start;
2717 const slice = tree.tokenSlice(node.token);
3109 const main_tokens = tree.nodes.items(.main_token);
3110 const main_token = main_tokens[node];
3111 const token_starts = tree.tokens.items(.start);
3112
3113 const src = token_starts[main_token];
3114 const slice = tree.tokenSlice(main_token);
27183115
27193116 var bad_index: usize = undefined;
27203117 const value = std.zig.parseCharLiteral(slice, &bad_index) catch |err| switch (err) {
......@@ -2723,18 +3120,27 @@ fn charLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) !*zir.Inst
27233120 return mod.fail(scope, src + bad_index, "invalid character: '{c}'\n", .{bad_byte});
27243121 },
27253122 };
2726
2727 return addZIRInstConst(mod, scope, src, .{
3123 const result = try addZIRInstConst(mod, scope, src, .{
27283124 .ty = Type.initTag(.comptime_int),
27293125 .val = try Value.Tag.int_u64.create(scope.arena(), value),
27303126 });
3127 return rvalue(mod, scope, rl, result);
27313128}
27323129
2733fn integerLiteral(mod: *Module, scope: *Scope, int_lit: *ast.Node.OneToken) InnerError!*zir.Inst {
3130fn integerLiteral(
3131 mod: *Module,
3132 scope: *Scope,
3133 rl: ResultLoc,
3134 int_lit: ast.Node.Index,
3135) InnerError!*zir.Inst {
27343136 const arena = scope.arena();
27353137 const tree = scope.tree();
2736 const prefixed_bytes = tree.tokenSlice(int_lit.token);
2737 const base = if (mem.startsWith(u8, prefixed_bytes, "0x"))
3138 const main_tokens = tree.nodes.items(.main_token);
3139 const token_starts = tree.tokens.items(.start);
3140
3141 const int_token = main_tokens[int_lit];
3142 const prefixed_bytes = tree.tokenSlice(int_token);
3143 const base: u8 = if (mem.startsWith(u8, prefixed_bytes, "0x"))
27383144 16
27393145 else if (mem.startsWith(u8, prefixed_bytes, "0o"))
27403146 8
......@@ -2749,90 +3155,70 @@ fn integerLiteral(mod: *Module, scope: *Scope, int_lit: *ast.Node.OneToken) Inne
27493155 prefixed_bytes[2..];
27503156
27513157 if (std.fmt.parseInt(u64, bytes, base)) |small_int| {
2752 const src = tree.token_locs[int_lit.token].start;
2753 return addZIRInstConst(mod, scope, src, .{
3158 const src = token_starts[int_token];
3159 const result = try addZIRInstConst(mod, scope, src, .{
27543160 .ty = Type.initTag(.comptime_int),
27553161 .val = try Value.Tag.int_u64.create(arena, small_int),
27563162 });
3163 return rvalue(mod, scope, rl, result);
27573164 } else |err| {
2758 return mod.failTok(scope, int_lit.token, "TODO implement int literals that don't fit in a u64", .{});
3165 return mod.failTok(scope, int_token, "TODO implement int literals that don't fit in a u64", .{});
27593166 }
27603167}
27613168
2762fn floatLiteral(mod: *Module, scope: *Scope, float_lit: *ast.Node.OneToken) InnerError!*zir.Inst {
3169fn floatLiteral(
3170 mod: *Module,
3171 scope: *Scope,
3172 rl: ResultLoc,
3173 float_lit: ast.Node.Index,
3174) InnerError!*zir.Inst {
27633175 const arena = scope.arena();
27643176 const tree = scope.tree();
2765 const bytes = tree.tokenSlice(float_lit.token);
3177 const main_tokens = tree.nodes.items(.main_token);
3178 const token_starts = tree.tokens.items(.start);
3179
3180 const main_token = main_tokens[float_lit];
3181 const bytes = tree.tokenSlice(main_token);
27663182 if (bytes.len > 2 and bytes[1] == 'x') {
2767 return mod.failTok(scope, float_lit.token, "TODO hex floats", .{});
3183 return mod.failTok(scope, main_token, "TODO implement hex floats", .{});
27683184 }
2769
27703185 const float_number = std.fmt.parseFloat(f128, bytes) catch |e| switch (e) {
27713186 error.InvalidCharacter => unreachable, // validated by tokenizer
27723187 };
2773 const src = tree.token_locs[float_lit.token].start;
2774 return addZIRInstConst(mod, scope, src, .{
3188 const src = token_starts[main_token];
3189 const result = try addZIRInstConst(mod, scope, src, .{
27753190 .ty = Type.initTag(.comptime_float),
27763191 .val = try Value.Tag.float_128.create(arena, float_number),
27773192 });
3193 return rvalue(mod, scope, rl, result);
27783194}
27793195
2780fn undefLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
2781 const arena = scope.arena();
2782 const tree = scope.tree();
2783 const src = tree.token_locs[node.token].start;
2784 return addZIRInstConst(mod, scope, src, .{
2785 .ty = Type.initTag(.@"undefined"),
2786 .val = Value.initTag(.undef),
2787 });
2788}
2789
2790fn boolLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
2791 const arena = scope.arena();
2792 const tree = scope.tree();
2793 const src = tree.token_locs[node.token].start;
2794 return addZIRInstConst(mod, scope, src, .{
2795 .ty = Type.initTag(.bool),
2796 .val = switch (tree.token_ids[node.token]) {
2797 .Keyword_true => Value.initTag(.bool_true),
2798 .Keyword_false => Value.initTag(.bool_false),
2799 else => unreachable,
2800 },
2801 });
2802}
2803
2804fn nullLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
3196fn asmExpr(mod: *Module, scope: *Scope, rl: ResultLoc, full: ast.full.Asm) InnerError!*zir.Inst {
28053197 const arena = scope.arena();
28063198 const tree = scope.tree();
2807 const src = tree.token_locs[node.token].start;
2808 return addZIRInstConst(mod, scope, src, .{
2809 .ty = Type.initTag(.@"null"),
2810 .val = Value.initTag(.null_value),
2811 });
2812}
3199 const main_tokens = tree.nodes.items(.main_token);
3200 const token_starts = tree.tokens.items(.start);
3201 const node_datas = tree.nodes.items(.data);
28133202
2814fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zir.Inst {
2815 if (asm_node.outputs.len != 0) {
2816 return mod.failNode(scope, &asm_node.base, "TODO implement asm with an output", .{});
3203 if (full.outputs.len != 0) {
3204 return mod.failTok(scope, full.ast.asm_token, "TODO implement asm with an output", .{});
28173205 }
2818 const arena = scope.arena();
2819 const tree = scope.tree();
28203206
2821 const inputs = try arena.alloc(*zir.Inst, asm_node.inputs.len);
2822 const args = try arena.alloc(*zir.Inst, asm_node.inputs.len);
2823
2824 const src = tree.token_locs[asm_node.asm_token].start;
3207 const inputs = try arena.alloc([]const u8, full.inputs.len);
3208 const args = try arena.alloc(*zir.Inst, full.inputs.len);
28253209
3210 const src = token_starts[full.ast.asm_token];
28263211 const str_type = try addZIRInstConst(mod, scope, src, .{
28273212 .ty = Type.initTag(.type),
28283213 .val = Value.initTag(.const_slice_u8_type),
28293214 });
28303215 const str_type_rl: ResultLoc = .{ .ty = str_type };
28313216
2832 for (asm_node.inputs) |input, i| {
3217 for (full.inputs) |input, i| {
28333218 // TODO semantically analyze constraints
2834 inputs[i] = try expr(mod, scope, str_type_rl, input.constraint);
2835 args[i] = try expr(mod, scope, .none, input.expr);
3219 const constraint_token = main_tokens[input] + 2;
3220 inputs[i] = try parseStringLiteral(mod, scope, constraint_token);
3221 args[i] = try expr(mod, scope, .none, node_datas[input].lhs);
28363222 }
28373223
28383224 const return_type = try addZIRInstConst(mod, scope, src, .{
......@@ -2840,81 +3226,47 @@ fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zi
28403226 .val = Value.initTag(.void_type),
28413227 });
28423228 const asm_inst = try addZIRInst(mod, scope, src, zir.Inst.Asm, .{
2843 .asm_source = try expr(mod, scope, str_type_rl, asm_node.template),
3229 .asm_source = try expr(mod, scope, str_type_rl, full.ast.template),
28443230 .return_type = return_type,
28453231 }, .{
2846 .@"volatile" = asm_node.volatile_token != null,
3232 .@"volatile" = full.volatile_token != null,
28473233 //.clobbers = TODO handle clobbers
28483234 .inputs = inputs,
28493235 .args = args,
28503236 });
2851 return asm_inst;
2852}
2853
2854fn ensureBuiltinParamCount(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall, count: u32) !void {
2855 if (call.params_len == count)
2856 return;
2857
2858 const s = if (count == 1) "" else "s";
2859 return mod.failTok(scope, call.builtin_token, "expected {d} parameter{s}, found {d}", .{ count, s, call.params_len });
2860}
2861
2862fn simpleCast(
2863 mod: *Module,
2864 scope: *Scope,
2865 rl: ResultLoc,
2866 call: *ast.Node.BuiltinCall,
2867 inst_tag: zir.Inst.Tag,
2868) InnerError!*zir.Inst {
2869 try ensureBuiltinParamCount(mod, scope, call, 2);
2870 const tree = scope.tree();
2871 const src = tree.token_locs[call.builtin_token].start;
2872 const params = call.params();
2873 const dest_type = try typeExpr(mod, scope, params[0]);
2874 const rhs = try expr(mod, scope, .none, params[1]);
2875 const result = try addZIRBinOp(mod, scope, src, inst_tag, dest_type, rhs);
2876 return rvalue(mod, scope, rl, result);
2877}
2878
2879fn ptrToInt(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
2880 try ensureBuiltinParamCount(mod, scope, call, 1);
2881 const operand = try expr(mod, scope, .none, call.params()[0]);
2882 const tree = scope.tree();
2883 const src = tree.token_locs[call.builtin_token].start;
2884 return addZIRUnOp(mod, scope, src, .ptrtoint, operand);
3237 return rvalue(mod, scope, rl, asm_inst);
28853238}
28863239
28873240fn as(
28883241 mod: *Module,
28893242 scope: *Scope,
28903243 rl: ResultLoc,
2891 call: *ast.Node.BuiltinCall,
3244 builtin_token: ast.TokenIndex,
3245 src: usize,
3246 lhs: ast.Node.Index,
3247 rhs: ast.Node.Index,
28923248) InnerError!*zir.Inst {
2893 try ensureBuiltinParamCount(mod, scope, call, 2);
2894 const tree = scope.tree();
2895 const src = tree.token_locs[call.builtin_token].start;
2896 const params = call.params();
2897 const dest_type = try typeExpr(mod, scope, params[0]);
3249 const dest_type = try typeExpr(mod, scope, lhs);
28983250 switch (rl) {
28993251 .none, .discard, .ref, .ty => {
2900 const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]);
3252 const result = try expr(mod, scope, .{ .ty = dest_type }, rhs);
29013253 return rvalue(mod, scope, rl, result);
29023254 },
29033255
29043256 .ptr => |result_ptr| {
2905 return asRlPtr(mod, scope, rl, src, result_ptr, params[1], dest_type);
3257 return asRlPtr(mod, scope, rl, src, result_ptr, rhs, dest_type);
29063258 },
29073259 .block_ptr => |block_scope| {
2908 return asRlPtr(mod, scope, rl, src, block_scope.rl_ptr.?, params[1], dest_type);
3260 return asRlPtr(mod, scope, rl, src, block_scope.rl_ptr.?, rhs, dest_type);
29093261 },
29103262
29113263 .bitcasted_ptr => |bitcasted_ptr| {
29123264 // TODO here we should be able to resolve the inference; we now have a type for the result.
2913 return mod.failTok(scope, call.builtin_token, "TODO implement @as with result location @bitCast", .{});
3265 return mod.failTok(scope, builtin_token, "TODO implement @as with result location @bitCast", .{});
29143266 },
29153267 .inferred_ptr => |result_alloc| {
29163268 // TODO here we should be able to resolve the inference; we now have a type for the result.
2917 return mod.failTok(scope, call.builtin_token, "TODO implement @as with inferred-type result location pointer", .{});
3269 return mod.failTok(scope, builtin_token, "TODO implement @as with inferred-type result location pointer", .{});
29183270 },
29193271 }
29203272}
......@@ -2925,7 +3277,7 @@ fn asRlPtr(
29253277 rl: ResultLoc,
29263278 src: usize,
29273279 result_ptr: *zir.Inst,
2928 operand_node: *ast.Node,
3280 operand_node: ast.Node.Index,
29293281 dest_type: *zir.Inst,
29303282) InnerError!*zir.Inst {
29313283 // Detect whether this expr() call goes into rvalue() to store the result into the
......@@ -2963,155 +3315,295 @@ fn asRlPtr(
29633315 }
29643316}
29653317
2966fn bitCast(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
2967 try ensureBuiltinParamCount(mod, scope, call, 2);
2968 const tree = scope.tree();
2969 const src = tree.token_locs[call.builtin_token].start;
2970 const params = call.params();
2971 const dest_type = try typeExpr(mod, scope, params[0]);
3318fn bitCast(
3319 mod: *Module,
3320 scope: *Scope,
3321 rl: ResultLoc,
3322 builtin_token: ast.TokenIndex,
3323 src: usize,
3324 lhs: ast.Node.Index,
3325 rhs: ast.Node.Index,
3326) InnerError!*zir.Inst {
3327 const dest_type = try typeExpr(mod, scope, lhs);
29723328 switch (rl) {
29733329 .none => {
2974 const operand = try expr(mod, scope, .none, params[1]);
3330 const operand = try expr(mod, scope, .none, rhs);
29753331 return addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand);
29763332 },
29773333 .discard => {
2978 const operand = try expr(mod, scope, .none, params[1]);
3334 const operand = try expr(mod, scope, .none, rhs);
29793335 const result = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand);
29803336 _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result);
29813337 return result;
29823338 },
29833339 .ref => {
2984 const operand = try expr(mod, scope, .ref, params[1]);
3340 const operand = try expr(mod, scope, .ref, rhs);
29853341 const result = try addZIRBinOp(mod, scope, src, .bitcast_ref, dest_type, operand);
29863342 return result;
29873343 },
29883344 .ty => |result_ty| {
2989 const result = try expr(mod, scope, .none, params[1]);
3345 const result = try expr(mod, scope, .none, rhs);
29903346 const bitcasted = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, result);
29913347 return addZIRBinOp(mod, scope, src, .as, result_ty, bitcasted);
29923348 },
29933349 .ptr => |result_ptr| {
29943350 const casted_result_ptr = try addZIRUnOp(mod, scope, src, .bitcast_result_ptr, result_ptr);
2995 return expr(mod, scope, .{ .bitcasted_ptr = casted_result_ptr.castTag(.bitcast_result_ptr).? }, params[1]);
3351 return expr(mod, scope, .{ .bitcasted_ptr = casted_result_ptr.castTag(.bitcast_result_ptr).? }, rhs);
29963352 },
29973353 .bitcasted_ptr => |bitcasted_ptr| {
2998 return mod.failTok(scope, call.builtin_token, "TODO implement @bitCast with result location another @bitCast", .{});
3354 return mod.failTok(scope, builtin_token, "TODO implement @bitCast with result location another @bitCast", .{});
29993355 },
30003356 .block_ptr => |block_ptr| {
3001 return mod.failTok(scope, call.builtin_token, "TODO implement @bitCast with result location inferred peer types", .{});
3357 return mod.failTok(scope, builtin_token, "TODO implement @bitCast with result location inferred peer types", .{});
30023358 },
30033359 .inferred_ptr => |result_alloc| {
30043360 // TODO here we should be able to resolve the inference; we now have a type for the result.
3005 return mod.failTok(scope, call.builtin_token, "TODO implement @bitCast with inferred-type result location pointer", .{});
3361 return mod.failTok(scope, builtin_token, "TODO implement @bitCast with inferred-type result location pointer", .{});
30063362 },
30073363 }
30083364}
30093365
3010fn import(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
3011 try ensureBuiltinParamCount(mod, scope, call, 1);
3012 const tree = scope.tree();
3013 const src = tree.token_locs[call.builtin_token].start;
3014 const params = call.params();
3015 const target = try expr(mod, scope, .none, params[0]);
3016 return addZIRUnOp(mod, scope, src, .import, target);
3017}
3018
3019fn compileError(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
3020 try ensureBuiltinParamCount(mod, scope, call, 1);
3021 const tree = scope.tree();
3022 const src = tree.token_locs[call.builtin_token].start;
3023 const params = call.params();
3024 const target = try expr(mod, scope, .none, params[0]);
3025 return addZIRUnOp(mod, scope, src, .compile_error, target);
3026}
3027
3028fn setEvalBranchQuota(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
3029 try ensureBuiltinParamCount(mod, scope, call, 1);
3030 const tree = scope.tree();
3031 const src = tree.token_locs[call.builtin_token].start;
3032 const params = call.params();
3033 const u32_type = try addZIRInstConst(mod, scope, src, .{
3034 .ty = Type.initTag(.type),
3035 .val = Value.initTag(.u32_type),
3036 });
3037 const quota = try expr(mod, scope, .{ .ty = u32_type }, params[0]);
3038 return addZIRUnOp(mod, scope, src, .set_eval_branch_quota, quota);
3039}
3040
3041fn typeOf(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
3042 const tree = scope.tree();
3043 const arena = scope.arena();
3044 const src = tree.token_locs[call.builtin_token].start;
3045 const params = call.params();
3366fn typeOf(
3367 mod: *Module,
3368 scope: *Scope,
3369 rl: ResultLoc,
3370 builtin_token: ast.TokenIndex,
3371 src: usize,
3372 params: []const ast.Node.Index,
3373) InnerError!*zir.Inst {
30463374 if (params.len < 1) {
3047 return mod.failTok(scope, call.builtin_token, "expected at least 1 argument, found 0", .{});
3375 return mod.failTok(scope, builtin_token, "expected at least 1 argument, found 0", .{});
30483376 }
30493377 if (params.len == 1) {
30503378 return rvalue(mod, scope, rl, try addZIRUnOp(mod, scope, src, .typeof, try expr(mod, scope, .none, params[0])));
30513379 }
3380 const arena = scope.arena();
30523381 var items = try arena.alloc(*zir.Inst, params.len);
30533382 for (params) |param, param_i|
30543383 items[param_i] = try expr(mod, scope, .none, param);
30553384 return rvalue(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.TypeOfPeer, .{ .items = items }, .{}));
30563385}
3057fn compileLog(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
3058 const tree = scope.tree();
3059 const arena = scope.arena();
3060 const src = tree.token_locs[call.builtin_token].start;
3061 const params = call.params();
3062 var targets = try arena.alloc(*zir.Inst, params.len);
3063 for (params) |param, param_i|
3064 targets[param_i] = try expr(mod, scope, .none, param);
3065 return addZIRInst(mod, scope, src, zir.Inst.CompileLog, .{ .to_log = targets }, .{});
3066}
30673386
3068fn builtinCall(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
3387fn builtinCall(
3388 mod: *Module,
3389 scope: *Scope,
3390 rl: ResultLoc,
3391 call: ast.Node.Index,
3392 params: []const ast.Node.Index,
3393) InnerError!*zir.Inst {
30693394 const tree = scope.tree();
3070 const builtin_name = tree.tokenSlice(call.builtin_token);
3395 const main_tokens = tree.nodes.items(.main_token);
3396 const token_starts = tree.tokens.items(.start);
3397
3398 const builtin_token = main_tokens[call];
3399 const builtin_name = tree.tokenSlice(builtin_token);
30713400
30723401 // We handle the different builtins manually because they have different semantics depending
30733402 // on the function. For example, `@as` and others participate in result location semantics,
30743403 // and `@cImport` creates a special scope that collects a .c source code text buffer.
30753404 // Also, some builtins have a variable number of parameters.
30763405
3077 if (mem.eql(u8, builtin_name, "@ptrToInt")) {
3078 return rvalue(mod, scope, rl, try ptrToInt(mod, scope, call));
3079 } else if (mem.eql(u8, builtin_name, "@as")) {
3080 return as(mod, scope, rl, call);
3081 } else if (mem.eql(u8, builtin_name, "@floatCast")) {
3082 return simpleCast(mod, scope, rl, call, .floatcast);
3083 } else if (mem.eql(u8, builtin_name, "@intCast")) {
3084 return simpleCast(mod, scope, rl, call, .intcast);
3085 } else if (mem.eql(u8, builtin_name, "@bitCast")) {
3086 return bitCast(mod, scope, rl, call);
3087 } else if (mem.eql(u8, builtin_name, "@TypeOf")) {
3088 return typeOf(mod, scope, rl, call);
3089 } else if (mem.eql(u8, builtin_name, "@breakpoint")) {
3090 const src = tree.token_locs[call.builtin_token].start;
3091 return rvalue(mod, scope, rl, try addZIRNoOp(mod, scope, src, .breakpoint));
3092 } else if (mem.eql(u8, builtin_name, "@import")) {
3093 return rvalue(mod, scope, rl, try import(mod, scope, call));
3094 } else if (mem.eql(u8, builtin_name, "@compileError")) {
3095 return compileError(mod, scope, call);
3096 } else if (mem.eql(u8, builtin_name, "@setEvalBranchQuota")) {
3097 return setEvalBranchQuota(mod, scope, call);
3098 } else if (mem.eql(u8, builtin_name, "@compileLog")) {
3099 return compileLog(mod, scope, call);
3100 } else if (mem.eql(u8, builtin_name, "@field")) {
3101 return namedField(mod, scope, rl, call);
3102 } else {
3103 return mod.failTok(scope, call.builtin_token, "invalid builtin function: '{s}'", .{builtin_name});
3406 const info = BuiltinFn.list.get(builtin_name) orelse {
3407 return mod.failTok(scope, builtin_token, "invalid builtin function: '{s}'", .{
3408 builtin_name,
3409 });
3410 };
3411 if (info.param_count) |expected| {
3412 if (expected != params.len) {
3413 const s = if (expected == 1) "" else "s";
3414 return mod.failTok(scope, builtin_token, "expected {d} parameter{s}, found {d}", .{
3415 expected, s, params.len,
3416 });
3417 }
3418 }
3419 const src = token_starts[builtin_token];
3420
3421 switch (info.tag) {
3422 .ptr_to_int => {
3423 const operand = try expr(mod, scope, .none, params[0]);
3424 const result = try addZIRUnOp(mod, scope, src, .ptrtoint, operand);
3425 return rvalue(mod, scope, rl, result);
3426 },
3427 .float_cast => {
3428 const dest_type = try typeExpr(mod, scope, params[0]);
3429 const rhs = try expr(mod, scope, .none, params[1]);
3430 const result = try addZIRBinOp(mod, scope, src, .floatcast, dest_type, rhs);
3431 return rvalue(mod, scope, rl, result);
3432 },
3433 .int_cast => {
3434 const dest_type = try typeExpr(mod, scope, params[0]);
3435 const rhs = try expr(mod, scope, .none, params[1]);
3436 const result = try addZIRBinOp(mod, scope, src, .intcast, dest_type, rhs);
3437 return rvalue(mod, scope, rl, result);
3438 },
3439 .breakpoint => {
3440 const result = try addZIRNoOp(mod, scope, src, .breakpoint);
3441 return rvalue(mod, scope, rl, result);
3442 },
3443 .import => {
3444 const target = try expr(mod, scope, .none, params[0]);
3445 const result = try addZIRUnOp(mod, scope, src, .import, target);
3446 return rvalue(mod, scope, rl, result);
3447 },
3448 .compile_error => {
3449 const target = try expr(mod, scope, .none, params[0]);
3450 const result = try addZIRUnOp(mod, scope, src, .compile_error, target);
3451 return rvalue(mod, scope, rl, result);
3452 },
3453 .set_eval_branch_quota => {
3454 const u32_type = try addZIRInstConst(mod, scope, src, .{
3455 .ty = Type.initTag(.type),
3456 .val = Value.initTag(.u32_type),
3457 });
3458 const quota = try expr(mod, scope, .{ .ty = u32_type }, params[0]);
3459 const result = try addZIRUnOp(mod, scope, src, .set_eval_branch_quota, quota);
3460 return rvalue(mod, scope, rl, result);
3461 },
3462 .compile_log => {
3463 const arena = scope.arena();
3464 var targets = try arena.alloc(*zir.Inst, params.len);
3465 for (params) |param, param_i|
3466 targets[param_i] = try expr(mod, scope, .none, param);
3467 const result = try addZIRInst(mod, scope, src, zir.Inst.CompileLog, .{ .to_log = targets }, .{});
3468 return rvalue(mod, scope, rl, result);
3469 },
3470 .field => {
3471 const string_type = try addZIRInstConst(mod, scope, src, .{
3472 .ty = Type.initTag(.type),
3473 .val = Value.initTag(.const_slice_u8_type),
3474 });
3475 const string_rl: ResultLoc = .{ .ty = string_type };
3476
3477 if (rl == .ref) {
3478 return addZirInstTag(mod, scope, src, .field_ptr_named, .{
3479 .object = try expr(mod, scope, .ref, params[0]),
3480 .field_name = try comptimeExpr(mod, scope, string_rl, params[1]),
3481 });
3482 }
3483 return rvalue(mod, scope, rl, try addZirInstTag(mod, scope, src, .field_val_named, .{
3484 .object = try expr(mod, scope, .none, params[0]),
3485 .field_name = try comptimeExpr(mod, scope, string_rl, params[1]),
3486 }));
3487 },
3488 .as => return as(mod, scope, rl, builtin_token, src, params[0], params[1]),
3489 .bit_cast => return bitCast(mod, scope, rl, builtin_token, src, params[0], params[1]),
3490 .TypeOf => return typeOf(mod, scope, rl, builtin_token, src, params),
3491
3492 .add_with_overflow,
3493 .align_cast,
3494 .align_of,
3495 .async_call,
3496 .atomic_load,
3497 .atomic_rmw,
3498 .atomic_store,
3499 .bit_offset_of,
3500 .bool_to_int,
3501 .bit_size_of,
3502 .mul_add,
3503 .byte_swap,
3504 .bit_reverse,
3505 .byte_offset_of,
3506 .call,
3507 .c_define,
3508 .c_import,
3509 .c_include,
3510 .clz,
3511 .cmpxchg_strong,
3512 .cmpxchg_weak,
3513 .ctz,
3514 .c_undef,
3515 .div_exact,
3516 .div_floor,
3517 .div_trunc,
3518 .embed_file,
3519 .enum_to_int,
3520 .error_name,
3521 .error_return_trace,
3522 .error_to_int,
3523 .err_set_cast,
3524 .@"export",
3525 .fence,
3526 .field_parent_ptr,
3527 .float_to_int,
3528 .frame,
3529 .Frame,
3530 .frame_address,
3531 .frame_size,
3532 .has_decl,
3533 .has_field,
3534 .int_to_enum,
3535 .int_to_error,
3536 .int_to_float,
3537 .int_to_ptr,
3538 .memcpy,
3539 .memset,
3540 .wasm_memory_size,
3541 .wasm_memory_grow,
3542 .mod,
3543 .mul_with_overflow,
3544 .panic,
3545 .pop_count,
3546 .ptr_cast,
3547 .rem,
3548 .return_address,
3549 .set_align_stack,
3550 .set_cold,
3551 .set_float_mode,
3552 .set_runtime_safety,
3553 .shl_exact,
3554 .shl_with_overflow,
3555 .shr_exact,
3556 .shuffle,
3557 .size_of,
3558 .splat,
3559 .reduce,
3560 .src,
3561 .sqrt,
3562 .sin,
3563 .cos,
3564 .exp,
3565 .exp2,
3566 .log,
3567 .log2,
3568 .log10,
3569 .fabs,
3570 .floor,
3571 .ceil,
3572 .trunc,
3573 .round,
3574 .sub_with_overflow,
3575 .tag_name,
3576 .This,
3577 .truncate,
3578 .Type,
3579 .type_info,
3580 .type_name,
3581 .union_init,
3582 => return mod.failTok(scope, builtin_token, "TODO: implement builtin function {s}", .{
3583 builtin_name,
3584 }),
31043585 }
31053586}
31063587
3107fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Call) InnerError!*zir.Inst {
3588fn callExpr(
3589 mod: *Module,
3590 scope: *Scope,
3591 rl: ResultLoc,
3592 call: ast.full.Call,
3593) InnerError!*zir.Inst {
3594 if (call.async_token) |async_token| {
3595 return mod.failTok(scope, async_token, "TODO implement async fn call", .{});
3596 }
3597
31083598 const tree = scope.tree();
3109 const lhs = try expr(mod, scope, .none, node.lhs);
3599 const main_tokens = tree.nodes.items(.main_token);
3600 const token_starts = tree.tokens.items(.start);
31103601
3111 const param_nodes = node.params();
3112 const args = try scope.getGenZIR().arena.alloc(*zir.Inst, param_nodes.len);
3113 for (param_nodes) |param_node, i| {
3114 const param_src = tree.token_locs[param_node.firstToken()].start;
3602 const lhs = try expr(mod, scope, .none, call.ast.fn_expr);
3603
3604 const args = try scope.getGenZIR().arena.alloc(*zir.Inst, call.ast.params.len);
3605 for (call.ast.params) |param_node, i| {
3606 const param_src = token_starts[tree.firstToken(param_node)];
31153607 const param_type = try addZIRInst(mod, scope, param_src, zir.Inst.ParamType, .{
31163608 .func = lhs,
31173609 .arg_index = i,
......@@ -3119,7 +3611,7 @@ fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Call) In
31193611 args[i] = try expr(mod, scope, .{ .ty = param_type }, param_node);
31203612 }
31213613
3122 const src = tree.token_locs[node.lhs.firstToken()].start;
3614 const src = token_starts[call.ast.lparen];
31233615 const result = try addZIRInst(mod, scope, src, zir.Inst.Call, .{
31243616 .func = lhs,
31253617 .args = args,
......@@ -3128,288 +3620,244 @@ fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Call) In
31283620 return rvalue(mod, scope, rl, result);
31293621}
31303622
3131fn unreach(mod: *Module, scope: *Scope, unreach_node: *ast.Node.OneToken) InnerError!*zir.Inst {
3623pub const simple_types = std.ComptimeStringMap(Value.Tag, .{
3624 .{ "u8", .u8_type },
3625 .{ "i8", .i8_type },
3626 .{ "isize", .isize_type },
3627 .{ "usize", .usize_type },
3628 .{ "c_short", .c_short_type },
3629 .{ "c_ushort", .c_ushort_type },
3630 .{ "c_int", .c_int_type },
3631 .{ "c_uint", .c_uint_type },
3632 .{ "c_long", .c_long_type },
3633 .{ "c_ulong", .c_ulong_type },
3634 .{ "c_longlong", .c_longlong_type },
3635 .{ "c_ulonglong", .c_ulonglong_type },
3636 .{ "c_longdouble", .c_longdouble_type },
3637 .{ "f16", .f16_type },
3638 .{ "f32", .f32_type },
3639 .{ "f64", .f64_type },
3640 .{ "f128", .f128_type },
3641 .{ "c_void", .c_void_type },
3642 .{ "bool", .bool_type },
3643 .{ "void", .void_type },
3644 .{ "type", .type_type },
3645 .{ "anyerror", .anyerror_type },
3646 .{ "comptime_int", .comptime_int_type },
3647 .{ "comptime_float", .comptime_float_type },
3648 .{ "noreturn", .noreturn_type },
3649});
3650
3651fn nodeMayNeedMemoryLocation(scope: *Scope, start_node: ast.Node.Index) bool {
31323652 const tree = scope.tree();
3133 const src = tree.token_locs[unreach_node.token].start;
3134 return addZIRNoOp(mod, scope, src, .unreachable_safe);
3135}
3136
3137fn getSimplePrimitiveValue(name: []const u8) ?TypedValue {
3138 const simple_types = std.ComptimeStringMap(Value.Tag, .{
3139 .{ "u8", .u8_type },
3140 .{ "i8", .i8_type },
3141 .{ "isize", .isize_type },
3142 .{ "usize", .usize_type },
3143 .{ "c_short", .c_short_type },
3144 .{ "c_ushort", .c_ushort_type },
3145 .{ "c_int", .c_int_type },
3146 .{ "c_uint", .c_uint_type },
3147 .{ "c_long", .c_long_type },
3148 .{ "c_ulong", .c_ulong_type },
3149 .{ "c_longlong", .c_longlong_type },
3150 .{ "c_ulonglong", .c_ulonglong_type },
3151 .{ "c_longdouble", .c_longdouble_type },
3152 .{ "f16", .f16_type },
3153 .{ "f32", .f32_type },
3154 .{ "f64", .f64_type },
3155 .{ "f128", .f128_type },
3156 .{ "c_void", .c_void_type },
3157 .{ "bool", .bool_type },
3158 .{ "void", .void_type },
3159 .{ "type", .type_type },
3160 .{ "anyerror", .anyerror_type },
3161 .{ "comptime_int", .comptime_int_type },
3162 .{ "comptime_float", .comptime_float_type },
3163 .{ "noreturn", .noreturn_type },
3164 });
3165 if (simple_types.get(name)) |tag| {
3166 return TypedValue{
3167 .ty = Type.initTag(.type),
3168 .val = Value.initTag(tag),
3169 };
3170 }
3171 return null;
3172}
3653 const node_tags = tree.nodes.items(.tag);
3654 const node_datas = tree.nodes.items(.data);
3655 const main_tokens = tree.nodes.items(.main_token);
3656 const token_tags = tree.tokens.items(.tag);
31733657
3174fn nodeMayNeedMemoryLocation(start_node: *ast.Node, scope: *Scope) bool {
31753658 var node = start_node;
31763659 while (true) {
3177 switch (node.tag) {
3178 .Root,
3179 .Use,
3180 .TestDecl,
3181 .DocComment,
3182 .SwitchCase,
3183 .SwitchElse,
3184 .Else,
3185 .Payload,
3186 .PointerPayload,
3187 .PointerIndexPayload,
3188 .ContainerField,
3189 .ErrorTag,
3190 .FieldInitializer,
3660 switch (node_tags[node]) {
3661 .root,
3662 .@"usingnamespace",
3663 .test_decl,
3664 .switch_case,
3665 .switch_case_one,
3666 .container_field_init,
3667 .container_field_align,
3668 .container_field,
3669 .asm_output,
3670 .asm_input,
31913671 => unreachable,
31923672
3193 .Return,
3194 .Break,
3195 .Continue,
3196 .BitNot,
3197 .BoolNot,
3198 .VarDecl,
3199 .Defer,
3200 .AddressOf,
3201 .OptionalType,
3202 .Negation,
3203 .NegationWrap,
3204 .Resume,
3205 .ArrayType,
3206 .ArrayTypeSentinel,
3207 .PtrType,
3208 .SliceType,
3209 .Suspend,
3210 .AnyType,
3211 .ErrorType,
3212 .FnProto,
3213 .AnyFrameType,
3214 .IntegerLiteral,
3215 .FloatLiteral,
3216 .EnumLiteral,
3217 .StringLiteral,
3218 .MultilineStringLiteral,
3219 .CharLiteral,
3220 .BoolLiteral,
3221 .NullLiteral,
3222 .UndefinedLiteral,
3223 .Unreachable,
3224 .Identifier,
3225 .ErrorSetDecl,
3226 .ContainerDecl,
3227 .Asm,
3228 .Add,
3229 .AddWrap,
3230 .ArrayCat,
3231 .ArrayMult,
3232 .Assign,
3233 .AssignBitAnd,
3234 .AssignBitOr,
3235 .AssignBitShiftLeft,
3236 .AssignBitShiftRight,
3237 .AssignBitXor,
3238 .AssignDiv,
3239 .AssignSub,
3240 .AssignSubWrap,
3241 .AssignMod,
3242 .AssignAdd,
3243 .AssignAddWrap,
3244 .AssignMul,
3245 .AssignMulWrap,
3246 .BangEqual,
3247 .BitAnd,
3248 .BitOr,
3249 .BitShiftLeft,
3250 .BitShiftRight,
3251 .BitXor,
3252 .BoolAnd,
3253 .BoolOr,
3254 .Div,
3255 .EqualEqual,
3256 .ErrorUnion,
3257 .GreaterOrEqual,
3258 .GreaterThan,
3259 .LessOrEqual,
3260 .LessThan,
3261 .MergeErrorSets,
3262 .Mod,
3263 .Mul,
3264 .MulWrap,
3265 .Range,
3266 .Period,
3267 .Sub,
3268 .SubWrap,
3269 .Slice,
3270 .Deref,
3271 .ArrayAccess,
3272 .Block,
3673 .@"return",
3674 .@"break",
3675 .@"continue",
3676 .bit_not,
3677 .bool_not,
3678 .global_var_decl,
3679 .local_var_decl,
3680 .simple_var_decl,
3681 .aligned_var_decl,
3682 .@"defer",
3683 .@"errdefer",
3684 .address_of,
3685 .optional_type,
3686 .negation,
3687 .negation_wrap,
3688 .@"resume",
3689 .array_type,
3690 .array_type_sentinel,
3691 .ptr_type_aligned,
3692 .ptr_type_sentinel,
3693 .ptr_type,
3694 .ptr_type_bit_range,
3695 .@"suspend",
3696 .@"anytype",
3697 .fn_proto_simple,
3698 .fn_proto_multi,
3699 .fn_proto_one,
3700 .fn_proto,
3701 .fn_decl,
3702 .anyframe_type,
3703 .anyframe_literal,
3704 .integer_literal,
3705 .float_literal,
3706 .enum_literal,
3707 .string_literal,
3708 .multiline_string_literal,
3709 .char_literal,
3710 .true_literal,
3711 .false_literal,
3712 .null_literal,
3713 .undefined_literal,
3714 .unreachable_literal,
3715 .identifier,
3716 .error_set_decl,
3717 .container_decl,
3718 .container_decl_trailing,
3719 .container_decl_two,
3720 .container_decl_two_trailing,
3721 .container_decl_arg,
3722 .container_decl_arg_trailing,
3723 .tagged_union,
3724 .tagged_union_trailing,
3725 .tagged_union_two,
3726 .tagged_union_two_trailing,
3727 .tagged_union_enum_tag,
3728 .tagged_union_enum_tag_trailing,
3729 .@"asm",
3730 .asm_simple,
3731 .add,
3732 .add_wrap,
3733 .array_cat,
3734 .array_mult,
3735 .assign,
3736 .assign_bit_and,
3737 .assign_bit_or,
3738 .assign_bit_shift_left,
3739 .assign_bit_shift_right,
3740 .assign_bit_xor,
3741 .assign_div,
3742 .assign_sub,
3743 .assign_sub_wrap,
3744 .assign_mod,
3745 .assign_add,
3746 .assign_add_wrap,
3747 .assign_mul,
3748 .assign_mul_wrap,
3749 .bang_equal,
3750 .bit_and,
3751 .bit_or,
3752 .bit_shift_left,
3753 .bit_shift_right,
3754 .bit_xor,
3755 .bool_and,
3756 .bool_or,
3757 .div,
3758 .equal_equal,
3759 .error_union,
3760 .greater_or_equal,
3761 .greater_than,
3762 .less_or_equal,
3763 .less_than,
3764 .merge_error_sets,
3765 .mod,
3766 .mul,
3767 .mul_wrap,
3768 .switch_range,
3769 .field_access,
3770 .sub,
3771 .sub_wrap,
3772 .slice,
3773 .slice_open,
3774 .slice_sentinel,
3775 .deref,
3776 .array_access,
3777 .error_value,
3778 .while_simple, // This variant cannot have an else expression.
3779 .while_cont, // This variant cannot have an else expression.
3780 .for_simple, // This variant cannot have an else expression.
3781 .if_simple, // This variant cannot have an else expression.
32733782 => return false,
32743783
3275 // Forward the question to a sub-expression.
3276 .GroupedExpression => node = node.castTag(.GroupedExpression).?.expr,
3277 .Try => node = node.castTag(.Try).?.rhs,
3278 .Await => node = node.castTag(.Await).?.rhs,
3279 .Catch => node = node.castTag(.Catch).?.rhs,
3280 .OrElse => node = node.castTag(.OrElse).?.rhs,
3281 .Comptime => node = node.castTag(.Comptime).?.expr,
3282 .Nosuspend => node = node.castTag(.Nosuspend).?.expr,
3283 .UnwrapOptional => node = node.castTag(.UnwrapOptional).?.lhs,
3784 // Forward the question to the LHS sub-expression.
3785 .grouped_expression,
3786 .@"try",
3787 .@"await",
3788 .@"comptime",
3789 .@"nosuspend",
3790 .unwrap_optional,
3791 => node = node_datas[node].lhs,
3792
3793 // Forward the question to the RHS sub-expression.
3794 .@"catch",
3795 .@"orelse",
3796 => node = node_datas[node].rhs,
32843797
32853798 // True because these are exactly the expressions we need memory locations for.
3286 .ArrayInitializer,
3287 .ArrayInitializerDot,
3288 .StructInitializer,
3289 .StructInitializerDot,
3799 .array_init_one,
3800 .array_init_one_comma,
3801 .array_init_dot_two,
3802 .array_init_dot_two_comma,
3803 .array_init_dot,
3804 .array_init_dot_comma,
3805 .array_init,
3806 .array_init_comma,
3807 .struct_init_one,
3808 .struct_init_one_comma,
3809 .struct_init_dot_two,
3810 .struct_init_dot_two_comma,
3811 .struct_init_dot,
3812 .struct_init_dot_comma,
3813 .struct_init,
3814 .struct_init_comma,
32903815 => return true,
32913816
32923817 // True because depending on comptime conditions, sub-expressions
32933818 // may be the kind that need memory locations.
3294 .While,
3295 .For,
3296 .Switch,
3297 .Call,
3298 .LabeledBlock,
3819 .@"while", // This variant always has an else expression.
3820 .@"if", // This variant always has an else expression.
3821 .@"for", // This variant always has an else expression.
3822 .@"switch",
3823 .switch_comma,
3824 .call_one,
3825 .call_one_comma,
3826 .async_call_one,
3827 .async_call_one_comma,
3828 .call,
3829 .call_comma,
3830 .async_call,
3831 .async_call_comma,
32993832 => return true,
33003833
3301 .BuiltinCall => {
3302 @setEvalBranchQuota(5000);
3303 const builtin_needs_mem_loc = std.ComptimeStringMap(bool, .{
3304 .{ "@addWithOverflow", false },
3305 .{ "@alignCast", false },
3306 .{ "@alignOf", false },
3307 .{ "@as", true },
3308 .{ "@asyncCall", false },
3309 .{ "@atomicLoad", false },
3310 .{ "@atomicRmw", false },
3311 .{ "@atomicStore", false },
3312 .{ "@bitCast", true },
3313 .{ "@bitOffsetOf", false },
3314 .{ "@boolToInt", false },
3315 .{ "@bitSizeOf", false },
3316 .{ "@breakpoint", false },
3317 .{ "@mulAdd", false },
3318 .{ "@byteSwap", false },
3319 .{ "@bitReverse", false },
3320 .{ "@byteOffsetOf", false },
3321 .{ "@call", true },
3322 .{ "@cDefine", false },
3323 .{ "@cImport", false },
3324 .{ "@cInclude", false },
3325 .{ "@clz", false },
3326 .{ "@cmpxchgStrong", false },
3327 .{ "@cmpxchgWeak", false },
3328 .{ "@compileError", false },
3329 .{ "@compileLog", false },
3330 .{ "@ctz", false },
3331 .{ "@cUndef", false },
3332 .{ "@divExact", false },
3333 .{ "@divFloor", false },
3334 .{ "@divTrunc", false },
3335 .{ "@embedFile", false },
3336 .{ "@enumToInt", false },
3337 .{ "@errorName", false },
3338 .{ "@errorReturnTrace", false },
3339 .{ "@errorToInt", false },
3340 .{ "@errSetCast", false },
3341 .{ "@export", false },
3342 .{ "@fence", false },
3343 .{ "@field", true },
3344 .{ "@fieldParentPtr", false },
3345 .{ "@floatCast", false },
3346 .{ "@floatToInt", false },
3347 .{ "@frame", false },
3348 .{ "@Frame", false },
3349 .{ "@frameAddress", false },
3350 .{ "@frameSize", false },
3351 .{ "@hasDecl", false },
3352 .{ "@hasField", false },
3353 .{ "@import", false },
3354 .{ "@intCast", false },
3355 .{ "@intToEnum", false },
3356 .{ "@intToError", false },
3357 .{ "@intToFloat", false },
3358 .{ "@intToPtr", false },
3359 .{ "@memcpy", false },
3360 .{ "@memset", false },
3361 .{ "@wasmMemorySize", false },
3362 .{ "@wasmMemoryGrow", false },
3363 .{ "@mod", false },
3364 .{ "@mulWithOverflow", false },
3365 .{ "@panic", false },
3366 .{ "@popCount", false },
3367 .{ "@ptrCast", false },
3368 .{ "@ptrToInt", false },
3369 .{ "@rem", false },
3370 .{ "@returnAddress", false },
3371 .{ "@setAlignStack", false },
3372 .{ "@setCold", false },
3373 .{ "@setEvalBranchQuota", false },
3374 .{ "@setFloatMode", false },
3375 .{ "@setRuntimeSafety", false },
3376 .{ "@shlExact", false },
3377 .{ "@shlWithOverflow", false },
3378 .{ "@shrExact", false },
3379 .{ "@shuffle", false },
3380 .{ "@sizeOf", false },
3381 .{ "@splat", true },
3382 .{ "@reduce", false },
3383 .{ "@src", true },
3384 .{ "@sqrt", false },
3385 .{ "@sin", false },
3386 .{ "@cos", false },
3387 .{ "@exp", false },
3388 .{ "@exp2", false },
3389 .{ "@log", false },
3390 .{ "@log2", false },
3391 .{ "@log10", false },
3392 .{ "@fabs", false },
3393 .{ "@floor", false },
3394 .{ "@ceil", false },
3395 .{ "@trunc", false },
3396 .{ "@round", false },
3397 .{ "@subWithOverflow", false },
3398 .{ "@tagName", false },
3399 .{ "@This", false },
3400 .{ "@truncate", false },
3401 .{ "@Type", false },
3402 .{ "@typeInfo", false },
3403 .{ "@typeName", false },
3404 .{ "@TypeOf", false },
3405 .{ "@unionInit", true },
3406 });
3407 const name = scope.tree().tokenSlice(node.castTag(.BuiltinCall).?.builtin_token);
3408 return builtin_needs_mem_loc.get(name).?;
3834 .block_two,
3835 .block_two_semicolon,
3836 .block,
3837 .block_semicolon,
3838 => {
3839 const lbrace = main_tokens[node];
3840 if (token_tags[lbrace - 1] == .colon) {
3841 // Labeled blocks may need a memory location to forward
3842 // to their break statements.
3843 return true;
3844 } else {
3845 return false;
3846 }
34093847 },
34103848
3411 // Depending on AST properties, they may need memory locations.
3412 .If => return node.castTag(.If).?.@"else" != null,
3849 .builtin_call,
3850 .builtin_call_comma,
3851 .builtin_call_two,
3852 .builtin_call_two_comma,
3853 => {
3854 const builtin_token = main_tokens[node];
3855 const builtin_name = tree.tokenSlice(builtin_token);
3856 // If the builtin is an invalid name, we don't cause an error here; instead
3857 // let it pass, and the error will be "invalid builtin function" later.
3858 const builtin_info = BuiltinFn.list.get(builtin_name) orelse return false;
3859 return builtin_info.needs_mem_loc;
3860 },
34133861 }
34143862 }
34153863}
......@@ -3450,8 +3898,18 @@ fn rvalue(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerEr
34503898 }
34513899}
34523900
3453fn rvalueVoid(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node, result: void) InnerError!*zir.Inst {
3454 const src = scope.tree().token_locs[node.firstToken()].start;
3901/// TODO when reworking ZIR memory layout, make the void value correspond to a hard coded
3902/// index; that way this does not actually need to allocate anything.
3903fn rvalueVoid(
3904 mod: *Module,
3905 scope: *Scope,
3906 rl: ResultLoc,
3907 node: ast.Node.Index,
3908 result: void,
3909) InnerError!*zir.Inst {
3910 const tree = scope.tree();
3911 const main_tokens = tree.nodes.items(.main_token);
3912 const src = tree.tokens.items(.start)[tree.firstToken(node)];
34553913 const void_inst = try addZIRInstConst(mod, scope, src, .{
34563914 .ty = Type.initTag(.void),
34573915 .val = Value.initTag(.void_value),
......@@ -3547,6 +4005,29 @@ pub fn addZirInstTag(
35474005 return &inst.base;
35484006}
35494007
4008pub fn addZirInstT(
4009 mod: *Module,
4010 scope: *Scope,
4011 src: usize,
4012 comptime T: type,
4013 tag: zir.Inst.Tag,
4014 positionals: std.meta.fieldInfo(T, .positionals).field_type,
4015) !*T {
4016 const gen_zir = scope.getGenZIR();
4017 try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
4018 const inst = try gen_zir.arena.create(T);
4019 inst.* = .{
4020 .base = .{
4021 .tag = tag,
4022 .src = src,
4023 },
4024 .positionals = positionals,
4025 .kw_args = .{},
4026 };
4027 gen_zir.instructions.appendAssumeCapacity(&inst.base);
4028 return inst;
4029}
4030
35504031pub fn addZIRInstSpecial(
35514032 mod: *Module,
35524033 scope: *Scope,
src/clang.zig+7-6
......@@ -127,6 +127,9 @@ pub const APSInt = opaque {
127127
128128 pub const getNumWords = ZigClangAPSInt_getNumWords;
129129 extern fn ZigClangAPSInt_getNumWords(*const APSInt) c_uint;
130
131 pub const lessThanEqual = ZigClangAPSInt_lessThanEqual;
132 extern fn ZigClangAPSInt_lessThanEqual(*const APSInt, rhs: u64) bool;
130133};
131134
132135pub const ASTContext = opaque {
......@@ -270,12 +273,12 @@ pub const CompoundAssignOperator = opaque {
270273
271274pub const CompoundStmt = opaque {
272275 pub const body_begin = ZigClangCompoundStmt_body_begin;
273 extern fn ZigClangCompoundStmt_body_begin(*const CompoundStmt) const_body_iterator;
276 extern fn ZigClangCompoundStmt_body_begin(*const CompoundStmt) ConstBodyIterator;
274277
275278 pub const body_end = ZigClangCompoundStmt_body_end;
276 extern fn ZigClangCompoundStmt_body_end(*const CompoundStmt) const_body_iterator;
279 extern fn ZigClangCompoundStmt_body_end(*const CompoundStmt) ConstBodyIterator;
277280
278 pub const const_body_iterator = [*]const *Stmt;
281 pub const ConstBodyIterator = [*]const *Stmt;
279282};
280283
281284pub const ConditionalOperator = opaque {};
......@@ -407,7 +410,7 @@ pub const Expr = opaque {
407410 pub const getBeginLoc = ZigClangExpr_getBeginLoc;
408411 extern fn ZigClangExpr_getBeginLoc(*const Expr) SourceLocation;
409412
410 pub const EvaluateAsConstantExpr = ZigClangExpr_EvaluateAsConstantExpr;
413 pub const evaluateAsConstantExpr = ZigClangExpr_EvaluateAsConstantExpr;
411414 extern fn ZigClangExpr_EvaluateAsConstantExpr(*const Expr, *ExprEvalResult, Expr_ConstExprUsage, *const ASTContext) bool;
412415};
413416
......@@ -694,8 +697,6 @@ pub const ReturnStmt = opaque {
694697 extern fn ZigClangReturnStmt_getRetValue(*const ReturnStmt) ?*const Expr;
695698};
696699
697pub const SkipFunctionBodiesScope = opaque {};
698
699700pub const SourceManager = opaque {
700701 pub const getSpellingLoc = ZigClangSourceManager_getSpellingLoc;
701702 extern fn ZigClangSourceManager_getSpellingLoc(*const SourceManager, Loc: SourceLocation) SourceLocation;
src/codegen.zig+10-5
......@@ -451,11 +451,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
451451
452452 const src_data: struct { lbrace_src: usize, rbrace_src: usize, source: []const u8 } = blk: {
453453 const container_scope = module_fn.owner_decl.container;
454 const tree = container_scope.file_scope.contents.tree;
455 const fn_proto = tree.root_node.decls()[module_fn.owner_decl.src_index].castTag(.FnProto).?;
456 const block = fn_proto.getBodyNode().?.castTag(.Block).?;
457 const lbrace_src = tree.token_locs[block.lbrace].start;
458 const rbrace_src = tree.token_locs[block.rbrace].start;
454 const tree = container_scope.file_scope.tree;
455 const node_tags = tree.nodes.items(.tag);
456 const node_datas = tree.nodes.items(.data);
457 const token_starts = tree.tokens.items(.start);
458
459 const fn_decl = tree.rootDecls()[module_fn.owner_decl.src_index];
460 assert(node_tags[fn_decl] == .fn_decl);
461 const block = node_datas[fn_decl].rhs;
462 const lbrace_src = token_starts[tree.firstToken(block)];
463 const rbrace_src = token_starts[tree.lastToken(block)];
459464 break :blk .{
460465 .lbrace_src = lbrace_src,
461466 .rbrace_src = rbrace_src,
src/codegen/c.zig+56-2
......@@ -1,4 +1,5 @@
11const std = @import("std");
2const assert = std.debug.assert;
23const mem = std.mem;
34const log = std.log.scoped(.c);
45
......@@ -42,7 +43,7 @@ pub const Object = struct {
4243 next_arg_index: usize = 0,
4344 next_local_index: usize = 0,
4445 next_block_index: usize = 0,
45 indent_writer: std.io.AutoIndentingStream(std.ArrayList(u8).Writer),
46 indent_writer: IndentWriter(std.ArrayList(u8).Writer),
4647
4748 fn resolveInst(o: *Object, inst: *Inst) !CValue {
4849 if (inst.value()) |_| {
......@@ -63,7 +64,7 @@ pub const Object = struct {
6364 return local_value;
6465 }
6566
66 fn writer(o: *Object) std.io.AutoIndentingStream(std.ArrayList(u8).Writer).Writer {
67 fn writer(o: *Object) IndentWriter(std.ArrayList(u8).Writer).Writer {
6768 return o.indent_writer.writer();
6869 }
6970
......@@ -796,3 +797,56 @@ fn genAsm(o: *Object, as: *Inst.Assembly) !CValue {
796797
797798 return o.dg.fail(o.dg.decl.src(), "TODO: C backend: inline asm expression result used", .{});
798799}
800
801fn IndentWriter(comptime UnderlyingWriter: type) type {
802 return struct {
803 const Self = @This();
804 pub const Error = UnderlyingWriter.Error;
805 pub const Writer = std.io.Writer(*Self, Error, write);
806
807 pub const indent_delta = 4;
808
809 underlying_writer: UnderlyingWriter,
810 indent_count: usize = 0,
811 current_line_empty: bool = true,
812
813 pub fn writer(self: *Self) Writer {
814 return .{ .context = self };
815 }
816
817 pub fn write(self: *Self, bytes: []const u8) Error!usize {
818 if (bytes.len == 0) return @as(usize, 0);
819
820 const current_indent = self.indent_count * Self.indent_delta;
821 if (self.current_line_empty and current_indent > 0) {
822 try self.underlying_writer.writeByteNTimes(' ', current_indent);
823 }
824 self.current_line_empty = false;
825
826 return self.writeNoIndent(bytes);
827 }
828
829 pub fn insertNewline(self: *Self) Error!void {
830 _ = try self.writeNoIndent("\n");
831 }
832
833 pub fn pushIndent(self: *Self) void {
834 self.indent_count += 1;
835 }
836
837 pub fn popIndent(self: *Self) void {
838 assert(self.indent_count != 0);
839 self.indent_count -= 1;
840 }
841
842 fn writeNoIndent(self: *Self, bytes: []const u8) Error!usize {
843 if (bytes.len == 0) return @as(usize, 0);
844
845 try self.underlying_writer.writeAll(bytes);
846 if (bytes[bytes.len - 1] == '\n') {
847 self.current_line_empty = true;
848 }
849 return bytes.len;
850 }
851 };
852}
src/ir.zig+1
......@@ -317,6 +317,7 @@ pub const Inst = struct {
317317 pub const base_tag = Tag.arg;
318318
319319 base: Inst,
320 /// This exists to be emitted into debug info.
320321 name: [*:0]const u8,
321322
322323 pub fn operandCount(self: *const Arg) usize {
src/link.zig+2-2
......@@ -550,11 +550,11 @@ pub const File = struct {
550550 id_symlink_basename,
551551 &prev_digest_buf,
552552 ) catch |err| b: {
553 log.debug("archive new_digest={} readFile error: {s}", .{ digest, @errorName(err) });
553 log.debug("archive new_digest={x} readFile error: {s}", .{ digest, @errorName(err) });
554554 break :b prev_digest_buf[0..0];
555555 };
556556 if (mem.eql(u8, prev_digest, &digest)) {
557 log.debug("archive digest={} match - skipping invocation", .{digest});
557 log.debug("archive digest={x} match - skipping invocation", .{digest});
558558 base.lock = man.toOwnedLock();
559559 return;
560560 }
src/link/C.zig+1-1
......@@ -97,7 +97,7 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
9797 .value_map = codegen.CValueMap.init(module.gpa),
9898 .indent_writer = undefined, // set later so we can get a pointer to object.code
9999 };
100 object.indent_writer = std.io.autoIndentingStream(4, object.code.writer());
100 object.indent_writer = .{ .underlying_writer = object.code.writer() };
101101 defer object.value_map.deinit();
102102 defer object.code.deinit();
103103 defer object.dg.fwd_decl.deinit();
src/link/Coff.zig+3-3
......@@ -892,17 +892,17 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
892892 id_symlink_basename,
893893 &prev_digest_buf,
894894 ) catch |err| blk: {
895 log.debug("COFF LLD new_digest={} error: {s}", .{ digest, @errorName(err) });
895 log.debug("COFF LLD new_digest={x} error: {s}", .{ digest, @errorName(err) });
896896 // Handle this as a cache miss.
897897 break :blk prev_digest_buf[0..0];
898898 };
899899 if (mem.eql(u8, prev_digest, &digest)) {
900 log.debug("COFF LLD digest={} match - skipping invocation", .{digest});
900 log.debug("COFF LLD digest={x} match - skipping invocation", .{digest});
901901 // Hot diggity dog! The output binary is already there.
902902 self.base.lock = man.toOwnedLock();
903903 return;
904904 }
905 log.debug("COFF LLD prev_digest={} new_digest={}", .{ prev_digest, digest });
905 log.debug("COFF LLD prev_digest={x} new_digest={x}", .{ prev_digest, digest });
906906
907907 // We are about to change the output file to be different, so we invalidate the build hash now.
908908 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
src/link/Elf.zig+27-15
......@@ -1365,17 +1365,17 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
13651365 id_symlink_basename,
13661366 &prev_digest_buf,
13671367 ) catch |err| blk: {
1368 log.debug("ELF LLD new_digest={} error: {s}", .{ digest, @errorName(err) });
1368 log.debug("ELF LLD new_digest={x} error: {s}", .{ digest, @errorName(err) });
13691369 // Handle this as a cache miss.
13701370 break :blk prev_digest_buf[0..0];
13711371 };
13721372 if (mem.eql(u8, prev_digest, &digest)) {
1373 log.debug("ELF LLD digest={} match - skipping invocation", .{digest});
1373 log.debug("ELF LLD digest={x} match - skipping invocation", .{digest});
13741374 // Hot diggity dog! The output binary is already there.
13751375 self.base.lock = man.toOwnedLock();
13761376 return;
13771377 }
1378 log.debug("ELF LLD prev_digest={} new_digest={}", .{ prev_digest, digest });
1378 log.debug("ELF LLD prev_digest={x} new_digest={x}", .{ prev_digest, digest });
13791379
13801380 // We are about to change the output file to be different, so we invalidate the build hash now.
13811381 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
......@@ -2223,13 +2223,19 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
22232223 try dbg_line_buffer.ensureCapacity(26);
22242224
22252225 const line_off: u28 = blk: {
2226 const tree = decl.container.file_scope.contents.tree;
2227 const file_ast_decls = tree.root_node.decls();
2226 const tree = decl.container.file_scope.tree;
2227 const node_tags = tree.nodes.items(.tag);
2228 const node_datas = tree.nodes.items(.data);
2229 const token_starts = tree.tokens.items(.start);
2230
2231 const file_ast_decls = tree.rootDecls();
22282232 // TODO Look into improving the performance here by adding a token-index-to-line
22292233 // lookup table. Currently this involves scanning over the source code for newlines.
2230 const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?;
2231 const block = fn_proto.getBodyNode().?.castTag(.Block).?;
2232 const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start);
2234 const fn_decl = file_ast_decls[decl.src_index];
2235 assert(node_tags[fn_decl] == .fn_decl);
2236 const block = node_datas[fn_decl].rhs;
2237 const lbrace = tree.firstToken(block);
2238 const line_delta = std.zig.lineDelta(tree.source, 0, token_starts[lbrace]);
22332239 break :blk @intCast(u28, line_delta);
22342240 };
22352241
......@@ -2744,13 +2750,19 @@ pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Dec
27442750
27452751 if (self.llvm_ir_module) |_| return;
27462752
2747 const tree = decl.container.file_scope.contents.tree;
2748 const file_ast_decls = tree.root_node.decls();
2753 const tree = decl.container.file_scope.tree;
2754 const node_tags = tree.nodes.items(.tag);
2755 const node_datas = tree.nodes.items(.data);
2756 const token_starts = tree.tokens.items(.start);
2757
2758 const file_ast_decls = tree.rootDecls();
27492759 // TODO Look into improving the performance here by adding a token-index-to-line
27502760 // lookup table. Currently this involves scanning over the source code for newlines.
2751 const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?;
2752 const block = fn_proto.getBodyNode().?.castTag(.Block).?;
2753 const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start);
2761 const fn_decl = file_ast_decls[decl.src_index];
2762 assert(node_tags[fn_decl] == .fn_decl);
2763 const block = node_datas[fn_decl].rhs;
2764 const lbrace = tree.firstToken(block);
2765 const line_delta = std.zig.lineDelta(tree.source, 0, token_starts[lbrace]);
27542766 const casted_line_off = @intCast(u28, line_delta);
27552767
27562768 const shdr = &self.sections.items[self.debug_line_section_index.?];
......@@ -3025,7 +3037,7 @@ const min_nop_size = 2;
30253037
30263038/// Writes to the file a buffer, prefixed and suffixed by the specified number of
30273039/// bytes of NOPs. Asserts each padding size is at least `min_nop_size` and total padding bytes
3028/// are less than 126,976 bytes (if this limit is ever reached, this function can be
3040/// are less than 1044480 bytes (if this limit is ever reached, this function can be
30293041/// improved to make more than one pwritev call, or the limit can be raised by a fixed
30303042/// amount by increasing the length of `vecs`).
30313043fn pwriteDbgLineNops(
......@@ -3040,7 +3052,7 @@ fn pwriteDbgLineNops(
30403052
30413053 const page_of_nops = [1]u8{DW.LNS_negate_stmt} ** 4096;
30423054 const three_byte_nop = [3]u8{ DW.LNS_advance_pc, 0b1000_0000, 0 };
3043 var vecs: [32]std.os.iovec_const = undefined;
3055 var vecs: [256]std.os.iovec_const = undefined;
30443056 var vec_index: usize = 0;
30453057 {
30463058 var padding_left = prev_padding_size;
src/link/MachO.zig+3-3
......@@ -556,17 +556,17 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
556556 id_symlink_basename,
557557 &prev_digest_buf,
558558 ) catch |err| blk: {
559 log.debug("MachO LLD new_digest={} error: {s}", .{ digest, @errorName(err) });
559 log.debug("MachO LLD new_digest={x} error: {s}", .{ digest, @errorName(err) });
560560 // Handle this as a cache miss.
561561 break :blk prev_digest_buf[0..0];
562562 };
563563 if (mem.eql(u8, prev_digest, &digest)) {
564 log.debug("MachO LLD digest={} match - skipping invocation", .{digest});
564 log.debug("MachO LLD digest={x} match - skipping invocation", .{digest});
565565 // Hot diggity dog! The output binary is already there.
566566 self.base.lock = man.toOwnedLock();
567567 return;
568568 }
569 log.debug("MachO LLD prev_digest={} new_digest={}", .{ prev_digest, digest });
569 log.debug("MachO LLD prev_digest={x} new_digest={x}", .{ prev_digest, digest });
570570
571571 // We are about to change the output file to be different, so we invalidate the build hash now.
572572 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
src/link/MachO/DebugSymbols.zig+22-10
......@@ -904,13 +904,19 @@ pub fn updateDeclLineNumber(self: *DebugSymbols, module: *Module, decl: *const M
904904 const tracy = trace(@src());
905905 defer tracy.end();
906906
907 const tree = decl.container.file_scope.contents.tree;
908 const file_ast_decls = tree.root_node.decls();
907 const tree = decl.container.file_scope.tree;
908 const node_tags = tree.nodes.items(.tag);
909 const node_datas = tree.nodes.items(.data);
910 const token_starts = tree.tokens.items(.start);
911
912 const file_ast_decls = tree.rootDecls();
909913 // TODO Look into improving the performance here by adding a token-index-to-line
910914 // lookup table. Currently this involves scanning over the source code for newlines.
911 const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?;
912 const block = fn_proto.getBodyNode().?.castTag(.Block).?;
913 const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start);
915 const fn_decl = file_ast_decls[decl.src_index];
916 assert(node_tags[fn_decl] == .fn_decl);
917 const block = node_datas[fn_decl].rhs;
918 const lbrace = tree.firstToken(block);
919 const line_delta = std.zig.lineDelta(tree.source, 0, token_starts[lbrace]);
914920 const casted_line_off = @intCast(u28, line_delta);
915921
916922 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
......@@ -948,13 +954,19 @@ pub fn initDeclDebugBuffers(
948954 try dbg_line_buffer.ensureCapacity(26);
949955
950956 const line_off: u28 = blk: {
951 const tree = decl.container.file_scope.contents.tree;
952 const file_ast_decls = tree.root_node.decls();
957 const tree = decl.container.file_scope.tree;
958 const node_tags = tree.nodes.items(.tag);
959 const node_datas = tree.nodes.items(.data);
960 const token_starts = tree.tokens.items(.start);
961
962 const file_ast_decls = tree.rootDecls();
953963 // TODO Look into improving the performance here by adding a token-index-to-line
954964 // lookup table. Currently this involves scanning over the source code for newlines.
955 const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?;
956 const block = fn_proto.getBodyNode().?.castTag(.Block).?;
957 const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start);
965 const fn_decl = file_ast_decls[decl.src_index];
966 assert(node_tags[fn_decl] == .fn_decl);
967 const block = node_datas[fn_decl].rhs;
968 const lbrace = tree.firstToken(block);
969 const line_delta = std.zig.lineDelta(tree.source, 0, token_starts[lbrace]);
958970 break :blk @intCast(u28, line_delta);
959971 };
960972
src/link/Wasm.zig+3-3
......@@ -391,17 +391,17 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
391391 id_symlink_basename,
392392 &prev_digest_buf,
393393 ) catch |err| blk: {
394 log.debug("WASM LLD new_digest={} error: {s}", .{ digest, @errorName(err) });
394 log.debug("WASM LLD new_digest={x} error: {s}", .{ digest, @errorName(err) });
395395 // Handle this as a cache miss.
396396 break :blk prev_digest_buf[0..0];
397397 };
398398 if (mem.eql(u8, prev_digest, &digest)) {
399 log.debug("WASM LLD digest={} match - skipping invocation", .{digest});
399 log.debug("WASM LLD digest={x} match - skipping invocation", .{digest});
400400 // Hot diggity dog! The output binary is already there.
401401 self.base.lock = man.toOwnedLock();
402402 return;
403403 }
404 log.debug("WASM LLD prev_digest={} new_digest={}", .{ prev_digest, digest });
404 log.debug("WASM LLD prev_digest={x} new_digest={x}", .{ prev_digest, digest });
405405
406406 // We are about to change the output file to be different, so we invalidate the build hash now.
407407 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
src/main.zig+44-41
......@@ -2158,7 +2158,7 @@ fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !voi
21582158 const c_headers_dir_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{"include"});
21592159 const c_headers_dir_path_z = try arena.dupeZ(u8, c_headers_dir_path);
21602160 var clang_errors: []translate_c.ClangErrMsg = &[0]translate_c.ClangErrMsg{};
2161 const tree = translate_c.translate(
2161 var tree = translate_c.translate(
21622162 comp.gpa,
21632163 new_argv.ptr,
21642164 new_argv.ptr + new_argv.len,
......@@ -2179,7 +2179,7 @@ fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !voi
21792179 process.exit(1);
21802180 },
21812181 };
2182 defer tree.deinit();
2182 defer tree.deinit(comp.gpa);
21832183
21842184 if (out_dep_path) |dep_file_path| {
21852185 const dep_basename = std.fs.path.basename(dep_file_path);
......@@ -2193,16 +2193,21 @@ fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !voi
21932193
21942194 const digest = man.final();
21952195 const o_sub_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest });
2196
21962197 var o_dir = try comp.local_cache_directory.handle.makeOpenPath(o_sub_path, .{});
21972198 defer o_dir.close();
2199
21982200 var zig_file = try o_dir.createFile(translated_zig_basename, .{});
21992201 defer zig_file.close();
22002202
2201 var bw = io.bufferedWriter(zig_file.writer());
2202 _ = try std.zig.render(comp.gpa, bw.writer(), tree);
2203 try bw.flush();
2203 const formatted = try tree.render(comp.gpa);
2204 defer comp.gpa.free(formatted);
22042205
2205 man.writeManifest() catch |err| warn("failed to write cache manifest: {s}", .{@errorName(err)});
2206 try zig_file.writeAll(formatted);
2207
2208 man.writeManifest() catch |err| warn("failed to write cache manifest: {s}", .{
2209 @errorName(err),
2210 });
22062211
22072212 break :digest digest;
22082213 };
......@@ -2689,10 +2694,10 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
26892694 const source_code = try stdin.readAllAlloc(gpa, max_src_size);
26902695 defer gpa.free(source_code);
26912696
2692 const tree = std.zig.parse(gpa, source_code) catch |err| {
2697 var tree = std.zig.parse(gpa, source_code) catch |err| {
26932698 fatal("error parsing stdin: {s}", .{err});
26942699 };
2695 defer tree.deinit();
2700 defer tree.deinit(gpa);
26962701
26972702 for (tree.errors) |parse_error| {
26982703 try printErrMsgToFile(gpa, parse_error, tree, "<stdin>", stderr_file, color);
......@@ -2700,16 +2705,15 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
27002705 if (tree.errors.len != 0) {
27012706 process.exit(1);
27022707 }
2708 const formatted = try tree.render(gpa);
2709 defer gpa.free(formatted);
2710
27032711 if (check_flag) {
2704 const anything_changed = try std.zig.render(gpa, io.null_writer, tree);
2705 const code = if (anything_changed) @as(u8, 1) else @as(u8, 0);
2712 const code: u8 = @boolToInt(mem.eql(u8, formatted, source_code));
27062713 process.exit(code);
27072714 }
27082715
2709 var bw = io.bufferedWriter(io.getStdOut().writer());
2710 _ = try std.zig.render(gpa, bw.writer(), tree);
2711 try bw.flush();
2712 return;
2716 return io.getStdOut().writeAll(formatted);
27132717 }
27142718
27152719 if (input_files.items.len == 0) {
......@@ -2846,8 +2850,8 @@ fn fmtPathFile(
28462850 // Add to set after no longer possible to get error.IsDir.
28472851 if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;
28482852
2849 const tree = try std.zig.parse(fmt.gpa, source_code);
2850 defer tree.deinit();
2853 var tree = try std.zig.parse(fmt.gpa, source_code);
2854 defer tree.deinit(fmt.gpa);
28512855
28522856 for (tree.errors) |parse_error| {
28532857 try printErrMsgToFile(fmt.gpa, parse_error, tree, file_path, std.io.getStdErr(), fmt.color);
......@@ -2857,22 +2861,19 @@ fn fmtPathFile(
28572861 return;
28582862 }
28592863
2864 // As a heuristic, we make enough capacity for the same as the input source.
2865 fmt.out_buffer.shrinkRetainingCapacity(0);
2866 try fmt.out_buffer.ensureCapacity(source_code.len);
2867
2868 try tree.renderToArrayList(&fmt.out_buffer);
2869 if (mem.eql(u8, fmt.out_buffer.items, source_code))
2870 return;
2871
28602872 if (check_mode) {
2861 const anything_changed = try std.zig.render(fmt.gpa, io.null_writer, tree);
2862 if (anything_changed) {
2863 const stdout = io.getStdOut().writer();
2864 try stdout.print("{s}\n", .{file_path});
2865 fmt.any_error = true;
2866 }
2873 const stdout = io.getStdOut().writer();
2874 try stdout.print("{s}\n", .{file_path});
2875 fmt.any_error = true;
28672876 } else {
2868 // As a heuristic, we make enough capacity for the same as the input source.
2869 try fmt.out_buffer.ensureCapacity(source_code.len);
2870 fmt.out_buffer.items.len = 0;
2871 const writer = fmt.out_buffer.writer();
2872 const anything_changed = try std.zig.render(fmt.gpa, writer, tree);
2873 if (!anything_changed)
2874 return; // Good thing we didn't waste any file system access on this.
2875
28762877 var af = try dir.atomicFile(sub_path, .{ .mode = stat.mode });
28772878 defer af.deinit();
28782879
......@@ -2886,7 +2887,7 @@ fn fmtPathFile(
28862887fn printErrMsgToFile(
28872888 gpa: *mem.Allocator,
28882889 parse_error: ast.Error,
2889 tree: *ast.Tree,
2890 tree: ast.Tree,
28902891 path: []const u8,
28912892 file: fs.File,
28922893 color: Color,
......@@ -2896,19 +2897,17 @@ fn printErrMsgToFile(
28962897 .on => true,
28972898 .off => false,
28982899 };
2899 const lok_token = parse_error.loc();
2900 const span_first = lok_token;
2901 const span_last = lok_token;
2900 const lok_token = parse_error.token;
29022901
2903 const first_token = tree.token_locs[span_first];
2904 const last_token = tree.token_locs[span_last];
2905 const start_loc = tree.tokenLocationLoc(0, first_token);
2906 const end_loc = tree.tokenLocationLoc(first_token.end, last_token);
2902 const token_starts = tree.tokens.items(.start);
2903 const token_tags = tree.tokens.items(.tag);
2904 const first_token_start = token_starts[lok_token];
2905 const start_loc = tree.tokenLocation(0, lok_token);
29072906
29082907 var text_buf = std.ArrayList(u8).init(gpa);
29092908 defer text_buf.deinit();
29102909 const writer = text_buf.writer();
2911 try parse_error.render(tree.token_ids, writer);
2910 try tree.renderError(parse_error, writer);
29122911 const text = text_buf.items;
29132912
29142913 const stream = file.writer();
......@@ -2925,8 +2924,12 @@ fn printErrMsgToFile(
29252924 }
29262925 try stream.writeByte('\n');
29272926 try stream.writeByteNTimes(' ', start_loc.column);
2928 try stream.writeByteNTimes('~', last_token.end - first_token.start);
2929 try stream.writeByte('\n');
2927 if (token_tags[lok_token].lexeme()) |lexeme| {
2928 try stream.writeByteNTimes('~', lexeme.len);
2929 try stream.writeByte('\n');
2930 } else {
2931 try stream.writeAll("^\n");
2932 }
29302933}
29312934
29322935pub const info_zen =
src/test.zig+13-11
......@@ -155,7 +155,7 @@ pub const TestContext = struct {
155155 self.updates.append(.{
156156 .src = src,
157157 .case = .{ .Header = result },
158 }) catch unreachable;
158 }) catch @panic("out of memory");
159159 }
160160
161161 /// Adds a subcase in which the module is updated with `src`, compiled,
......@@ -164,7 +164,7 @@ pub const TestContext = struct {
164164 self.updates.append(.{
165165 .src = src,
166166 .case = .{ .Execution = result },
167 }) catch unreachable;
167 }) catch @panic("out of memory");
168168 }
169169
170170 /// Adds a subcase in which the module is updated with `src`, compiled,
......@@ -173,7 +173,7 @@ pub const TestContext = struct {
173173 self.updates.append(.{
174174 .src = src,
175175 .case = .{ .CompareObjectFile = result },
176 }) catch unreachable;
176 }) catch @panic("out of memory");
177177 }
178178
179179 /// Adds a subcase in which the module is updated with `src`, which
......@@ -181,7 +181,7 @@ pub const TestContext = struct {
181181 /// for the expected reasons, given in sequential order in `errors` in
182182 /// the form `:line:column: error: message`.
183183 pub fn addError(self: *Case, src: [:0]const u8, errors: []const []const u8) void {
184 var array = self.updates.allocator.alloc(ErrorMsg, errors.len) catch unreachable;
184 var array = self.updates.allocator.alloc(ErrorMsg, errors.len) catch @panic("out of memory");
185185 for (errors) |err_msg_line, i| {
186186 if (std.mem.startsWith(u8, err_msg_line, "error: ")) {
187187 array[i] = .{
......@@ -224,7 +224,7 @@ pub const TestContext = struct {
224224 },
225225 };
226226 }
227 self.updates.append(.{ .src = src, .case = .{ .Error = array } }) catch unreachable;
227 self.updates.append(.{ .src = src, .case = .{ .Error = array } }) catch @panic("out of memory");
228228 }
229229
230230 /// Adds a subcase in which the module is updated with `src`, and
......@@ -247,7 +247,7 @@ pub const TestContext = struct {
247247 .output_mode = .Exe,
248248 .extension = extension,
249249 .files = std.ArrayList(File).init(ctx.cases.allocator),
250 }) catch unreachable;
250 }) catch @panic("out of memory");
251251 return &ctx.cases.items[ctx.cases.items.len - 1];
252252 }
253253
......@@ -262,15 +262,17 @@ pub const TestContext = struct {
262262 }
263263
264264 pub fn exeFromCompiledC(ctx: *TestContext, name: []const u8, target: CrossTarget) *Case {
265 const prefixed_name = std.fmt.allocPrint(ctx.cases.allocator, "CBE: {s}", .{name}) catch
266 @panic("out of memory");
265267 ctx.cases.append(Case{
266 .name = name,
268 .name = prefixed_name,
267269 .target = target,
268270 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
269271 .output_mode = .Exe,
270272 .extension = .Zig,
271273 .object_format = .c,
272274 .files = std.ArrayList(File).init(ctx.cases.allocator),
273 }) catch unreachable;
275 }) catch @panic("out of memory");
274276 return &ctx.cases.items[ctx.cases.items.len - 1];
275277 }
276278
......@@ -285,7 +287,7 @@ pub const TestContext = struct {
285287 .extension = .Zig,
286288 .files = std.ArrayList(File).init(ctx.cases.allocator),
287289 .llvm_backend = true,
288 }) catch unreachable;
290 }) catch @panic("out of memory");
289291 return &ctx.cases.items[ctx.cases.items.len - 1];
290292 }
291293
......@@ -302,7 +304,7 @@ pub const TestContext = struct {
302304 .output_mode = .Obj,
303305 .extension = extension,
304306 .files = std.ArrayList(File).init(ctx.cases.allocator),
305 }) catch unreachable;
307 }) catch @panic("out of memory");
306308 return &ctx.cases.items[ctx.cases.items.len - 1];
307309 }
308310
......@@ -326,7 +328,7 @@ pub const TestContext = struct {
326328 .extension = ext,
327329 .object_format = .c,
328330 .files = std.ArrayList(File).init(ctx.cases.allocator),
329 }) catch unreachable;
331 }) catch @panic("out of memory");
330332 return &ctx.cases.items[ctx.cases.items.len - 1];
331333 }
332334
src/translate_c.zig+1790-3687
......@@ -3,23 +3,24 @@
33
44const std = @import("std");
55const assert = std.debug.assert;
6const ast = std.zig.ast;
7const Token = std.zig.Token;
86const clang = @import("clang.zig");
97const ctok = std.c.tokenizer;
108const CToken = std.c.Token;
119const mem = std.mem;
1210const math = std.math;
11const ast = @import("translate_c/ast.zig");
12const Node = ast.Node;
13const Tag = Node.Tag;
1314
1415const CallingConvention = std.builtin.CallingConvention;
1516
1617pub const ClangErrMsg = clang.Stage2ErrorMsg;
1718
18pub const Error = error{OutOfMemory};
19pub const Error = std.mem.Allocator.Error;
1920const TypeError = Error || error{UnsupportedType};
2021const TransError = TypeError || error{UnsupportedTranslation};
2122
22const SymbolTable = std.StringArrayHashMap(*ast.Node);
23const SymbolTable = std.StringArrayHashMap(Node);
2324const AliasList = std.ArrayList(struct {
2425 alias: []const u8,
2526 name: []const u8,
......@@ -30,23 +31,11 @@ const Scope = struct {
3031 parent: ?*Scope,
3132
3233 const Id = enum {
33 Switch,
34 Block,
35 Root,
36 Condition,
37 Loop,
38 };
39
40 /// Represents an in-progress ast.Node.Switch. This struct is stack-allocated.
41 /// When it is deinitialized, it produces an ast.Node.Switch which is allocated
42 /// into the main arena.
43 const Switch = struct {
44 base: Scope,
45 pending_block: Block,
46 cases: []*ast.Node,
47 case_index: usize,
48 switch_label: ?[]const u8,
49 default_label: ?[]const u8,
34 block,
35 root,
36 condition,
37 loop,
38 do_loop,
5039 };
5140
5241 /// Used for the scope of condition expressions, for example `if (cond)`.
......@@ -67,16 +56,15 @@ const Scope = struct {
6756 }
6857 };
6958
70 /// Represents an in-progress ast.Node.Block. This struct is stack-allocated.
71 /// When it is deinitialized, it produces an ast.Node.Block which is allocated
59 /// Represents an in-progress Node.Block. This struct is stack-allocated.
60 /// When it is deinitialized, it produces an Node.Block which is allocated
7261 /// into the main arena.
7362 const Block = struct {
7463 base: Scope,
75 statements: std.ArrayList(*ast.Node),
64 statements: std.ArrayList(Node),
7665 variables: AliasList,
77 label: ?ast.TokenIndex,
7866 mangle_count: u32 = 0,
79 lbrace: ast.TokenIndex,
67 label: ?[]const u8 = null,
8068
8169 /// When the block corresponds to a function, keep track of the return type
8270 /// so that the return expression can be cast, if necessary
......@@ -85,17 +73,14 @@ const Scope = struct {
8573 fn init(c: *Context, parent: *Scope, labeled: bool) !Block {
8674 var blk = Block{
8775 .base = .{
88 .id = .Block,
76 .id = .block,
8977 .parent = parent,
9078 },
91 .statements = std.ArrayList(*ast.Node).init(c.gpa),
79 .statements = std.ArrayList(Node).init(c.gpa),
9280 .variables = AliasList.init(c.gpa),
93 .label = null,
94 .lbrace = try appendToken(c, .LBrace, "{"),
9581 };
9682 if (labeled) {
97 blk.label = try appendIdentifier(c, try blk.makeMangledName(c, "blk"));
98 _ = try appendToken(c, .Colon, ":");
83 blk.label = try blk.makeMangledName(c, "blk");
9984 }
10085 return blk;
10186 }
......@@ -106,31 +91,24 @@ const Scope = struct {
10691 self.* = undefined;
10792 }
10893
109 fn complete(self: *Block, c: *Context) !*ast.Node {
110 // We reserve 1 extra statement if the parent is a Loop. This is in case of
111 // do while, we want to put `if (cond) break;` at the end.
112 const alloc_len = self.statements.items.len + @boolToInt(self.base.parent.?.id == .Loop);
113 const rbrace = try appendToken(c, .RBrace, "}");
114 if (self.label) |label| {
115 const node = try ast.Node.LabeledBlock.alloc(c.arena, alloc_len);
116 node.* = .{
117 .statements_len = self.statements.items.len,
118 .lbrace = self.lbrace,
119 .rbrace = rbrace,
120 .label = label,
121 };
122 mem.copy(*ast.Node, node.statements(), self.statements.items);
123 return &node.base;
124 } else {
125 const node = try ast.Node.Block.alloc(c.arena, alloc_len);
126 node.* = .{
127 .statements_len = self.statements.items.len,
128 .lbrace = self.lbrace,
129 .rbrace = rbrace,
130 };
131 mem.copy(*ast.Node, node.statements(), self.statements.items);
132 return &node.base;
94 fn complete(self: *Block, c: *Context) !Node {
95 if (self.base.parent.?.id == .do_loop) {
96 // We reserve 1 extra statement if the parent is a do_loop. This is in case of
97 // do while, we want to put `if (cond) break;` at the end.
98 const alloc_len = self.statements.items.len + @boolToInt(self.base.parent.?.id == .do_loop);
99 var stmts = try c.arena.alloc(Node, alloc_len);
100 stmts.len = self.statements.items.len;
101 mem.copy(Node, stmts, self.statements.items);
102 return Tag.block.create(c.arena, .{
103 .label = self.label,
104 .stmts = stmts,
105 });
133106 }
107 if (self.statements.items.len == 0) return Tag.empty_block.init();
108 return Tag.block.create(c.arena, .{
109 .label = self.label,
110 .stmts = try c.arena.dupe(Node, self.statements.items),
111 });
134112 }
135113
136114 /// Given the desired name, return a name that does not shadow anything from outer scopes.
......@@ -174,19 +152,27 @@ const Scope = struct {
174152 sym_table: SymbolTable,
175153 macro_table: SymbolTable,
176154 context: *Context,
155 nodes: std.ArrayList(Node),
177156
178157 fn init(c: *Context) Root {
179158 return .{
180159 .base = .{
181 .id = .Root,
160 .id = .root,
182161 .parent = null,
183162 },
184 .sym_table = SymbolTable.init(c.arena),
185 .macro_table = SymbolTable.init(c.arena),
163 .sym_table = SymbolTable.init(c.gpa),
164 .macro_table = SymbolTable.init(c.gpa),
186165 .context = c,
166 .nodes = std.ArrayList(Node).init(c.gpa),
187167 };
188168 }
189169
170 fn deinit(scope: *Root) void {
171 scope.sym_table.deinit();
172 scope.macro_table.deinit();
173 scope.nodes.deinit();
174 }
175
190176 /// Check if the global scope contains this name, without looking into the "future", e.g.
191177 /// ignore the preprocessed decl and macro names.
192178 fn containsNow(scope: *Root, name: []const u8) bool {
......@@ -205,20 +191,20 @@ const Scope = struct {
205191 var scope = inner;
206192 while (true) {
207193 switch (scope.id) {
208 .Root => unreachable,
209 .Block => return @fieldParentPtr(Block, "base", scope),
210 .Condition => return @fieldParentPtr(Condition, "base", scope).getBlockScope(c),
194 .root => unreachable,
195 .block => return @fieldParentPtr(Block, "base", scope),
196 .condition => return @fieldParentPtr(Condition, "base", scope).getBlockScope(c),
211197 else => scope = scope.parent.?,
212198 }
213199 }
214200 }
215201
216 fn findBlockReturnType(inner: *Scope, c: *Context) ?clang.QualType {
202 fn findBlockReturnType(inner: *Scope, c: *Context) clang.QualType {
217203 var scope = inner;
218204 while (true) {
219205 switch (scope.id) {
220 .Root => return null,
221 .Block => {
206 .root => unreachable,
207 .block => {
222208 const block = @fieldParentPtr(Block, "base", scope);
223209 if (block.return_type) |qt| return qt;
224210 scope = scope.parent.?;
......@@ -230,17 +216,17 @@ const Scope = struct {
230216
231217 fn getAlias(scope: *Scope, name: []const u8) []const u8 {
232218 return switch (scope.id) {
233 .Root => return name,
234 .Block => @fieldParentPtr(Block, "base", scope).getAlias(name),
235 .Switch, .Loop, .Condition => scope.parent.?.getAlias(name),
219 .root => return name,
220 .block => @fieldParentPtr(Block, "base", scope).getAlias(name),
221 .loop, .do_loop, .condition => scope.parent.?.getAlias(name),
236222 };
237223 }
238224
239225 fn contains(scope: *Scope, name: []const u8) bool {
240226 return switch (scope.id) {
241 .Root => @fieldParentPtr(Root, "base", scope).contains(name),
242 .Block => @fieldParentPtr(Block, "base", scope).contains(name),
243 .Switch, .Loop, .Condition => scope.parent.?.contains(name),
227 .root => @fieldParentPtr(Root, "base", scope).contains(name),
228 .block => @fieldParentPtr(Block, "base", scope).contains(name),
229 .loop, .do_loop, .condition => scope.parent.?.contains(name),
244230 };
245231 }
246232
......@@ -248,20 +234,26 @@ const Scope = struct {
248234 var scope = inner;
249235 while (true) {
250236 switch (scope.id) {
251 .Root => unreachable,
252 .Switch => return scope,
253 .Loop => return scope,
237 .root => unreachable,
238 .loop, .do_loop => return scope,
254239 else => scope = scope.parent.?,
255240 }
256241 }
257242 }
258243
259 fn getSwitch(inner: *Scope) *Scope.Switch {
244 /// Appends a node to the first block scope if inside a function, or to the root tree if not.
245 fn appendNode(inner: *Scope, node: Node) !void {
260246 var scope = inner;
261247 while (true) {
262248 switch (scope.id) {
263 .Root => unreachable,
264 .Switch => return @fieldParentPtr(Switch, "base", scope),
249 .root => {
250 const root = @fieldParentPtr(Root, "base", scope);
251 return root.nodes.append(node);
252 },
253 .block => {
254 const block = @fieldParentPtr(Block, "base", scope);
255 return block.statements.append(node);
256 },
265257 else => scope = scope.parent.?,
266258 }
267259 }
......@@ -271,18 +263,12 @@ const Scope = struct {
271263pub const Context = struct {
272264 gpa: *mem.Allocator,
273265 arena: *mem.Allocator,
274 token_ids: std.ArrayListUnmanaged(Token.Id) = .{},
275 token_locs: std.ArrayListUnmanaged(Token.Loc) = .{},
276 errors: std.ArrayListUnmanaged(ast.Error) = .{},
277 source_buffer: *std.ArrayList(u8),
278 err: Error,
279266 source_manager: *clang.SourceManager,
280267 decl_table: std.AutoArrayHashMapUnmanaged(usize, []const u8) = .{},
281268 alias_list: AliasList,
282269 global_scope: *Scope.Root,
283270 clang_context: *clang.ASTContext,
284271 mangle_count: u32 = 0,
285 root_decls: std.ArrayListUnmanaged(*ast.Node) = .{},
286272 opaque_demotes: std.AutoHashMapUnmanaged(usize, void) = .{},
287273
288274 /// This one is different than the root scope's name table. This contains
......@@ -311,90 +297,15 @@ pub const Context = struct {
311297 const column = c.source_manager.getSpellingColumnNumber(spelling_loc);
312298 return std.fmt.allocPrint(c.arena, "{s}:{d}:{d}", .{ filename, line, column });
313299 }
314
315 fn createCall(c: *Context, fn_expr: *ast.Node, params_len: ast.NodeIndex) !*ast.Node.Call {
316 _ = try appendToken(c, .LParen, "(");
317 const node = try ast.Node.Call.alloc(c.arena, params_len);
318 node.* = .{
319 .lhs = fn_expr,
320 .params_len = params_len,
321 .async_token = null,
322 .rtoken = undefined, // set after appending args
323 };
324 return node;
325 }
326
327 fn createBuiltinCall(c: *Context, name: []const u8, params_len: ast.NodeIndex) !*ast.Node.BuiltinCall {
328 const builtin_token = try appendToken(c, .Builtin, name);
329 _ = try appendToken(c, .LParen, "(");
330 const node = try ast.Node.BuiltinCall.alloc(c.arena, params_len);
331 node.* = .{
332 .builtin_token = builtin_token,
333 .params_len = params_len,
334 .rparen_token = undefined, // set after appending args
335 };
336 return node;
337 }
338
339 fn createBlock(c: *Context, statements_len: ast.NodeIndex) !*ast.Node.Block {
340 const block_node = try ast.Node.Block.alloc(c.arena, statements_len);
341 block_node.* = .{
342 .lbrace = try appendToken(c, .LBrace, "{"),
343 .statements_len = statements_len,
344 .rbrace = undefined,
345 };
346 return block_node;
347 }
348300};
349301
350fn addCBuiltinsNamespace(c: *Context) Error!void {
351 // pub usingnamespace @import("std").c.builtins;
352 const pub_tok = try appendToken(c, .Keyword_pub, "pub");
353 const use_tok = try appendToken(c, .Keyword_usingnamespace, "usingnamespace");
354 const import_tok = try appendToken(c, .Builtin, "@import");
355 const lparen_tok = try appendToken(c, .LParen, "(");
356 const std_tok = try appendToken(c, .StringLiteral, "\"std\"");
357 const rparen_tok = try appendToken(c, .RParen, ")");
358
359 const std_node = try c.arena.create(ast.Node.OneToken);
360 std_node.* = .{
361 .base = .{ .tag = .StringLiteral },
362 .token = std_tok,
363 };
364
365 const call_node = try ast.Node.BuiltinCall.alloc(c.arena, 1);
366 call_node.* = .{
367 .builtin_token = import_tok,
368 .params_len = 1,
369 .rparen_token = rparen_tok,
370 };
371 call_node.params()[0] = &std_node.base;
372
373 var access_chain = &call_node.base;
374 access_chain = try transCreateNodeFieldAccess(c, access_chain, "c");
375 access_chain = try transCreateNodeFieldAccess(c, access_chain, "builtins");
376
377 const semi_tok = try appendToken(c, .Semicolon, ";");
378
379 const bytes = try c.gpa.alignedAlloc(u8, @alignOf(ast.Node.Use), @sizeOf(ast.Node.Use));
380 const using_node = @ptrCast(*ast.Node.Use, bytes.ptr);
381 using_node.* = .{
382 .doc_comments = null,
383 .visib_token = pub_tok,
384 .use_token = use_tok,
385 .expr = access_chain,
386 .semicolon_token = semi_tok,
387 };
388 try c.root_decls.append(c.gpa, &using_node.base);
389}
390
391302pub fn translate(
392303 gpa: *mem.Allocator,
393304 args_begin: [*]?[*]const u8,
394305 args_end: [*]?[*]const u8,
395306 errors: *[]ClangErrMsg,
396307 resources_path: [*:0]const u8,
397) !*ast.Tree {
308) !std.zig.ast.Tree {
398309 const ast_unit = clang.LoadFromCommandLine(
399310 args_begin,
400311 args_end,
......@@ -407,9 +318,6 @@ pub fn translate(
407318 };
408319 defer ast_unit.delete();
409320
410 var source_buffer = std.ArrayList(u8).init(gpa);
411 defer source_buffer.deinit();
412
413321 // For memory that has the same lifetime as the Tree that we return
414322 // from this function.
415323 var arena = std.heap.ArenaAllocator.init(gpa);
......@@ -418,9 +326,7 @@ pub fn translate(
418326 var context = Context{
419327 .gpa = gpa,
420328 .arena = &arena.allocator,
421 .source_buffer = &source_buffer,
422329 .source_manager = ast_unit.getSourceManager(),
423 .err = undefined,
424330 .alias_list = AliasList.init(gpa),
425331 .global_scope = try arena.allocator.create(Scope.Root),
426332 .clang_context = ast_unit.getASTContext(),
......@@ -429,20 +335,17 @@ pub fn translate(
429335 defer {
430336 context.decl_table.deinit(gpa);
431337 context.alias_list.deinit();
432 context.token_ids.deinit(gpa);
433 context.token_locs.deinit(gpa);
434 context.errors.deinit(gpa);
435338 context.global_names.deinit(gpa);
436 context.root_decls.deinit(gpa);
437339 context.opaque_demotes.deinit(gpa);
340 context.global_scope.deinit();
438341 }
439342
440 try addCBuiltinsNamespace(&context);
343 try context.global_scope.nodes.append(Tag.usingnamespace_builtins.init());
441344
442345 try prepopulateGlobalNameTable(ast_unit, &context);
443346
444347 if (!ast_unit.visitLocalTopLevelDecls(&context, declVisitorC)) {
445 return context.err;
348 return error.OutOfMemory;
446349 }
447350
448351 try transPreprocessorEntities(&context, ast_unit);
......@@ -450,38 +353,17 @@ pub fn translate(
450353 try addMacros(&context);
451354 for (context.alias_list.items) |alias| {
452355 if (!context.global_scope.sym_table.contains(alias.alias)) {
453 try createAlias(&context, alias);
454 }
455 }
456
457 const eof_token = try appendToken(&context, .Eof, "");
458 const root_node = try ast.Node.Root.create(&arena.allocator, context.root_decls.items.len, eof_token);
459 mem.copy(*ast.Node, root_node.decls(), context.root_decls.items);
460
461 if (false) {
462 std.debug.warn("debug source:\n{s}\n==EOF==\ntokens:\n", .{source_buffer.items});
463 for (context.token_ids.items) |token| {
464 std.debug.warn("{}\n", .{token});
356 const node = try Tag.alias.create(context.arena, .{ .actual = alias.alias, .mangled = alias.name });
357 try addTopLevelDecl(&context, alias.alias, node);
465358 }
466359 }
467360
468 const tree = try arena.allocator.create(ast.Tree);
469 tree.* = .{
470 .gpa = gpa,
471 .source = try arena.allocator.dupe(u8, source_buffer.items),
472 .token_ids = context.token_ids.toOwnedSlice(gpa),
473 .token_locs = context.token_locs.toOwnedSlice(gpa),
474 .errors = context.errors.toOwnedSlice(gpa),
475 .root_node = root_node,
476 .arena = arena.state,
477 .generated = true,
478 };
479 return tree;
361 return ast.render(gpa, context.global_scope.nodes.items);
480362}
481363
482364fn prepopulateGlobalNameTable(ast_unit: *clang.ASTUnit, c: *Context) !void {
483365 if (!ast_unit.visitLocalTopLevelDecls(c, declVisitorNamesOnlyC)) {
484 return c.err;
366 return error.OutOfMemory;
485367 }
486368
487369 // TODO if we see #undef, delete it from the table
......@@ -504,19 +386,13 @@ fn prepopulateGlobalNameTable(ast_unit: *clang.ASTUnit, c: *Context) !void {
504386
505387fn declVisitorNamesOnlyC(context: ?*c_void, decl: *const clang.Decl) callconv(.C) bool {
506388 const c = @ptrCast(*Context, @alignCast(@alignOf(Context), context));
507 declVisitorNamesOnly(c, decl) catch |err| {
508 c.err = err;
509 return false;
510 };
389 declVisitorNamesOnly(c, decl) catch return false;
511390 return true;
512391}
513392
514393fn declVisitorC(context: ?*c_void, decl: *const clang.Decl) callconv(.C) bool {
515394 const c = @ptrCast(*Context, @alignCast(@alignOf(Context), context));
516 declVisitor(c, decl) catch |err| {
517 c.err = err;
518 return false;
519 };
395 declVisitor(c, decl) catch return false;
520396 return true;
521397}
522398
......@@ -533,13 +409,13 @@ fn declVisitor(c: *Context, decl: *const clang.Decl) Error!void {
533409 return visitFnDecl(c, @ptrCast(*const clang.FunctionDecl, decl));
534410 },
535411 .Typedef => {
536 _ = try transTypeDef(c, @ptrCast(*const clang.TypedefNameDecl, decl), true);
412 try transTypeDef(c, &c.global_scope.base, @ptrCast(*const clang.TypedefNameDecl, decl));
537413 },
538414 .Enum => {
539 _ = try transEnumDecl(c, @ptrCast(*const clang.EnumDecl, decl));
415 try transEnumDecl(c, &c.global_scope.base, @ptrCast(*const clang.EnumDecl, decl));
540416 },
541417 .Record => {
542 _ = try transRecordDecl(c, @ptrCast(*const clang.RecordDecl, decl));
418 try transRecordDecl(c, &c.global_scope.base, @ptrCast(*const clang.RecordDecl, decl));
543419 },
544420 .Var => {
545421 return visitVarDecl(c, @ptrCast(*const clang.VarDecl, decl), null);
......@@ -549,7 +425,7 @@ fn declVisitor(c: *Context, decl: *const clang.Decl) Error!void {
549425 },
550426 else => {
551427 const decl_name = try c.str(decl.getDeclKindName());
552 try emitWarning(c, decl.getLocation(), "ignoring {s} declaration", .{decl_name});
428 try warn(c, &c.global_scope.base, decl.getLocation(), "ignoring {s} declaration", .{decl_name});
553429 },
554430 }
555431}
......@@ -565,7 +441,6 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void {
565441 return visitFnDecl(c, def);
566442 }
567443
568 const rp = makeRestorePoint(c);
569444 const fn_decl_loc = fn_decl.getLocation();
570445 const has_body = fn_decl.hasBody();
571446 const storage_class = fn_decl.getStorageClass();
......@@ -609,9 +484,9 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void {
609484 decl_ctx.has_body = false;
610485 decl_ctx.storage_class = .Extern;
611486 decl_ctx.is_export = false;
612 try emitWarning(c, fn_decl_loc, "TODO unable to translate variadic function, demoted to declaration", .{});
487 try warn(c, &c.global_scope.base, fn_decl_loc, "TODO unable to translate variadic function, demoted to extern", .{});
613488 }
614 break :blk transFnProto(rp, fn_decl, fn_proto_type, fn_decl_loc, decl_ctx, true) catch |err| switch (err) {
489 break :blk transFnProto(c, fn_decl, fn_proto_type, fn_decl_loc, decl_ctx, true) catch |err| switch (err) {
615490 error.UnsupportedType => {
616491 return failDecl(c, fn_decl_loc, fn_name, "unable to resolve prototype of function", .{});
617492 },
......@@ -620,7 +495,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void {
620495 },
621496 .FunctionNoProto => blk: {
622497 const fn_no_proto_type = @ptrCast(*const clang.FunctionType, fn_type);
623 break :blk transFnNoProto(rp, fn_no_proto_type, fn_decl_loc, decl_ctx, true) catch |err| switch (err) {
498 break :blk transFnNoProto(c, fn_no_proto_type, fn_decl_loc, decl_ctx, true) catch |err| switch (err) {
624499 error.UnsupportedType => {
625500 return failDecl(c, fn_decl_loc, fn_name, "unable to resolve prototype of function", .{});
626501 },
......@@ -631,124 +506,99 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void {
631506 };
632507
633508 if (!decl_ctx.has_body) {
634 const semi_tok = try appendToken(c, .Semicolon, ";");
635 return addTopLevelDecl(c, fn_name, &proto_node.base);
509 return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base));
636510 }
637511
638512 // actual function definition with body
639513 const body_stmt = fn_decl.getBody();
640 var block_scope = try Scope.Block.init(rp.c, &c.global_scope.base, false);
514 var block_scope = try Scope.Block.init(c, &c.global_scope.base, false);
641515 block_scope.return_type = return_qt;
642516 defer block_scope.deinit();
643517
644518 var scope = &block_scope.base;
645519
646520 var param_id: c_uint = 0;
647 for (proto_node.params()) |*param, i| {
648 const param_name = if (param.name_token) |name_tok|
649 tokenSlice(c, name_tok)
650 else
651 return failDecl(c, fn_decl_loc, fn_name, "function {s} parameter has no name", .{fn_name});
521 for (proto_node.data.params) |*param, i| {
522 const param_name = param.name orelse {
523 proto_node.data.is_extern = true;
524 proto_node.data.is_export = false;
525 try warn(c, &c.global_scope.base, fn_decl_loc, "function {s} parameter has no name, demoted to extern", .{fn_name});
526 return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base));
527 };
652528
653529 const c_param = fn_decl.getParamDecl(param_id);
654530 const qual_type = c_param.getOriginalType();
655531 const is_const = qual_type.isConstQualified();
656532
657533 const mangled_param_name = try block_scope.makeMangledName(c, param_name);
534 param.name = mangled_param_name;
658535
659536 if (!is_const) {
660537 const bare_arg_name = try std.fmt.allocPrint(c.arena, "arg_{s}", .{mangled_param_name});
661538 const arg_name = try block_scope.makeMangledName(c, bare_arg_name);
539 param.name = arg_name;
662540
663 const mut_tok = try appendToken(c, .Keyword_var, "var");
664 const name_tok = try appendIdentifier(c, mangled_param_name);
665 const eq_token = try appendToken(c, .Equal, "=");
666 const init_node = try transCreateNodeIdentifier(c, arg_name);
667 const semicolon_token = try appendToken(c, .Semicolon, ";");
668 const node = try ast.Node.VarDecl.create(c.arena, .{
669 .mut_token = mut_tok,
670 .name_token = name_tok,
671 .semicolon_token = semicolon_token,
672 }, .{
673 .eq_token = eq_token,
674 .init_node = init_node,
675 });
676 try block_scope.statements.append(&node.base);
677 param.name_token = try appendIdentifier(c, arg_name);
678 _ = try appendToken(c, .Colon, ":");
541 const redecl_node = try Tag.arg_redecl.create(c.arena, .{ .actual = mangled_param_name, .mangled = arg_name });
542 try block_scope.statements.append(redecl_node);
679543 }
680544
681545 param_id += 1;
682546 }
683547
684548 const casted_body = @ptrCast(*const clang.CompoundStmt, body_stmt);
685 transCompoundStmtInline(rp, &block_scope.base, casted_body, &block_scope) catch |err| switch (err) {
549 transCompoundStmtInline(c, casted_body, &block_scope) catch |err| switch (err) {
686550 error.OutOfMemory => |e| return e,
687551 error.UnsupportedTranslation,
688552 error.UnsupportedType,
689 => return failDecl(c, fn_decl_loc, fn_name, "unable to translate function", .{}),
553 => {
554 proto_node.data.is_extern = true;
555 proto_node.data.is_export = false;
556 try warn(c, &c.global_scope.base, fn_decl_loc, "unable to translate function, demoted to extern", .{});
557 return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base));
558 },
690559 };
691560 // add return statement if the function didn't have one
692561 blk: {
693 if (fn_ty.getNoReturnAttr()) break :blk;
694 if (isCVoid(return_qt)) break :blk;
695
696 if (block_scope.statements.items.len > 0) {
697 var last = block_scope.statements.items[block_scope.statements.items.len - 1];
698 while (true) {
699 switch (last.tag) {
700 .Block, .LabeledBlock => {
701 const stmts = last.blockStatements();
702 if (stmts.len == 0) break;
703
704 last = stmts[stmts.len - 1];
705 },
706 // no extra return needed
707 .Return => break :blk,
708 else => break,
709 }
710 }
562 const maybe_body = try block_scope.complete(c);
563 if (fn_ty.getNoReturnAttr() or isCVoid(return_qt) or maybe_body.isNoreturn(false)) {
564 proto_node.data.body = maybe_body;
565 break :blk;
711566 }
712567
713 const return_expr = try ast.Node.ControlFlowExpression.create(rp.c.arena, .{
714 .ltoken = try appendToken(rp.c, .Keyword_return, "return"),
715 .tag = .Return,
716 }, .{
717 .rhs = transZeroInitExpr(rp, scope, fn_decl_loc, return_qt.getTypePtr()) catch |err| switch (err) {
718 error.OutOfMemory => |e| return e,
719 error.UnsupportedTranslation,
720 error.UnsupportedType,
721 => return failDecl(c, fn_decl_loc, fn_name, "unable to create a return value for function", .{}),
568 const rhs = transZeroInitExpr(c, scope, fn_decl_loc, return_qt.getTypePtr()) catch |err| switch (err) {
569 error.OutOfMemory => |e| return e,
570 error.UnsupportedTranslation,
571 error.UnsupportedType,
572 => {
573 proto_node.data.is_extern = true;
574 proto_node.data.is_export = false;
575 try warn(c, &c.global_scope.base, fn_decl_loc, "unable to create a return value for function, demoted to extern", .{});
576 return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base));
722577 },
723 });
724 _ = try appendToken(rp.c, .Semicolon, ";");
725 try block_scope.statements.append(&return_expr.base);
578 };
579 const ret = try Tag.@"return".create(c.arena, rhs);
580 try block_scope.statements.append(ret);
581 proto_node.data.body = try block_scope.complete(c);
726582 }
727583
728 const body_node = try block_scope.complete(rp.c);
729 proto_node.setBodyNode(body_node);
730 return addTopLevelDecl(c, fn_name, &proto_node.base);
584 return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base));
731585}
732586
733fn transQualTypeMaybeInitialized(rp: RestorePoint, qt: clang.QualType, decl_init: ?*const clang.Expr, loc: clang.SourceLocation) TransError!*ast.Node {
587fn transQualTypeMaybeInitialized(c: *Context, scope: *Scope, qt: clang.QualType, decl_init: ?*const clang.Expr, loc: clang.SourceLocation) TransError!Node {
734588 return if (decl_init) |init_expr|
735 transQualTypeInitialized(rp, qt, init_expr, loc)
589 transQualTypeInitialized(c, scope, qt, init_expr, loc)
736590 else
737 transQualType(rp, qt, loc);
591 transQualType(c, scope, qt, loc);
738592}
593
739594/// if mangled_name is not null, this var decl was declared in a block scope.
740595fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]const u8) Error!void {
741596 const var_name = mangled_name orelse try c.str(@ptrCast(*const clang.NamedDecl, var_decl).getName_bytes_begin());
742597 if (c.global_scope.sym_table.contains(var_name))
743598 return; // Avoid processing this decl twice
744 const rp = makeRestorePoint(c);
745 const visib_tok = if (mangled_name) |_| null else try appendToken(c, .Keyword_pub, "pub");
746
747 const thread_local_token = if (var_decl.getTLSKind() == .None)
748 null
749 else
750 try appendToken(c, .Keyword_threadlocal, "threadlocal");
751599
600 const is_pub = mangled_name == null;
601 const is_threadlocal = var_decl.getTLSKind() != .None;
752602 const scope = &c.global_scope.base;
753603
754604 // TODO https://github.com/ziglang/zig/issues/3756
......@@ -767,211 +617,148 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co
767617 // does the same as:
768618 // extern int foo;
769619 // int foo = 2;
770 const extern_tok = if (storage_class == .Extern and !has_init)
771 try appendToken(c, .Keyword_extern, "extern")
772 else if (storage_class != .Static)
773 try appendToken(c, .Keyword_export, "export")
774 else
775 null;
776
777 const mut_tok = if (is_const)
778 try appendToken(c, .Keyword_const, "const")
779 else
780 try appendToken(c, .Keyword_var, "var");
620 var is_extern = storage_class == .Extern and !has_init;
621 var is_export = !is_extern and storage_class != .Static;
781622
782 const name_tok = try appendIdentifier(c, checked_name);
783
784 _ = try appendToken(c, .Colon, ":");
785
786 const type_node = transQualTypeMaybeInitialized(rp, qual_type, decl_init, var_decl_loc) catch |err| switch (err) {
623 const type_node = transQualTypeMaybeInitialized(c, scope, qual_type, decl_init, var_decl_loc) catch |err| switch (err) {
787624 error.UnsupportedTranslation, error.UnsupportedType => {
788625 return failDecl(c, var_decl_loc, checked_name, "unable to resolve variable type", .{});
789626 },
790627 error.OutOfMemory => |e| return e,
791628 };
792629
793 var eq_tok: ast.TokenIndex = undefined;
794 var init_node: ?*ast.Node = null;
630 var init_node: ?Node = null;
795631
796632 // If the initialization expression is not present, initialize with undefined.
797633 // If it is an integer literal, we can skip the @as since it will be redundant
798634 // with the variable type.
799 if (has_init) {
800 eq_tok = try appendToken(c, .Equal, "=");
635 if (has_init) trans_init: {
801636 if (decl_init) |expr| {
802637 const node_or_error = if (expr.getStmtClass() == .StringLiteralClass)
803 transStringLiteralAsArray(rp, &c.global_scope.base, @ptrCast(*const clang.StringLiteral, expr), zigArraySize(rp.c, type_node) catch 0)
638 transStringLiteralAsArray(c, scope, @ptrCast(*const clang.StringLiteral, expr), zigArraySize(c, type_node) catch 0)
804639 else
805 transExprCoercing(rp, scope, expr, .used, .r_value);
640 transExprCoercing(c, scope, expr, .used);
806641 init_node = node_or_error catch |err| switch (err) {
807642 error.UnsupportedTranslation,
808643 error.UnsupportedType,
809644 => {
810 return failDecl(c, var_decl_loc, checked_name, "unable to translate initializer", .{});
645 is_extern = true;
646 is_export = false;
647 try warn(c, scope, var_decl_loc, "unable to translate variable initializer, demoted to extern", .{});
648 break :trans_init;
811649 },
812650 error.OutOfMemory => |e| return e,
813651 };
652 if (!qualTypeIsBoolean(qual_type) and isBoolRes(init_node.?)) {
653 init_node = try Tag.bool_to_int.create(c.arena, init_node.?);
654 }
814655 } else {
815 init_node = try transCreateNodeUndefinedLiteral(c);
656 init_node = Tag.undefined_literal.init();
816657 }
817658 } else if (storage_class != .Extern) {
818 eq_tok = try appendToken(c, .Equal, "=");
819659 // The C language specification states that variables with static or threadlocal
820660 // storage without an initializer are initialized to a zero value.
821661
822662 // @import("std").mem.zeroes(T)
823 const import_fn_call = try c.createBuiltinCall("@import", 1);
824 const std_node = try transCreateNodeStringLiteral(c, "\"std\"");
825 import_fn_call.params()[0] = std_node;
826 import_fn_call.rparen_token = try appendToken(c, .RParen, ")");
827 const inner_field_access = try transCreateNodeFieldAccess(c, &import_fn_call.base, "mem");
828 const outer_field_access = try transCreateNodeFieldAccess(c, inner_field_access, "zeroes");
829
830 const zero_init_call = try c.createCall(outer_field_access, 1);
831 zero_init_call.params()[0] = type_node;
832 zero_init_call.rtoken = try appendToken(c, .RParen, ")");
833
834 init_node = &zero_init_call.base;
663 init_node = try Tag.std_mem_zeroes.create(c.arena, type_node);
835664 }
836665
837 const linksection_expr = blk: {
666 const linksection_string = blk: {
838667 var str_len: usize = undefined;
839668 if (var_decl.getSectionAttribute(&str_len)) |str_ptr| {
840 _ = try appendToken(rp.c, .Keyword_linksection, "linksection");
841 _ = try appendToken(rp.c, .LParen, "(");
842 const expr = try transCreateNodeStringLiteral(
843 rp.c,
844 try std.fmt.allocPrint(rp.c.arena, "\"{s}\"", .{str_ptr[0..str_len]}),
845 );
846 _ = try appendToken(rp.c, .RParen, ")");
847
848 break :blk expr;
669 break :blk str_ptr[0..str_len];
849670 }
850671 break :blk null;
851672 };
852673
853 const align_expr = blk: {
854 const alignment = var_decl.getAlignedAttribute(rp.c.clang_context);
674 const alignment = blk: {
675 const alignment = var_decl.getAlignedAttribute(c.clang_context);
855676 if (alignment != 0) {
856 _ = try appendToken(rp.c, .Keyword_align, "align");
857 _ = try appendToken(rp.c, .LParen, "(");
858677 // Clang reports the alignment in bits
859 const expr = try transCreateNodeInt(rp.c, alignment / 8);
860 _ = try appendToken(rp.c, .RParen, ")");
861
862 break :blk expr;
678 break :blk alignment / 8;
863679 }
864680 break :blk null;
865681 };
866682
867 const node = try ast.Node.VarDecl.create(c.arena, .{
868 .name_token = name_tok,
869 .mut_token = mut_tok,
870 .semicolon_token = try appendToken(c, .Semicolon, ";"),
871 }, .{
872 .visib_token = visib_tok,
873 .thread_local_token = thread_local_token,
874 .eq_token = eq_tok,
875 .extern_export_token = extern_tok,
876 .type_node = type_node,
877 .align_node = align_expr,
878 .section_node = linksection_expr,
879 .init_node = init_node,
683 const node = try Tag.var_decl.create(c.arena, .{
684 .is_pub = is_pub,
685 .is_const = is_const,
686 .is_extern = is_extern,
687 .is_export = is_export,
688 .is_threadlocal = is_threadlocal,
689 .linksection_string = linksection_string,
690 .alignment = alignment,
691 .name = checked_name,
692 .type = type_node,
693 .init = init_node,
880694 });
881 return addTopLevelDecl(c, checked_name, &node.base);
882}
883
884fn transTypeDefAsBuiltin(c: *Context, typedef_decl: *const clang.TypedefNameDecl, builtin_name: []const u8) !*ast.Node {
885 _ = try c.decl_table.put(c.gpa, @ptrToInt(typedef_decl.getCanonicalDecl()), builtin_name);
886 return transCreateNodeIdentifier(c, builtin_name);
887}
888
889fn checkForBuiltinTypedef(checked_name: []const u8) ?[]const u8 {
890 const table = [_][2][]const u8{
891 .{ "uint8_t", "u8" },
892 .{ "int8_t", "i8" },
893 .{ "uint16_t", "u16" },
894 .{ "int16_t", "i16" },
895 .{ "uint32_t", "u32" },
896 .{ "int32_t", "i32" },
897 .{ "uint64_t", "u64" },
898 .{ "int64_t", "i64" },
899 .{ "intptr_t", "isize" },
900 .{ "uintptr_t", "usize" },
901 .{ "ssize_t", "isize" },
902 .{ "size_t", "usize" },
903 };
904
905 for (table) |entry| {
906 if (mem.eql(u8, checked_name, entry[0])) {
907 return entry[1];
908 }
909 }
910
911 return null;
912}
695 return addTopLevelDecl(c, checked_name, node);
696}
697
698const builtin_typedef_map = std.ComptimeStringMap([]const u8, .{
699 .{ "uint8_t", "u8" },
700 .{ "int8_t", "i8" },
701 .{ "uint16_t", "u16" },
702 .{ "int16_t", "i16" },
703 .{ "uint32_t", "u32" },
704 .{ "int32_t", "i32" },
705 .{ "uint64_t", "u64" },
706 .{ "int64_t", "i64" },
707 .{ "intptr_t", "isize" },
708 .{ "uintptr_t", "usize" },
709 .{ "ssize_t", "isize" },
710 .{ "size_t", "usize" },
711});
913712
914fn transTypeDef(c: *Context, typedef_decl: *const clang.TypedefNameDecl, top_level_visit: bool) Error!?*ast.Node {
713fn transTypeDef(c: *Context, scope: *Scope, typedef_decl: *const clang.TypedefNameDecl) Error!void {
915714 if (c.decl_table.get(@ptrToInt(typedef_decl.getCanonicalDecl()))) |name|
916 return transCreateNodeIdentifier(c, name); // Avoid processing this decl twice
917 const rp = makeRestorePoint(c);
715 return; // Avoid processing this decl twice
716 const toplevel = scope.id == .root;
717 const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined;
918718
919 const typedef_name = try c.str(@ptrCast(*const clang.NamedDecl, typedef_decl).getName_bytes_begin());
719 const bare_name = try c.str(@ptrCast(*const clang.NamedDecl, typedef_decl).getName_bytes_begin());
920720
921721 // TODO https://github.com/ziglang/zig/issues/3756
922722 // TODO https://github.com/ziglang/zig/issues/1802
923 const checked_name = if (isZigPrimitiveType(typedef_name)) try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ typedef_name, c.getMangle() }) else typedef_name;
924 if (checkForBuiltinTypedef(checked_name)) |builtin| {
925 return transTypeDefAsBuiltin(c, typedef_decl, builtin);
926 }
927
928 if (!top_level_visit) {
929 return transCreateNodeIdentifier(c, checked_name);
723 var name: []const u8 = if (isZigPrimitiveType(bare_name)) try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ bare_name, c.getMangle() }) else bare_name;
724 if (builtin_typedef_map.get(name)) |builtin| {
725 return c.decl_table.putNoClobber(c.gpa, @ptrToInt(typedef_decl.getCanonicalDecl()), builtin);
930726 }
727 if (!toplevel) name = try bs.makeMangledName(c, name);
728 try c.decl_table.putNoClobber(c.gpa, @ptrToInt(typedef_decl.getCanonicalDecl()), name);
931729
932 _ = try c.decl_table.put(c.gpa, @ptrToInt(typedef_decl.getCanonicalDecl()), checked_name);
933 const node = (try transCreateNodeTypedef(rp, typedef_decl, true, checked_name)) orelse return null;
934 try addTopLevelDecl(c, checked_name, node);
935 return transCreateNodeIdentifier(c, checked_name);
936}
937
938fn transCreateNodeTypedef(
939 rp: RestorePoint,
940 typedef_decl: *const clang.TypedefNameDecl,
941 toplevel: bool,
942 checked_name: []const u8,
943) Error!?*ast.Node {
944 const visib_tok = if (toplevel) try appendToken(rp.c, .Keyword_pub, "pub") else null;
945 const mut_tok = try appendToken(rp.c, .Keyword_const, "const");
946 const name_tok = try appendIdentifier(rp.c, checked_name);
947 const eq_token = try appendToken(rp.c, .Equal, "=");
948730 const child_qt = typedef_decl.getUnderlyingType();
949731 const typedef_loc = typedef_decl.getLocation();
950 const init_node = transQualType(rp, child_qt, typedef_loc) catch |err| switch (err) {
732 const init_node = transQualType(c, scope, child_qt, typedef_loc) catch |err| switch (err) {
951733 error.UnsupportedType => {
952 try failDecl(rp.c, typedef_loc, checked_name, "unable to resolve typedef child type", .{});
953 return null;
734 return failDecl(c, typedef_loc, name, "unable to resolve typedef child type", .{});
954735 },
955736 error.OutOfMemory => |e| return e,
956737 };
957 const semicolon_token = try appendToken(rp.c, .Semicolon, ";");
958
959 const node = try ast.Node.VarDecl.create(rp.c.arena, .{
960 .name_token = name_tok,
961 .mut_token = mut_tok,
962 .semicolon_token = semicolon_token,
963 }, .{
964 .visib_token = visib_tok,
965 .eq_token = eq_token,
966 .init_node = init_node,
967 });
968 return &node.base;
738
739 const payload = try c.arena.create(ast.Payload.SimpleVarDecl);
740 payload.* = .{
741 .base = .{ .tag = ([2]Tag{ .var_simple, .pub_var_simple })[@boolToInt(toplevel)] },
742 .data = .{
743 .name = name,
744 .init = init_node,
745 },
746 };
747 const node = Node.initPayload(&payload.base);
748
749 if (toplevel) {
750 try addTopLevelDecl(c, name, node);
751 } else {
752 try scope.appendNode(node);
753 }
969754}
970755
971fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*ast.Node {
756fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordDecl) Error!void {
972757 if (c.decl_table.get(@ptrToInt(record_decl.getCanonicalDecl()))) |name|
973 return try transCreateNodeIdentifier(c, name); // Avoid processing this decl twice
758 return; // Avoid processing this decl twice
974759 const record_loc = record_decl.getLocation();
760 const toplevel = scope.id == .root;
761 const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined;
975762
976763 var bare_name = try c.str(@ptrCast(*const clang.NamedDecl, record_decl).getName_bytes_begin());
977764 var is_unnamed = false;
......@@ -983,46 +770,31 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as
983770 }
984771
985772 var container_kind_name: []const u8 = undefined;
986 var container_kind: std.zig.Token.Id = undefined;
773 var is_union = false;
987774 if (record_decl.isUnion()) {
988775 container_kind_name = "union";
989 container_kind = .Keyword_union;
776 is_union = true;
990777 } else if (record_decl.isStruct()) {
991778 container_kind_name = "struct";
992 container_kind = .Keyword_struct;
993779 } else {
994 try emitWarning(c, record_loc, "record {s} is not a struct or union", .{bare_name});
995 return null;
780 try c.decl_table.putNoClobber(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), bare_name);
781 return failDecl(c, record_loc, bare_name, "record {s} is not a struct or union", .{bare_name});
996782 }
997783
998 const name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ container_kind_name, bare_name });
999 _ = try c.decl_table.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), name);
1000
1001 const visib_tok = if (!is_unnamed) try appendToken(c, .Keyword_pub, "pub") else null;
1002 const mut_tok = try appendToken(c, .Keyword_const, "const");
1003 const name_tok = try appendIdentifier(c, name);
1004
1005 const eq_token = try appendToken(c, .Equal, "=");
784 var name: []const u8 = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ container_kind_name, bare_name });
785 if (!toplevel) name = try bs.makeMangledName(c, name);
786 try c.decl_table.putNoClobber(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), name);
1006787
1007 var semicolon: ast.TokenIndex = undefined;
788 const is_pub = toplevel and !is_unnamed;
1008789 const init_node = blk: {
1009 const rp = makeRestorePoint(c);
1010790 const record_def = record_decl.getDefinition() orelse {
1011791 _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {});
1012 const opaque_type = try transCreateNodeOpaqueType(c);
1013 semicolon = try appendToken(c, .Semicolon, ";");
1014 break :blk opaque_type;
792 break :blk Tag.opaque_literal.init();
1015793 };
1016794
1017 const layout_tok = try if (record_decl.getPackedAttribute())
1018 appendToken(c, .Keyword_packed, "packed")
1019 else
1020 appendToken(c, .Keyword_extern, "extern");
1021 const container_tok = try appendToken(c, container_kind, container_kind_name);
1022 const lbrace_token = try appendToken(c, .LBrace, "{");
1023
1024 var fields_and_decls = std.ArrayList(*ast.Node).init(c.gpa);
1025 defer fields_and_decls.deinit();
795 const is_packed = record_decl.getPackedAttribute();
796 var fields = std.ArrayList(ast.Payload.Record.Field).init(c.gpa);
797 defer fields.deinit();
1026798
1027799 var unnamed_field_count: u32 = 0;
1028800 var it = record_def.field_begin();
......@@ -1034,111 +806,88 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as
1034806
1035807 if (field_decl.isBitField()) {
1036808 _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {});
1037 const opaque_type = try transCreateNodeOpaqueType(c);
1038 semicolon = try appendToken(c, .Semicolon, ";");
1039 try emitWarning(c, field_loc, "{s} demoted to opaque type - has bitfield", .{container_kind_name});
1040 break :blk opaque_type;
809 try warn(c, scope, field_loc, "{s} demoted to opaque type - has bitfield", .{container_kind_name});
810 break :blk Tag.opaque_literal.init();
1041811 }
1042812
1043813 if (qualTypeCanon(field_qt).isIncompleteOrZeroLengthArrayType(c.clang_context)) {
1044814 _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {});
1045 const opaque_type = try transCreateNodeOpaqueType(c);
1046 semicolon = try appendToken(c, .Semicolon, ";");
1047 try emitWarning(c, field_loc, "{s} demoted to opaque type - has variable length array", .{container_kind_name});
1048 break :blk opaque_type;
815 try warn(c, scope, field_loc, "{s} demoted to opaque type - has variable length array", .{container_kind_name});
816 break :blk Tag.opaque_literal.init();
1049817 }
1050818
1051819 var is_anon = false;
1052 var raw_name = try c.str(@ptrCast(*const clang.NamedDecl, field_decl).getName_bytes_begin());
1053 if (field_decl.isAnonymousStructOrUnion() or raw_name.len == 0) {
820 var field_name = try c.str(@ptrCast(*const clang.NamedDecl, field_decl).getName_bytes_begin());
821 if (field_decl.isAnonymousStructOrUnion() or field_name.len == 0) {
1054822 // Context.getMangle() is not used here because doing so causes unpredictable field names for anonymous fields.
1055 raw_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{unnamed_field_count});
823 field_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{unnamed_field_count});
1056824 unnamed_field_count += 1;
1057825 is_anon = true;
1058826 }
1059 const field_name = try appendIdentifier(c, raw_name);
1060 _ = try appendToken(c, .Colon, ":");
1061 const field_type = transQualType(rp, field_qt, field_loc) catch |err| switch (err) {
827 const field_type = transQualType(c, scope, field_qt, field_loc) catch |err| switch (err) {
1062828 error.UnsupportedType => {
1063829 _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {});
1064 const opaque_type = try transCreateNodeOpaqueType(c);
1065 semicolon = try appendToken(c, .Semicolon, ";");
1066 try emitWarning(c, record_loc, "{s} demoted to opaque type - unable to translate type of field {s}", .{ container_kind_name, raw_name });
1067 break :blk opaque_type;
830 try warn(c, scope, record_loc, "{s} demoted to opaque type - unable to translate type of field {s}", .{ container_kind_name, field_name });
831 break :blk Tag.opaque_literal.init();
1068832 },
1069833 else => |e| return e,
1070834 };
1071835
1072 const align_expr = blk_2: {
836 const alignment = blk_2: {
1073837 const alignment = field_decl.getAlignedAttribute(c.clang_context);
1074838 if (alignment != 0) {
1075 _ = try appendToken(c, .Keyword_align, "align");
1076 _ = try appendToken(c, .LParen, "(");
1077839 // Clang reports the alignment in bits
1078 const expr = try transCreateNodeInt(c, alignment / 8);
1079 _ = try appendToken(c, .RParen, ")");
1080
1081 break :blk_2 expr;
840 break :blk_2 alignment / 8;
1082841 }
1083842 break :blk_2 null;
1084843 };
1085844
1086 const field_node = try c.arena.create(ast.Node.ContainerField);
1087 field_node.* = .{
1088 .doc_comments = null,
1089 .comptime_token = null,
1090 .name_token = field_name,
1091 .type_expr = field_type,
1092 .value_expr = null,
1093 .align_expr = align_expr,
1094 };
1095
1096845 if (is_anon) {
1097 _ = try c.decl_table.put(
1098 c.gpa,
1099 @ptrToInt(field_decl.getCanonicalDecl()),
1100 raw_name,
1101 );
846 try c.decl_table.putNoClobber(c.gpa, @ptrToInt(field_decl.getCanonicalDecl()), field_name);
1102847 }
1103848
1104 try fields_and_decls.append(&field_node.base);
1105 _ = try appendToken(c, .Comma, ",");
849 try fields.append(.{
850 .name = field_name,
851 .type = field_type,
852 .alignment = alignment,
853 });
1106854 }
1107 const container_node = try ast.Node.ContainerDecl.alloc(c.arena, fields_and_decls.items.len);
1108 container_node.* = .{
1109 .layout_token = layout_tok,
1110 .kind_token = container_tok,
1111 .init_arg_expr = .None,
1112 .fields_and_decls_len = fields_and_decls.items.len,
1113 .lbrace_token = lbrace_token,
1114 .rbrace_token = try appendToken(c, .RBrace, "}"),
855
856 const record_payload = try c.arena.create(ast.Payload.Record);
857 record_payload.* = .{
858 .base = .{ .tag = ([2]Tag{ .@"struct", .@"union" })[@boolToInt(is_union)] },
859 .data = .{
860 .is_packed = is_packed,
861 .fields = try c.arena.dupe(ast.Payload.Record.Field, fields.items),
862 },
1115863 };
1116 mem.copy(*ast.Node, container_node.fieldsAndDecls(), fields_and_decls.items);
1117 semicolon = try appendToken(c, .Semicolon, ";");
1118 break :blk &container_node.base;
864 break :blk Node.initPayload(&record_payload.base);
1119865 };
1120866
1121 const node = try ast.Node.VarDecl.create(c.arena, .{
1122 .name_token = name_tok,
1123 .mut_token = mut_tok,
1124 .semicolon_token = semicolon,
1125 }, .{
1126 .visib_token = visib_tok,
1127 .eq_token = eq_token,
1128 .init_node = init_node,
1129 });
867 const payload = try c.arena.create(ast.Payload.SimpleVarDecl);
868 payload.* = .{
869 .base = .{ .tag = ([2]Tag{ .var_simple, .pub_var_simple })[@boolToInt(is_pub)] },
870 .data = .{
871 .name = name,
872 .init = init_node,
873 },
874 };
1130875
1131 try addTopLevelDecl(c, name, &node.base);
1132 if (!is_unnamed)
1133 try c.alias_list.append(.{ .alias = bare_name, .name = name });
1134 return transCreateNodeIdentifier(c, name);
876 if (toplevel) {
877 try addTopLevelDecl(c, name, Node.initPayload(&payload.base));
878 if (!is_unnamed)
879 try c.alias_list.append(.{ .alias = bare_name, .name = name });
880 } else {
881 try scope.appendNode(Node.initPayload(&payload.base));
882 }
1135883}
1136884
1137fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node {
885fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: *const clang.EnumDecl) Error!void {
1138886 if (c.decl_table.get(@ptrToInt(enum_decl.getCanonicalDecl()))) |name|
1139 return try transCreateNodeIdentifier(c, name); // Avoid processing this decl twice
1140 const rp = makeRestorePoint(c);
887 return; // Avoid processing this decl twice
1141888 const enum_loc = enum_decl.getLocation();
889 const toplevel = scope.id == .root;
890 const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined;
1142891
1143892 var bare_name = try c.str(@ptrCast(*const clang.NamedDecl, enum_decl).getName_bytes_begin());
1144893 var is_unnamed = false;
......@@ -1147,13 +896,13 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node
1147896 is_unnamed = true;
1148897 }
1149898
1150 const name = try std.fmt.allocPrint(c.arena, "enum_{s}", .{bare_name});
1151 _ = try c.decl_table.put(c.gpa, @ptrToInt(enum_decl.getCanonicalDecl()), name);
899 var name: []const u8 = try std.fmt.allocPrint(c.arena, "enum_{s}", .{bare_name});
900 if (!toplevel) _ = try bs.makeMangledName(c, name);
901 try c.decl_table.putNoClobber(c.gpa, @ptrToInt(enum_decl.getCanonicalDecl()), name);
1152902
1153 const visib_tok = if (!is_unnamed) try appendToken(c, .Keyword_pub, "pub") else null;
1154 const mut_tok = try appendToken(c, .Keyword_const, "const");
1155 const name_tok = try appendIdentifier(c, name);
1156 const eq_token = try appendToken(c, .Equal, "=");
903 const is_pub = toplevel and !is_unnamed;
904 var redecls = std.ArrayList(Tag.enum_redecl.Data()).init(c.gpa);
905 defer redecls.deinit();
1157906
1158907 const init_node = if (enum_decl.getDefinition()) |enum_def| blk: {
1159908 var pure_enum = true;
......@@ -1167,11 +916,8 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node
1167916 }
1168917 }
1169918
1170 const extern_tok = try appendToken(c, .Keyword_extern, "extern");
1171 const container_tok = try appendToken(c, .Keyword_enum, "enum");
1172
1173 var fields_and_decls = std.ArrayList(*ast.Node).init(c.gpa);
1174 defer fields_and_decls.deinit();
919 var fields = std.ArrayList(ast.Payload.Enum.Field).init(c.gpa);
920 defer fields.deinit();
1175921
1176922 const int_type = enum_decl.getIntegerType();
1177923 // The underlying type may be null in case of forward-declared enum
......@@ -1179,30 +925,22 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node
1179925 // default to the usual integer type used for all the enums.
1180926
1181927 // default to c_int since msvc and gcc default to different types
1182 _ = try appendToken(c, .LParen, "(");
1183 const init_arg_expr = ast.Node.ContainerDecl.InitArg{
1184 .Type = if (int_type.ptr != null and
1185 !isCBuiltinType(int_type, .UInt) and
1186 !isCBuiltinType(int_type, .Int))
1187 transQualType(rp, int_type, enum_loc) catch |err| switch (err) {
1188 error.UnsupportedType => {
1189 try failDecl(c, enum_loc, name, "unable to translate enum tag type", .{});
1190 return null;
1191 },
1192 else => |e| return e,
1193 }
1194 else
1195 try transCreateNodeIdentifier(c, "c_int"),
1196 };
1197 _ = try appendToken(c, .RParen, ")");
1198
1199 const lbrace_token = try appendToken(c, .LBrace, "{");
928 const init_arg_expr = if (int_type.ptr != null and
929 !isCBuiltinType(int_type, .UInt) and
930 !isCBuiltinType(int_type, .Int))
931 transQualType(c, scope, int_type, enum_loc) catch |err| switch (err) {
932 error.UnsupportedType => {
933 return failDecl(c, enum_loc, name, "unable to translate enum tag type", .{});
934 },
935 else => |e| return e,
936 }
937 else
938 try Tag.type.create(c.arena, "c_int");
1200939
1201940 it = enum_def.enumerator_begin();
1202941 end_it = enum_def.enumerator_end();
1203942 while (it.neq(end_it)) : (it = it.next()) {
1204943 const enum_const = it.deref();
1205
1206944 const enum_val_name = try c.str(@ptrCast(*const clang.NamedDecl, enum_const).getName_bytes_begin());
1207945
1208946 const field_name = if (!is_unnamed and mem.startsWith(u8, enum_val_name, bare_name))
......@@ -1210,123 +948,62 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node
1210948 else
1211949 enum_val_name;
1212950
1213 const field_name_tok = try appendIdentifier(c, field_name);
1214
1215 const int_node = if (!pure_enum) blk_2: {
1216 _ = try appendToken(c, .Colon, "=");
1217 break :blk_2 try transCreateNodeAPInt(c, enum_const.getInitVal());
1218 } else
951 const int_node = if (!pure_enum)
952 try transCreateNodeAPInt(c, enum_const.getInitVal())
953 else
1219954 null;
1220955
1221 const field_node = try c.arena.create(ast.Node.ContainerField);
1222 field_node.* = .{
1223 .doc_comments = null,
1224 .comptime_token = null,
1225 .name_token = field_name_tok,
1226 .type_expr = null,
1227 .value_expr = int_node,
1228 .align_expr = null,
1229 };
1230
1231 try fields_and_decls.append(&field_node.base);
1232 _ = try appendToken(c, .Comma, ",");
956 try fields.append(.{
957 .name = field_name,
958 .value = int_node,
959 });
1233960
1234961 // In C each enum value is in the global namespace. So we put them there too.
1235962 // At this point we can rely on the enum emitting successfully.
1236 const tld_visib_tok = try appendToken(c, .Keyword_pub, "pub");
1237 const tld_mut_tok = try appendToken(c, .Keyword_const, "const");
1238 const tld_name_tok = try appendIdentifier(c, enum_val_name);
1239 const tld_eq_token = try appendToken(c, .Equal, "=");
1240 const cast_node = try rp.c.createBuiltinCall("@enumToInt", 1);
1241 const enum_ident = try transCreateNodeIdentifier(c, name);
1242 const period_tok = try appendToken(c, .Period, ".");
1243 const field_ident = try transCreateNodeIdentifier(c, field_name);
1244 const field_access_node = try c.arena.create(ast.Node.SimpleInfixOp);
1245 field_access_node.* = .{
1246 .base = .{ .tag = .Period },
1247 .op_token = period_tok,
1248 .lhs = enum_ident,
1249 .rhs = field_ident,
1250 };
1251 cast_node.params()[0] = &field_access_node.base;
1252 cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
1253 const tld_init_node = &cast_node.base;
1254 const tld_semicolon_token = try appendToken(c, .Semicolon, ";");
1255 const tld_node = try ast.Node.VarDecl.create(c.arena, .{
1256 .name_token = tld_name_tok,
1257 .mut_token = tld_mut_tok,
1258 .semicolon_token = tld_semicolon_token,
1259 }, .{
1260 .visib_token = tld_visib_tok,
1261 .eq_token = tld_eq_token,
1262 .init_node = tld_init_node,
963 try redecls.append(.{
964 .enum_val_name = enum_val_name,
965 .field_name = field_name,
966 .enum_name = name,
1263967 });
1264 try addTopLevelDecl(c, field_name, &tld_node.base);
1265968 }
1266 // make non exhaustive
1267 const field_node = try c.arena.create(ast.Node.ContainerField);
1268 field_node.* = .{
1269 .doc_comments = null,
1270 .comptime_token = null,
1271 .name_token = try appendIdentifier(c, "_"),
1272 .type_expr = null,
1273 .value_expr = null,
1274 .align_expr = null,
1275 };
1276969
1277 try fields_and_decls.append(&field_node.base);
1278 _ = try appendToken(c, .Comma, ",");
1279 const container_node = try ast.Node.ContainerDecl.alloc(c.arena, fields_and_decls.items.len);
1280 container_node.* = .{
1281 .layout_token = extern_tok,
1282 .kind_token = container_tok,
1283 .init_arg_expr = init_arg_expr,
1284 .fields_and_decls_len = fields_and_decls.items.len,
1285 .lbrace_token = lbrace_token,
1286 .rbrace_token = try appendToken(c, .RBrace, "}"),
1287 };
1288 mem.copy(*ast.Node, container_node.fieldsAndDecls(), fields_and_decls.items);
1289 break :blk &container_node.base;
970 break :blk try Tag.@"enum".create(c.arena, .{
971 .int_type = init_arg_expr,
972 .fields = try c.arena.dupe(ast.Payload.Enum.Field, fields.items),
973 });
1290974 } else blk: {
1291975 _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(enum_decl.getCanonicalDecl()), {});
1292 break :blk try transCreateNodeOpaqueType(c);
976 break :blk Tag.opaque_literal.init();
1293977 };
1294978
1295 const semicolon_token = try appendToken(c, .Semicolon, ";");
1296 const node = try ast.Node.VarDecl.create(c.arena, .{
1297 .name_token = name_tok,
1298 .mut_token = mut_tok,
1299 .semicolon_token = semicolon_token,
1300 }, .{
1301 .visib_token = visib_tok,
1302 .eq_token = eq_token,
1303 .init_node = init_node,
1304 });
979 const payload = try c.arena.create(ast.Payload.SimpleVarDecl);
980 payload.* = .{
981 .base = .{ .tag = ([2]Tag{ .var_simple, .pub_var_simple })[@boolToInt(is_pub)] },
982 .data = .{
983 .name = name,
984 .init = init_node,
985 },
986 };
1305987
1306 try addTopLevelDecl(c, name, &node.base);
1307 if (!is_unnamed)
1308 try c.alias_list.append(.{ .alias = bare_name, .name = name });
1309 return transCreateNodeIdentifier(c, name);
1310}
1311
1312fn createAlias(c: *Context, alias: anytype) !void {
1313 const visib_tok = try appendToken(c, .Keyword_pub, "pub");
1314 const mut_tok = try appendToken(c, .Keyword_const, "const");
1315 const name_tok = try appendIdentifier(c, alias.alias);
1316 const eq_token = try appendToken(c, .Equal, "=");
1317 const init_node = try transCreateNodeIdentifier(c, alias.name);
1318 const semicolon_token = try appendToken(c, .Semicolon, ";");
1319
1320 const node = try ast.Node.VarDecl.create(c.arena, .{
1321 .name_token = name_tok,
1322 .mut_token = mut_tok,
1323 .semicolon_token = semicolon_token,
1324 }, .{
1325 .visib_token = visib_tok,
1326 .eq_token = eq_token,
1327 .init_node = init_node,
1328 });
1329 return addTopLevelDecl(c, alias.alias, &node.base);
988 if (toplevel) {
989 try addTopLevelDecl(c, name, Node.initPayload(&payload.base));
990 if (!is_unnamed)
991 try c.alias_list.append(.{ .alias = bare_name, .name = name });
992 } else {
993 try scope.appendNode(Node.initPayload(&payload.base));
994 }
995
996 for (redecls.items) |redecl| {
997 if (toplevel) {
998 try addTopLevelDecl(c, redecl.field_name, try Tag.pub_enum_redecl.create(c.arena, redecl));
999 } else {
1000 try scope.appendNode(try Tag.enum_redecl.create(c.arena, .{
1001 .enum_val_name = try bs.makeMangledName(c, redecl.enum_val_name),
1002 .field_name = redecl.field_name,
1003 .enum_name = redecl.enum_name,
1004 }));
1005 }
1006 }
13301007}
13311008
13321009const ResultUsed = enum {
......@@ -1334,317 +1011,252 @@ const ResultUsed = enum {
13341011 unused,
13351012};
13361013
1337const LRValue = enum {
1338 l_value,
1339 r_value,
1340};
1341
13421014fn transStmt(
1343 rp: RestorePoint,
1015 c: *Context,
13441016 scope: *Scope,
13451017 stmt: *const clang.Stmt,
13461018 result_used: ResultUsed,
1347 lrvalue: LRValue,
1348) TransError!*ast.Node {
1019) TransError!Node {
13491020 const sc = stmt.getStmtClass();
13501021 switch (sc) {
1351 .BinaryOperatorClass => return transBinaryOperator(rp, scope, @ptrCast(*const clang.BinaryOperator, stmt), result_used),
1352 .CompoundStmtClass => return transCompoundStmt(rp, scope, @ptrCast(*const clang.CompoundStmt, stmt)),
1353 .CStyleCastExprClass => return transCStyleCastExprClass(rp, scope, @ptrCast(*const clang.CStyleCastExpr, stmt), result_used, lrvalue),
1354 .DeclStmtClass => return transDeclStmt(rp, scope, @ptrCast(*const clang.DeclStmt, stmt)),
1355 .DeclRefExprClass => return transDeclRefExpr(rp, scope, @ptrCast(*const clang.DeclRefExpr, stmt), lrvalue),
1356 .ImplicitCastExprClass => return transImplicitCastExpr(rp, scope, @ptrCast(*const clang.ImplicitCastExpr, stmt), result_used),
1357 .IntegerLiteralClass => return transIntegerLiteral(rp, scope, @ptrCast(*const clang.IntegerLiteral, stmt), result_used, .with_as),
1358 .ReturnStmtClass => return transReturnStmt(rp, scope, @ptrCast(*const clang.ReturnStmt, stmt)),
1359 .StringLiteralClass => return transStringLiteral(rp, scope, @ptrCast(*const clang.StringLiteral, stmt), result_used),
1022 .BinaryOperatorClass => return transBinaryOperator(c, scope, @ptrCast(*const clang.BinaryOperator, stmt), result_used),
1023 .CompoundStmtClass => return transCompoundStmt(c, scope, @ptrCast(*const clang.CompoundStmt, stmt)),
1024 .CStyleCastExprClass => return transCStyleCastExprClass(c, scope, @ptrCast(*const clang.CStyleCastExpr, stmt), result_used),
1025 .DeclStmtClass => return transDeclStmt(c, scope, @ptrCast(*const clang.DeclStmt, stmt)),
1026 .DeclRefExprClass => return transDeclRefExpr(c, scope, @ptrCast(*const clang.DeclRefExpr, stmt)),
1027 .ImplicitCastExprClass => return transImplicitCastExpr(c, scope, @ptrCast(*const clang.ImplicitCastExpr, stmt), result_used),
1028 .IntegerLiteralClass => return transIntegerLiteral(c, scope, @ptrCast(*const clang.IntegerLiteral, stmt), result_used, .with_as),
1029 .ReturnStmtClass => return transReturnStmt(c, scope, @ptrCast(*const clang.ReturnStmt, stmt)),
1030 .StringLiteralClass => return transStringLiteral(c, scope, @ptrCast(*const clang.StringLiteral, stmt), result_used),
13601031 .ParenExprClass => {
1361 const expr = try transExpr(rp, scope, @ptrCast(*const clang.ParenExpr, stmt).getSubExpr(), .used, lrvalue);
1362 if (expr.tag == .GroupedExpression) return maybeSuppressResult(rp, scope, result_used, expr);
1363 const node = try rp.c.arena.create(ast.Node.GroupedExpression);
1364 node.* = .{
1365 .lparen = try appendToken(rp.c, .LParen, "("),
1366 .expr = expr,
1367 .rparen = try appendToken(rp.c, .RParen, ")"),
1368 };
1369 return maybeSuppressResult(rp, scope, result_used, &node.base);
1370 },
1371 .InitListExprClass => return transInitListExpr(rp, scope, @ptrCast(*const clang.InitListExpr, stmt), result_used),
1372 .ImplicitValueInitExprClass => return transImplicitValueInitExpr(rp, scope, @ptrCast(*const clang.Expr, stmt), result_used),
1373 .IfStmtClass => return transIfStmt(rp, scope, @ptrCast(*const clang.IfStmt, stmt)),
1374 .WhileStmtClass => return transWhileLoop(rp, scope, @ptrCast(*const clang.WhileStmt, stmt)),
1375 .DoStmtClass => return transDoWhileLoop(rp, scope, @ptrCast(*const clang.DoStmt, stmt)),
1032 const expr = try transExpr(c, scope, @ptrCast(*const clang.ParenExpr, stmt).getSubExpr(), .used);
1033 return maybeSuppressResult(c, scope, result_used, expr);
1034 },
1035 .InitListExprClass => return transInitListExpr(c, scope, @ptrCast(*const clang.InitListExpr, stmt), result_used),
1036 .ImplicitValueInitExprClass => return transImplicitValueInitExpr(c, scope, @ptrCast(*const clang.Expr, stmt), result_used),
1037 .IfStmtClass => return transIfStmt(c, scope, @ptrCast(*const clang.IfStmt, stmt)),
1038 .WhileStmtClass => return transWhileLoop(c, scope, @ptrCast(*const clang.WhileStmt, stmt)),
1039 .DoStmtClass => return transDoWhileLoop(c, scope, @ptrCast(*const clang.DoStmt, stmt)),
13761040 .NullStmtClass => {
1377 const block = try rp.c.createBlock(0);
1378 block.rbrace = try appendToken(rp.c, .RBrace, "}");
1379 return &block.base;
1380 },
1381 .ContinueStmtClass => return try transCreateNodeContinue(rp.c),
1382 .BreakStmtClass => return transBreak(rp, scope),
1383 .ForStmtClass => return transForLoop(rp, scope, @ptrCast(*const clang.ForStmt, stmt)),
1384 .FloatingLiteralClass => return transFloatingLiteral(rp, scope, @ptrCast(*const clang.FloatingLiteral, stmt), result_used),
1041 return Tag.empty_block.init();
1042 },
1043 .ContinueStmtClass => return Tag.@"continue".init(),
1044 .BreakStmtClass => return Tag.@"break".init(),
1045 .ForStmtClass => return transForLoop(c, scope, @ptrCast(*const clang.ForStmt, stmt)),
1046 .FloatingLiteralClass => return transFloatingLiteral(c, scope, @ptrCast(*const clang.FloatingLiteral, stmt), result_used),
13851047 .ConditionalOperatorClass => {
1386 return transConditionalOperator(rp, scope, @ptrCast(*const clang.ConditionalOperator, stmt), result_used);
1048 return transConditionalOperator(c, scope, @ptrCast(*const clang.ConditionalOperator, stmt), result_used);
13871049 },
13881050 .BinaryConditionalOperatorClass => {
1389 return transBinaryConditionalOperator(rp, scope, @ptrCast(*const clang.BinaryConditionalOperator, stmt), result_used);
1390 },
1391 .SwitchStmtClass => return transSwitch(rp, scope, @ptrCast(*const clang.SwitchStmt, stmt)),
1392 .CaseStmtClass => return transCase(rp, scope, @ptrCast(*const clang.CaseStmt, stmt)),
1393 .DefaultStmtClass => return transDefault(rp, scope, @ptrCast(*const clang.DefaultStmt, stmt)),
1394 .ConstantExprClass => return transConstantExpr(rp, scope, @ptrCast(*const clang.Expr, stmt), result_used),
1395 .PredefinedExprClass => return transPredefinedExpr(rp, scope, @ptrCast(*const clang.PredefinedExpr, stmt), result_used),
1396 .CharacterLiteralClass => return transCharLiteral(rp, scope, @ptrCast(*const clang.CharacterLiteral, stmt), result_used, .with_as),
1397 .StmtExprClass => return transStmtExpr(rp, scope, @ptrCast(*const clang.StmtExpr, stmt), result_used),
1398 .MemberExprClass => return transMemberExpr(rp, scope, @ptrCast(*const clang.MemberExpr, stmt), result_used),
1399 .ArraySubscriptExprClass => return transArrayAccess(rp, scope, @ptrCast(*const clang.ArraySubscriptExpr, stmt), result_used),
1400 .CallExprClass => return transCallExpr(rp, scope, @ptrCast(*const clang.CallExpr, stmt), result_used),
1401 .UnaryExprOrTypeTraitExprClass => return transUnaryExprOrTypeTraitExpr(rp, scope, @ptrCast(*const clang.UnaryExprOrTypeTraitExpr, stmt), result_used),
1402 .UnaryOperatorClass => return transUnaryOperator(rp, scope, @ptrCast(*const clang.UnaryOperator, stmt), result_used),
1403 .CompoundAssignOperatorClass => return transCompoundAssignOperator(rp, scope, @ptrCast(*const clang.CompoundAssignOperator, stmt), result_used),
1051 return transBinaryConditionalOperator(c, scope, @ptrCast(*const clang.BinaryConditionalOperator, stmt), result_used);
1052 },
1053 .SwitchStmtClass => return transSwitch(c, scope, @ptrCast(*const clang.SwitchStmt, stmt)),
1054 .CaseStmtClass, .DefaultStmtClass => {
1055 return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "TODO complex switch", .{});
1056 },
1057 .ConstantExprClass => return transConstantExpr(c, scope, @ptrCast(*const clang.Expr, stmt), result_used),
1058 .PredefinedExprClass => return transPredefinedExpr(c, scope, @ptrCast(*const clang.PredefinedExpr, stmt), result_used),
1059 .CharacterLiteralClass => return transCharLiteral(c, scope, @ptrCast(*const clang.CharacterLiteral, stmt), result_used, .with_as),
1060 .StmtExprClass => return transStmtExpr(c, scope, @ptrCast(*const clang.StmtExpr, stmt), result_used),
1061 .MemberExprClass => return transMemberExpr(c, scope, @ptrCast(*const clang.MemberExpr, stmt), result_used),
1062 .ArraySubscriptExprClass => return transArrayAccess(c, scope, @ptrCast(*const clang.ArraySubscriptExpr, stmt), result_used),
1063 .CallExprClass => return transCallExpr(c, scope, @ptrCast(*const clang.CallExpr, stmt), result_used),
1064 .UnaryExprOrTypeTraitExprClass => return transUnaryExprOrTypeTraitExpr(c, scope, @ptrCast(*const clang.UnaryExprOrTypeTraitExpr, stmt), result_used),
1065 .UnaryOperatorClass => return transUnaryOperator(c, scope, @ptrCast(*const clang.UnaryOperator, stmt), result_used),
1066 .CompoundAssignOperatorClass => return transCompoundAssignOperator(c, scope, @ptrCast(*const clang.CompoundAssignOperator, stmt), result_used),
14041067 .OpaqueValueExprClass => {
14051068 const source_expr = @ptrCast(*const clang.OpaqueValueExpr, stmt).getSourceExpr().?;
1406 const expr = try transExpr(rp, scope, source_expr, .used, lrvalue);
1407 if (expr.tag == .GroupedExpression) return maybeSuppressResult(rp, scope, result_used, expr);
1408 const node = try rp.c.arena.create(ast.Node.GroupedExpression);
1409 node.* = .{
1410 .lparen = try appendToken(rp.c, .LParen, "("),
1411 .expr = expr,
1412 .rparen = try appendToken(rp.c, .RParen, ")"),
1413 };
1414 return maybeSuppressResult(rp, scope, result_used, &node.base);
1069 const expr = try transExpr(c, scope, source_expr, .used);
1070 return maybeSuppressResult(c, scope, result_used, expr);
14151071 },
14161072 else => {
1417 return revertAndWarn(
1418 rp,
1419 error.UnsupportedTranslation,
1420 stmt.getBeginLoc(),
1421 "TODO implement translation of stmt class {s}",
1422 .{@tagName(sc)},
1423 );
1073 return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "TODO implement translation of stmt class {s}", .{@tagName(sc)});
14241074 },
14251075 }
14261076}
14271077
14281078fn transBinaryOperator(
1429 rp: RestorePoint,
1079 c: *Context,
14301080 scope: *Scope,
14311081 stmt: *const clang.BinaryOperator,
14321082 result_used: ResultUsed,
1433) TransError!*ast.Node {
1083) TransError!Node {
14341084 const op = stmt.getOpcode();
14351085 const qt = stmt.getType();
1436 var op_token: ast.TokenIndex = undefined;
1437 var op_id: ast.Node.Tag = undefined;
14381086 switch (op) {
1439 .Assign => return try transCreateNodeAssign(rp, scope, result_used, stmt.getLHS(), stmt.getRHS()),
1087 .Assign => return try transCreateNodeAssign(c, scope, result_used, stmt.getLHS(), stmt.getRHS()),
14401088 .Comma => {
1441 var block_scope = try Scope.Block.init(rp.c, scope, true);
1442 const lparen = try appendToken(rp.c, .LParen, "(");
1089 var block_scope = try Scope.Block.init(c, scope, true);
1090 defer block_scope.deinit();
14431091
1444 const lhs = try transExpr(rp, &block_scope.base, stmt.getLHS(), .unused, .r_value);
1092 const lhs = try transExpr(c, &block_scope.base, stmt.getLHS(), .unused);
14451093 try block_scope.statements.append(lhs);
14461094
1447 const rhs = try transExpr(rp, &block_scope.base, stmt.getRHS(), .used, .r_value);
1448 _ = try appendToken(rp.c, .Semicolon, ";");
1449 const break_node = try transCreateNodeBreak(rp.c, block_scope.label, rhs);
1450 try block_scope.statements.append(&break_node.base);
1451 const block_node = try block_scope.complete(rp.c);
1452 const rparen = try appendToken(rp.c, .RParen, ")");
1453 const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
1454 grouped_expr.* = .{
1455 .lparen = lparen,
1456 .expr = block_node,
1457 .rparen = rparen,
1458 };
1459 return maybeSuppressResult(rp, scope, result_used, &grouped_expr.base);
1095 const rhs = try transExpr(c, &block_scope.base, stmt.getRHS(), .used);
1096 const break_node = try Tag.break_val.create(c.arena, .{
1097 .label = block_scope.label,
1098 .val = rhs,
1099 });
1100 try block_scope.statements.append(break_node);
1101 const block_node = try block_scope.complete(c);
1102 return maybeSuppressResult(c, scope, result_used, block_node);
14601103 },
14611104 .Div => {
14621105 if (cIsSignedInteger(qt)) {
14631106 // signed integer division uses @divTrunc
1464 const div_trunc_node = try rp.c.createBuiltinCall("@divTrunc", 2);
1465 div_trunc_node.params()[0] = try transExpr(rp, scope, stmt.getLHS(), .used, .l_value);
1466 _ = try appendToken(rp.c, .Comma, ",");
1467 const rhs = try transExpr(rp, scope, stmt.getRHS(), .used, .r_value);
1468 div_trunc_node.params()[1] = rhs;
1469 div_trunc_node.rparen_token = try appendToken(rp.c, .RParen, ")");
1470 return maybeSuppressResult(rp, scope, result_used, &div_trunc_node.base);
1107 const lhs = try transExpr(c, scope, stmt.getLHS(), .used);
1108 const rhs = try transExpr(c, scope, stmt.getRHS(), .used);
1109 const div_trunc = try Tag.div_trunc.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
1110 return maybeSuppressResult(c, scope, result_used, div_trunc);
14711111 }
14721112 },
14731113 .Rem => {
14741114 if (cIsSignedInteger(qt)) {
14751115 // signed integer division uses @rem
1476 const rem_node = try rp.c.createBuiltinCall("@rem", 2);
1477 rem_node.params()[0] = try transExpr(rp, scope, stmt.getLHS(), .used, .l_value);
1478 _ = try appendToken(rp.c, .Comma, ",");
1479 const rhs = try transExpr(rp, scope, stmt.getRHS(), .used, .r_value);
1480 rem_node.params()[1] = rhs;
1481 rem_node.rparen_token = try appendToken(rp.c, .RParen, ")");
1482 return maybeSuppressResult(rp, scope, result_used, &rem_node.base);
1116 const lhs = try transExpr(c, scope, stmt.getLHS(), .used);
1117 const rhs = try transExpr(c, scope, stmt.getRHS(), .used);
1118 const rem = try Tag.rem.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
1119 return maybeSuppressResult(c, scope, result_used, rem);
14831120 }
14841121 },
14851122 .Shl => {
1486 const node = try transCreateNodeShiftOp(rp, scope, stmt, .BitShiftLeft, .AngleBracketAngleBracketLeft, "<<");
1487 return maybeSuppressResult(rp, scope, result_used, node);
1123 return transCreateNodeShiftOp(c, scope, stmt, .shl, result_used);
14881124 },
14891125 .Shr => {
1490 const node = try transCreateNodeShiftOp(rp, scope, stmt, .BitShiftRight, .AngleBracketAngleBracketRight, ">>");
1491 return maybeSuppressResult(rp, scope, result_used, node);
1126 return transCreateNodeShiftOp(c, scope, stmt, .shr, result_used);
14921127 },
14931128 .LAnd => {
1494 const node = try transCreateNodeBoolInfixOp(rp, scope, stmt, .BoolAnd, result_used, true);
1495 return maybeSuppressResult(rp, scope, result_used, node);
1129 return transCreateNodeBoolInfixOp(c, scope, stmt, .@"and", result_used);
14961130 },
14971131 .LOr => {
1498 const node = try transCreateNodeBoolInfixOp(rp, scope, stmt, .BoolOr, result_used, true);
1499 return maybeSuppressResult(rp, scope, result_used, node);
1132 return transCreateNodeBoolInfixOp(c, scope, stmt, .@"or", result_used);
15001133 },
15011134 else => {},
15021135 }
1503 const lhs_node = try transExpr(rp, scope, stmt.getLHS(), .used, .l_value);
1136 var op_id: Tag = undefined;
15041137 switch (op) {
15051138 .Add => {
15061139 if (cIsUnsignedInteger(qt)) {
1507 op_token = try appendToken(rp.c, .PlusPercent, "+%");
1508 op_id = .AddWrap;
1140 op_id = .add_wrap;
15091141 } else {
1510 op_token = try appendToken(rp.c, .Plus, "+");
1511 op_id = .Add;
1142 op_id = .add;
15121143 }
15131144 },
15141145 .Sub => {
15151146 if (cIsUnsignedInteger(qt)) {
1516 op_token = try appendToken(rp.c, .MinusPercent, "-%");
1517 op_id = .SubWrap;
1147 op_id = .sub_wrap;
15181148 } else {
1519 op_token = try appendToken(rp.c, .Minus, "-");
1520 op_id = .Sub;
1149 op_id = .sub;
15211150 }
15221151 },
15231152 .Mul => {
15241153 if (cIsUnsignedInteger(qt)) {
1525 op_token = try appendToken(rp.c, .AsteriskPercent, "*%");
1526 op_id = .MulWrap;
1154 op_id = .mul_wrap;
15271155 } else {
1528 op_token = try appendToken(rp.c, .Asterisk, "*");
1529 op_id = .Mul;
1156 op_id = .mul;
15301157 }
15311158 },
15321159 .Div => {
15331160 // unsigned/float division uses the operator
1534 op_id = .Div;
1535 op_token = try appendToken(rp.c, .Slash, "/");
1161 op_id = .div;
15361162 },
15371163 .Rem => {
15381164 // unsigned/float division uses the operator
1539 op_id = .Mod;
1540 op_token = try appendToken(rp.c, .Percent, "%");
1165 op_id = .mod;
15411166 },
15421167 .LT => {
1543 op_id = .LessThan;
1544 op_token = try appendToken(rp.c, .AngleBracketLeft, "<");
1168 op_id = .less_than;
15451169 },
15461170 .GT => {
1547 op_id = .GreaterThan;
1548 op_token = try appendToken(rp.c, .AngleBracketRight, ">");
1171 op_id = .greater_than;
15491172 },
15501173 .LE => {
1551 op_id = .LessOrEqual;
1552 op_token = try appendToken(rp.c, .AngleBracketLeftEqual, "<=");
1174 op_id = .less_than_equal;
15531175 },
15541176 .GE => {
1555 op_id = .GreaterOrEqual;
1556 op_token = try appendToken(rp.c, .AngleBracketRightEqual, ">=");
1177 op_id = .greater_than_equal;
15571178 },
15581179 .EQ => {
1559 op_id = .EqualEqual;
1560 op_token = try appendToken(rp.c, .EqualEqual, "==");
1180 op_id = .equal;
15611181 },
15621182 .NE => {
1563 op_id = .BangEqual;
1564 op_token = try appendToken(rp.c, .BangEqual, "!=");
1183 op_id = .not_equal;
15651184 },
15661185 .And => {
1567 op_id = .BitAnd;
1568 op_token = try appendToken(rp.c, .Ampersand, "&");
1186 op_id = .bit_and;
15691187 },
15701188 .Xor => {
1571 op_id = .BitXor;
1572 op_token = try appendToken(rp.c, .Caret, "^");
1189 op_id = .bit_xor;
15731190 },
15741191 .Or => {
1575 op_id = .BitOr;
1576 op_token = try appendToken(rp.c, .Pipe, "|");
1192 op_id = .bit_or;
15771193 },
15781194 else => unreachable,
15791195 }
15801196
1581 const rhs_node = try transExpr(rp, scope, stmt.getRHS(), .used, .r_value);
1197 const lhs_uncasted = try transExpr(c, scope, stmt.getLHS(), .used);
1198 const rhs_uncasted = try transExpr(c, scope, stmt.getRHS(), .used);
15821199
1583 const lhs = if (isBoolRes(lhs_node)) init: {
1584 const cast_node = try rp.c.createBuiltinCall("@boolToInt", 1);
1585 cast_node.params()[0] = lhs_node;
1586 cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
1587 break :init &cast_node.base;
1588 } else lhs_node;
1200 const lhs = if (isBoolRes(lhs_uncasted))
1201 try Tag.bool_to_int.create(c.arena, lhs_uncasted)
1202 else
1203 lhs_uncasted;
15891204
1590 const rhs = if (isBoolRes(rhs_node)) init: {
1591 const cast_node = try rp.c.createBuiltinCall("@boolToInt", 1);
1592 cast_node.params()[0] = rhs_node;
1593 cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
1594 break :init &cast_node.base;
1595 } else rhs_node;
1205 const rhs = if (isBoolRes(rhs_uncasted))
1206 try Tag.bool_to_int.create(c.arena, rhs_uncasted)
1207 else
1208 rhs_uncasted;
15961209
1597 return transCreateNodeInfixOp(rp, scope, lhs, op_id, op_token, rhs, result_used, true);
1210 return transCreateNodeInfixOp(c, scope, op_id, lhs, rhs, result_used);
15981211}
15991212
16001213fn transCompoundStmtInline(
1601 rp: RestorePoint,
1602 parent_scope: *Scope,
1214 c: *Context,
16031215 stmt: *const clang.CompoundStmt,
16041216 block: *Scope.Block,
16051217) TransError!void {
16061218 var it = stmt.body_begin();
16071219 const end_it = stmt.body_end();
16081220 while (it != end_it) : (it += 1) {
1609 const result = try transStmt(rp, parent_scope, it[0], .unused, .r_value);
1610 try block.statements.append(result);
1221 const result = try transStmt(c, &block.base, it[0], .unused);
1222 switch (result.tag()) {
1223 .declaration, .empty_block => {},
1224 else => try block.statements.append(result),
1225 }
16111226 }
16121227}
16131228
1614fn transCompoundStmt(rp: RestorePoint, scope: *Scope, stmt: *const clang.CompoundStmt) TransError!*ast.Node {
1615 var block_scope = try Scope.Block.init(rp.c, scope, false);
1229fn transCompoundStmt(c: *Context, scope: *Scope, stmt: *const clang.CompoundStmt) TransError!Node {
1230 var block_scope = try Scope.Block.init(c, scope, false);
16161231 defer block_scope.deinit();
1617 try transCompoundStmtInline(rp, &block_scope.base, stmt, &block_scope);
1618 return try block_scope.complete(rp.c);
1232 try transCompoundStmtInline(c, stmt, &block_scope);
1233 return try block_scope.complete(c);
16191234}
16201235
16211236fn transCStyleCastExprClass(
1622 rp: RestorePoint,
1237 c: *Context,
16231238 scope: *Scope,
16241239 stmt: *const clang.CStyleCastExpr,
16251240 result_used: ResultUsed,
1626 lrvalue: LRValue,
1627) TransError!*ast.Node {
1241) TransError!Node {
16281242 const sub_expr = stmt.getSubExpr();
16291243 const cast_node = (try transCCast(
1630 rp,
1244 c,
16311245 scope,
16321246 stmt.getBeginLoc(),
16331247 stmt.getType(),
16341248 sub_expr.getType(),
1635 try transExpr(rp, scope, sub_expr, .used, lrvalue),
1249 try transExpr(c, scope, sub_expr, .used),
16361250 ));
1637 return maybeSuppressResult(rp, scope, result_used, cast_node);
1251 return maybeSuppressResult(c, scope, result_used, cast_node);
16381252}
16391253
16401254fn transDeclStmtOne(
1641 rp: RestorePoint,
1255 c: *Context,
16421256 scope: *Scope,
16431257 decl: *const clang.Decl,
16441258 block_scope: *Scope.Block,
1645) TransError!*ast.Node {
1646 const c = rp.c;
1647
1259) TransError!void {
16481260 switch (decl.getKind()) {
16491261 .Var => {
16501262 const var_decl = @ptrCast(*const clang.VarDecl, decl);
......@@ -1658,62 +1270,51 @@ fn transDeclStmtOne(
16581270 .Extern, .Static => {
16591271 // This is actually a global variable, put it in the global scope and reference it.
16601272 // `_ = mangled_name;`
1661 try visitVarDecl(rp.c, var_decl, mangled_name);
1662 return try maybeSuppressResult(rp, scope, .unused, try transCreateNodeIdentifier(rp.c, mangled_name));
1273 return visitVarDecl(c, var_decl, mangled_name);
16631274 },
16641275 else => {},
16651276 }
16661277
1667 const mut_tok = if (qual_type.isConstQualified())
1668 try appendToken(c, .Keyword_const, "const")
1669 else
1670 try appendToken(c, .Keyword_var, "var");
1671 const name_tok = try appendIdentifier(c, mangled_name);
1278 const is_const = qual_type.isConstQualified();
16721279
1673 _ = try appendToken(c, .Colon, ":");
16741280 const loc = decl.getLocation();
1675 const type_node = try transQualTypeMaybeInitialized(rp, qual_type, decl_init, loc);
1281 const type_node = try transQualTypeMaybeInitialized(c, scope, qual_type, decl_init, loc);
16761282
1677 const eq_token = try appendToken(c, .Equal, "=");
16781283 var init_node = if (decl_init) |expr|
16791284 if (expr.getStmtClass() == .StringLiteralClass)
1680 try transStringLiteralAsArray(rp, scope, @ptrCast(*const clang.StringLiteral, expr), try zigArraySize(rp.c, type_node))
1285 try transStringLiteralAsArray(c, scope, @ptrCast(*const clang.StringLiteral, expr), try zigArraySize(c, type_node))
16811286 else
1682 try transExprCoercing(rp, scope, expr, .used, .r_value)
1287 try transExprCoercing(c, scope, expr, .used)
16831288 else
1684 try transCreateNodeUndefinedLiteral(c);
1289 Tag.undefined_literal.init();
16851290 if (!qualTypeIsBoolean(qual_type) and isBoolRes(init_node)) {
1686 const builtin_node = try rp.c.createBuiltinCall("@boolToInt", 1);
1687 builtin_node.params()[0] = init_node;
1688 builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
1689 init_node = &builtin_node.base;
1291 init_node = try Tag.bool_to_int.create(c.arena, init_node);
16901292 }
1691 const semicolon_token = try appendToken(c, .Semicolon, ";");
1692 const node = try ast.Node.VarDecl.create(c.arena, .{
1693 .name_token = name_tok,
1694 .mut_token = mut_tok,
1695 .semicolon_token = semicolon_token,
1696 }, .{
1697 .eq_token = eq_token,
1698 .type_node = type_node,
1699 .init_node = init_node,
1293 const node = try Tag.var_decl.create(c.arena, .{
1294 .is_pub = false,
1295 .is_const = is_const,
1296 .is_extern = false,
1297 .is_export = false,
1298 .is_threadlocal = false,
1299 .linksection_string = null,
1300 .alignment = null,
1301 .name = mangled_name,
1302 .type = type_node,
1303 .init = init_node,
17001304 });
1701 return &node.base;
1305 try block_scope.statements.append(node);
17021306 },
17031307 .Typedef => {
1704 const typedef_decl = @ptrCast(*const clang.TypedefNameDecl, decl);
1705 const name = try c.str(@ptrCast(*const clang.NamedDecl, typedef_decl).getName_bytes_begin());
1706
1707 const underlying_qual = typedef_decl.getUnderlyingType();
1708 const underlying_type = underlying_qual.getTypePtr();
1709
1710 const mangled_name = try block_scope.makeMangledName(c, name);
1711 const node = (try transCreateNodeTypedef(rp, typedef_decl, false, mangled_name)) orelse
1712 return error.UnsupportedTranslation;
1713 return node;
1308 try transTypeDef(c, scope, @ptrCast(*const clang.TypedefNameDecl, decl));
1309 },
1310 .Record => {
1311 try transRecordDecl(c, scope, @ptrCast(*const clang.RecordDecl, decl));
17141312 },
1715 else => |kind| return revertAndWarn(
1716 rp,
1313 .Enum => {
1314 try transEnumDecl(c, scope, @ptrCast(*const clang.EnumDecl, decl));
1315 },
1316 else => |kind| return fail(
1317 c,
17171318 error.UnsupportedTranslation,
17181319 decl.getLocation(),
17191320 "TODO implement translation of DeclStmt kind {s}",
......@@ -1722,96 +1323,86 @@ fn transDeclStmtOne(
17221323 }
17231324}
17241325
1725fn transDeclStmt(rp: RestorePoint, scope: *Scope, stmt: *const clang.DeclStmt) TransError!*ast.Node {
1726 const block_scope = scope.findBlockScope(rp.c) catch unreachable;
1326fn transDeclStmt(c: *Context, scope: *Scope, stmt: *const clang.DeclStmt) TransError!Node {
1327 const block_scope = try scope.findBlockScope(c);
17271328
17281329 var it = stmt.decl_begin();
17291330 const end_it = stmt.decl_end();
1730 assert(it != end_it);
1731 while (true) : (it += 1) {
1732 const node = try transDeclStmtOne(rp, scope, it[0], block_scope);
1733
1734 if (it + 1 == end_it) {
1735 return node;
1736 } else {
1737 try block_scope.statements.append(node);
1738 }
1331 while (it != end_it) : (it += 1) {
1332 try transDeclStmtOne(c, scope, it[0], block_scope);
17391333 }
1740 unreachable;
1334 return Tag.declaration.init();
17411335}
17421336
17431337fn transDeclRefExpr(
1744 rp: RestorePoint,
1338 c: *Context,
17451339 scope: *Scope,
17461340 expr: *const clang.DeclRefExpr,
1747 lrvalue: LRValue,
1748) TransError!*ast.Node {
1341) TransError!Node {
17491342 const value_decl = expr.getDecl();
1750 const name = try rp.c.str(@ptrCast(*const clang.NamedDecl, value_decl).getName_bytes_begin());
1343 const name = try c.str(@ptrCast(*const clang.NamedDecl, value_decl).getName_bytes_begin());
17511344 const mangled_name = scope.getAlias(name);
1752 return transCreateNodeIdentifier(rp.c, mangled_name);
1345 return Tag.identifier.create(c.arena, mangled_name);
17531346}
17541347
17551348fn transImplicitCastExpr(
1756 rp: RestorePoint,
1349 c: *Context,
17571350 scope: *Scope,
17581351 expr: *const clang.ImplicitCastExpr,
17591352 result_used: ResultUsed,
1760) TransError!*ast.Node {
1761 const c = rp.c;
1353) TransError!Node {
17621354 const sub_expr = expr.getSubExpr();
17631355 const dest_type = getExprQualType(c, @ptrCast(*const clang.Expr, expr));
17641356 const src_type = getExprQualType(c, sub_expr);
17651357 switch (expr.getCastKind()) {
17661358 .BitCast, .FloatingCast, .FloatingToIntegral, .IntegralToFloating, .IntegralCast, .PointerToIntegral, .IntegralToPointer => {
1767 const sub_expr_node = try transExpr(rp, scope, sub_expr, .used, .r_value);
1768 return try transCCast(rp, scope, expr.getBeginLoc(), dest_type, src_type, sub_expr_node);
1359 const sub_expr_node = try transExpr(c, scope, sub_expr, .used);
1360 const casted = try transCCast(c, scope, expr.getBeginLoc(), dest_type, src_type, sub_expr_node);
1361 return maybeSuppressResult(c, scope, result_used, casted);
17691362 },
17701363 .LValueToRValue, .NoOp, .FunctionToPointerDecay => {
1771 const sub_expr_node = try transExpr(rp, scope, sub_expr, .used, .r_value);
1772 return maybeSuppressResult(rp, scope, result_used, sub_expr_node);
1364 const sub_expr_node = try transExpr(c, scope, sub_expr, .used);
1365 return maybeSuppressResult(c, scope, result_used, sub_expr_node);
17731366 },
17741367 .ArrayToPointerDecay => {
17751368 if (exprIsNarrowStringLiteral(sub_expr)) {
1776 const sub_expr_node = try transExpr(rp, scope, sub_expr, .used, .r_value);
1777 return maybeSuppressResult(rp, scope, result_used, sub_expr_node);
1369 const sub_expr_node = try transExpr(c, scope, sub_expr, .used);
1370 return maybeSuppressResult(c, scope, result_used, sub_expr_node);
17781371 }
17791372
1780 const prefix_op = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
1781 prefix_op.rhs = try transExpr(rp, scope, sub_expr, .used, .r_value);
1782
1783 return maybeSuppressResult(rp, scope, result_used, &prefix_op.base);
1373 const addr = try Tag.address_of.create(c.arena, try transExpr(c, scope, sub_expr, .used));
1374 return maybeSuppressResult(c, scope, result_used, addr);
17841375 },
17851376 .NullToPointer => {
1786 return try transCreateNodeNullLiteral(rp.c);
1377 return Tag.null_literal.init();
17871378 },
17881379 .PointerToBoolean => {
17891380 // @ptrToInt(val) != 0
1790 const ptr_to_int = try rp.c.createBuiltinCall("@ptrToInt", 1);
1791 ptr_to_int.params()[0] = try transExpr(rp, scope, sub_expr, .used, .r_value);
1792 ptr_to_int.rparen_token = try appendToken(rp.c, .RParen, ")");
1381 const ptr_to_int = try Tag.ptr_to_int.create(c.arena, try transExpr(c, scope, sub_expr, .used));
17931382
1794 const op_token = try appendToken(rp.c, .BangEqual, "!=");
1795 const rhs_node = try transCreateNodeInt(rp.c, 0);
1796 return transCreateNodeInfixOp(rp, scope, &ptr_to_int.base, .BangEqual, op_token, rhs_node, result_used, false);
1383 const ne = try Tag.not_equal.create(c.arena, .{ .lhs = ptr_to_int, .rhs = Tag.zero_literal.init() });
1384 return maybeSuppressResult(c, scope, result_used, ne);
17971385 },
17981386 .IntegralToBoolean => {
1799 const sub_expr_node = try transExpr(rp, scope, sub_expr, .used, .r_value);
1387 const sub_expr_node = try transExpr(c, scope, sub_expr, .used);
18001388
18011389 // The expression is already a boolean one, return it as-is
18021390 if (isBoolRes(sub_expr_node))
1803 return sub_expr_node;
1391 return maybeSuppressResult(c, scope, result_used, sub_expr_node);
18041392
18051393 // val != 0
1806 const op_token = try appendToken(rp.c, .BangEqual, "!=");
1807 const rhs_node = try transCreateNodeInt(rp.c, 0);
1808 return transCreateNodeInfixOp(rp, scope, sub_expr_node, .BangEqual, op_token, rhs_node, result_used, false);
1394 const ne = try Tag.not_equal.create(c.arena, .{ .lhs = sub_expr_node, .rhs = Tag.zero_literal.init() });
1395 return maybeSuppressResult(c, scope, result_used, ne);
18091396 },
18101397 .BuiltinFnToFnPtr => {
1811 return transExpr(rp, scope, sub_expr, .used, .r_value);
1398 return transExpr(c, scope, sub_expr, result_used);
1399 },
1400 .ToVoid => {
1401 // Should only appear in the rhs and lhs of a ConditionalOperator
1402 return transExpr(c, scope, sub_expr, .unused);
18121403 },
1813 else => |kind| return revertAndWarn(
1814 rp,
1404 else => |kind| return fail(
1405 c,
18151406 error.UnsupportedTranslation,
18161407 @ptrCast(*const clang.Stmt, expr).getBeginLoc(),
18171408 "TODO implement translation of CastKind {s}",
......@@ -1821,52 +1412,28 @@ fn transImplicitCastExpr(
18211412}
18221413
18231414fn transBoolExpr(
1824 rp: RestorePoint,
1415 c: *Context,
18251416 scope: *Scope,
18261417 expr: *const clang.Expr,
18271418 used: ResultUsed,
1828 lrvalue: LRValue,
1829 grouped: bool,
1830) TransError!*ast.Node {
1419) TransError!Node {
18311420 if (@ptrCast(*const clang.Stmt, expr).getStmtClass() == .IntegerLiteralClass) {
18321421 var is_zero: bool = undefined;
1833 if (!(@ptrCast(*const clang.IntegerLiteral, expr).isZero(&is_zero, rp.c.clang_context))) {
1834 return revertAndWarn(rp, error.UnsupportedTranslation, expr.getBeginLoc(), "invalid integer literal", .{});
1422 if (!(@ptrCast(*const clang.IntegerLiteral, expr).isZero(&is_zero, c.clang_context))) {
1423 return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "invalid integer literal", .{});
18351424 }
1836 return try transCreateNodeBoolLiteral(rp.c, !is_zero);
1425 return Node{ .tag_if_small_enough = @enumToInt(([2]Tag{ .true_literal, .false_literal })[@boolToInt(is_zero)]) };
18371426 }
18381427
1839 const lparen = if (grouped)
1840 try appendToken(rp.c, .LParen, "(")
1841 else
1842 undefined;
1843 var res = try transExpr(rp, scope, expr, used, lrvalue);
1844
1428 var res = try transExpr(c, scope, expr, used);
18451429 if (isBoolRes(res)) {
1846 if (!grouped and res.tag == .GroupedExpression) {
1847 const group = @fieldParentPtr(ast.Node.GroupedExpression, "base", res);
1848 res = group.expr;
1849 // get zig fmt to work properly
1850 tokenSlice(rp.c, group.lparen)[0] = ')';
1851 }
1852 return res;
1430 return maybeSuppressResult(c, scope, used, res);
18531431 }
18541432
1855 const ty = getExprQualType(rp.c, expr).getTypePtr();
1856 const node = try finishBoolExpr(rp, scope, expr.getBeginLoc(), ty, res, used);
1433 const ty = getExprQualType(c, expr).getTypePtr();
1434 const node = try finishBoolExpr(c, scope, expr.getBeginLoc(), ty, res, used);
18571435
1858 if (grouped) {
1859 const rparen = try appendToken(rp.c, .RParen, ")");
1860 const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
1861 grouped_expr.* = .{
1862 .lparen = lparen,
1863 .expr = node,
1864 .rparen = rparen,
1865 };
1866 return maybeSuppressResult(rp, scope, used, &grouped_expr.base);
1867 } else {
1868 return maybeSuppressResult(rp, scope, used, node);
1869 }
1436 return maybeSuppressResult(c, scope, used, node);
18701437}
18711438
18721439fn exprIsBooleanType(expr: *const clang.Expr) bool {
......@@ -1892,34 +1459,32 @@ fn exprIsNarrowStringLiteral(expr: *const clang.Expr) bool {
18921459 }
18931460}
18941461
1895fn isBoolRes(res: *ast.Node) bool {
1896 switch (res.tag) {
1897 .BoolOr,
1898 .BoolAnd,
1899 .EqualEqual,
1900 .BangEqual,
1901 .LessThan,
1902 .GreaterThan,
1903 .LessOrEqual,
1904 .GreaterOrEqual,
1905 .BoolNot,
1906 .BoolLiteral,
1462fn isBoolRes(res: Node) bool {
1463 switch (res.tag()) {
1464 .@"or",
1465 .@"and",
1466 .equal,
1467 .not_equal,
1468 .less_than,
1469 .less_than_equal,
1470 .greater_than,
1471 .greater_than_equal,
1472 .not,
1473 .false_literal,
1474 .true_literal,
19071475 => return true,
1908
1909 .GroupedExpression => return isBoolRes(@fieldParentPtr(ast.Node.GroupedExpression, "base", res).expr),
1910
19111476 else => return false,
19121477 }
19131478}
19141479
19151480fn finishBoolExpr(
1916 rp: RestorePoint,
1481 c: *Context,
19171482 scope: *Scope,
19181483 loc: clang.SourceLocation,
19191484 ty: *const clang.Type,
1920 node: *ast.Node,
1485 node: Node,
19211486 used: ResultUsed,
1922) TransError!*ast.Node {
1487) TransError!Node {
19231488 switch (ty.getTypeClass()) {
19241489 .Builtin => {
19251490 const builtin_ty = @ptrCast(*const clang.BuiltinType, ty);
......@@ -1951,42 +1516,38 @@ fn finishBoolExpr(
19511516 .WChar_S,
19521517 .Float16,
19531518 => {
1954 const op_token = try appendToken(rp.c, .BangEqual, "!=");
1955 const rhs_node = try transCreateNodeInt(rp.c, 0);
1956 return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, used, false);
1519 // node != 0
1520 return Tag.not_equal.create(c.arena, .{ .lhs = node, .rhs = Tag.zero_literal.init() });
19571521 },
19581522 .NullPtr => {
1959 const op_token = try appendToken(rp.c, .EqualEqual, "==");
1960 const rhs_node = try transCreateNodeNullLiteral(rp.c);
1961 return transCreateNodeInfixOp(rp, scope, node, .EqualEqual, op_token, rhs_node, used, false);
1523 // node == null
1524 return Tag.equal.create(c.arena, .{ .lhs = node, .rhs = Tag.null_literal.init() });
19621525 },
19631526 else => {},
19641527 }
19651528 },
19661529 .Pointer => {
1967 const op_token = try appendToken(rp.c, .BangEqual, "!=");
1968 const rhs_node = try transCreateNodeNullLiteral(rp.c);
1969 return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, used, false);
1530 // node != null
1531 return Tag.not_equal.create(c.arena, .{ .lhs = node, .rhs = Tag.null_literal.init() });
19701532 },
19711533 .Typedef => {
19721534 const typedef_ty = @ptrCast(*const clang.TypedefType, ty);
19731535 const typedef_decl = typedef_ty.getDecl();
19741536 const underlying_type = typedef_decl.getUnderlyingType();
1975 return finishBoolExpr(rp, scope, loc, underlying_type.getTypePtr(), node, used);
1537 return finishBoolExpr(c, scope, loc, underlying_type.getTypePtr(), node, used);
19761538 },
19771539 .Enum => {
1978 const op_token = try appendToken(rp.c, .BangEqual, "!=");
1979 const rhs_node = try transCreateNodeInt(rp.c, 0);
1980 return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, used, false);
1540 // node != 0
1541 return Tag.not_equal.create(c.arena, .{ .lhs = node, .rhs = Tag.zero_literal.init() });
19811542 },
19821543 .Elaborated => {
19831544 const elaborated_ty = @ptrCast(*const clang.ElaboratedType, ty);
19841545 const named_type = elaborated_ty.getNamedType();
1985 return finishBoolExpr(rp, scope, loc, named_type.getTypePtr(), node, used);
1546 return finishBoolExpr(c, scope, loc, named_type.getTypePtr(), node, used);
19861547 },
19871548 else => {},
19881549 }
1989 return revertAndWarn(rp, error.UnsupportedType, loc, "unsupported bool expression type", .{});
1550 return fail(c, error.UnsupportedType, loc, "unsupported bool expression type", .{});
19901551}
19911552
19921553const SuppressCast = enum {
......@@ -1994,21 +1555,21 @@ const SuppressCast = enum {
19941555 no_as,
19951556};
19961557fn transIntegerLiteral(
1997 rp: RestorePoint,
1558 c: *Context,
19981559 scope: *Scope,
19991560 expr: *const clang.IntegerLiteral,
20001561 result_used: ResultUsed,
20011562 suppress_as: SuppressCast,
2002) TransError!*ast.Node {
1563) TransError!Node {
20031564 var eval_result: clang.ExprEvalResult = undefined;
2004 if (!expr.EvaluateAsInt(&eval_result, rp.c.clang_context)) {
1565 if (!expr.EvaluateAsInt(&eval_result, c.clang_context)) {
20051566 const loc = expr.getBeginLoc();
2006 return revertAndWarn(rp, error.UnsupportedTranslation, loc, "invalid integer literal", .{});
1567 return fail(c, error.UnsupportedTranslation, loc, "invalid integer literal", .{});
20071568 }
20081569
20091570 if (suppress_as == .no_as) {
2010 const int_lit_node = try transCreateNodeAPInt(rp.c, eval_result.Val.getInt());
2011 return maybeSuppressResult(rp, scope, result_used, int_lit_node);
1571 const int_lit_node = try transCreateNodeAPInt(c, eval_result.Val.getInt());
1572 return maybeSuppressResult(c, scope, result_used, int_lit_node);
20121573 }
20131574
20141575 // Integer literals in C have types, and this can matter for several reasons.
......@@ -2023,115 +1584,61 @@ fn transIntegerLiteral(
20231584
20241585 // @as(T, x)
20251586 const expr_base = @ptrCast(*const clang.Expr, expr);
2026 const as_node = try rp.c.createBuiltinCall("@as", 2);
2027 const ty_node = try transQualType(rp, expr_base.getType(), expr_base.getBeginLoc());
2028 as_node.params()[0] = ty_node;
2029 _ = try appendToken(rp.c, .Comma, ",");
2030 as_node.params()[1] = try transCreateNodeAPInt(rp.c, eval_result.Val.getInt());
2031
2032 as_node.rparen_token = try appendToken(rp.c, .RParen, ")");
2033 return maybeSuppressResult(rp, scope, result_used, &as_node.base);
2034}
2035
2036/// In C if a function has return type `int` and the return value is a boolean
2037/// expression, there is no implicit cast. So the translated Zig will need to
2038/// call @boolToInt
2039fn zigShouldCastBooleanReturnToInt(node: ?*ast.Node, qt: ?clang.QualType) bool {
2040 if (node == null or qt == null) return false;
2041 return isBoolRes(node.?) and cIsNativeInt(qt.?);
1587 const ty_node = try transQualType(c, scope, expr_base.getType(), expr_base.getBeginLoc());
1588 const rhs = try transCreateNodeAPInt(c, eval_result.Val.getInt());
1589 const as = try Tag.as.create(c.arena, .{ .lhs = ty_node, .rhs = rhs });
1590 return maybeSuppressResult(c, scope, result_used, as);
20421591}
20431592
20441593fn transReturnStmt(
2045 rp: RestorePoint,
1594 c: *Context,
20461595 scope: *Scope,
20471596 expr: *const clang.ReturnStmt,
2048) TransError!*ast.Node {
2049 const return_kw = try appendToken(rp.c, .Keyword_return, "return");
2050 var rhs: ?*ast.Node = if (expr.getRetValue()) |val_expr|
2051 try transExprCoercing(rp, scope, val_expr, .used, .r_value)
2052 else
2053 null;
2054 const return_qt = scope.findBlockReturnType(rp.c);
2055 if (zigShouldCastBooleanReturnToInt(rhs, return_qt)) {
2056 const bool_to_int_node = try rp.c.createBuiltinCall("@boolToInt", 1);
2057 bool_to_int_node.params()[0] = rhs.?;
2058 bool_to_int_node.rparen_token = try appendToken(rp.c, .RParen, ")");
2059
2060 rhs = &bool_to_int_node.base;
2061 }
2062 const return_expr = try ast.Node.ControlFlowExpression.create(rp.c.arena, .{
2063 .ltoken = return_kw,
2064 .tag = .Return,
2065 }, .{
2066 .rhs = rhs,
2067 });
2068 _ = try appendToken(rp.c, .Semicolon, ";");
2069 return &return_expr.base;
1597) TransError!Node {
1598 const val_expr = expr.getRetValue() orelse
1599 return Tag.return_void.init();
1600
1601 var rhs = try transExprCoercing(c, scope, val_expr, .used);
1602 const return_qt = scope.findBlockReturnType(c);
1603 if (isBoolRes(rhs) and !qualTypeIsBoolean(return_qt)) {
1604 rhs = try Tag.bool_to_int.create(c.arena, rhs);
1605 }
1606 return Tag.@"return".create(c.arena, rhs);
20701607}
20711608
20721609fn transStringLiteral(
2073 rp: RestorePoint,
1610 c: *Context,
20741611 scope: *Scope,
20751612 stmt: *const clang.StringLiteral,
20761613 result_used: ResultUsed,
2077) TransError!*ast.Node {
1614) TransError!Node {
20781615 const kind = stmt.getKind();
20791616 switch (kind) {
20801617 .Ascii, .UTF8 => {
20811618 var len: usize = undefined;
20821619 const bytes_ptr = stmt.getString_bytes_begin_size(&len);
2083 const str = bytes_ptr[0..len];
20841620
2085 const token = try appendTokenFmt(rp.c, .StringLiteral, "\"{}\"", .{std.zig.fmtEscapes(str)});
2086 const node = try rp.c.arena.create(ast.Node.OneToken);
2087 node.* = .{
2088 .base = .{ .tag = .StringLiteral },
2089 .token = token,
2090 };
2091 return maybeSuppressResult(rp, scope, result_used, &node.base);
1621 const str = try std.fmt.allocPrint(c.arena, "\"{}\"", .{std.zig.fmtEscapes(bytes_ptr[0..len])});
1622 const node = try Tag.string_literal.create(c.arena, str);
1623 return maybeSuppressResult(c, scope, result_used, node);
20921624 },
20931625 .UTF16, .UTF32, .Wide => {
2094 const node = try transWideStringLiteral(rp, scope, stmt);
2095 return maybeSuppressResult(rp, scope, result_used, node);
1626 const str_type = @tagName(stmt.getKind());
1627 const name = try std.fmt.allocPrint(c.arena, "zig.{s}_string_{d}", .{ str_type, c.getMangle() });
1628 const lit_array = try transStringLiteralAsArray(c, scope, stmt, stmt.getLength() + 1);
1629
1630 const decl = try Tag.var_simple.create(c.arena, .{ .name = name, .init = lit_array });
1631 try scope.appendNode(decl);
1632 const node = try Tag.identifier.create(c.arena, name);
1633 return maybeSuppressResult(c, scope, result_used, node);
20961634 },
20971635 }
20981636}
20991637
2100/// Translates a wide string literal as a global "anonymous" array of the relevant-sized
2101/// integer type + null terminator, and returns an identifier node for it
2102fn transWideStringLiteral(rp: RestorePoint, scope: *Scope, stmt: *const clang.StringLiteral) TransError!*ast.Node {
2103 const str_type = @tagName(stmt.getKind());
2104 const mangle = rp.c.getMangle();
2105 const name = try std.fmt.allocPrint(rp.c.arena, "zig.{s}_string_{d}", .{ str_type, mangle });
2106
2107 const const_tok = try appendToken(rp.c, .Keyword_const, "const");
2108 const name_tok = try appendIdentifier(rp.c, name);
2109 const eq_tok = try appendToken(rp.c, .Equal, "=");
2110 var semi_tok: ast.TokenIndex = undefined;
2111
2112 const lit_array = try transStringLiteralAsArray(rp, scope, stmt, stmt.getLength() + 1);
2113
2114 semi_tok = try appendToken(rp.c, .Semicolon, ";");
2115 const var_decl_node = try ast.Node.VarDecl.create(rp.c.arena, .{
2116 .name_token = name_tok,
2117 .mut_token = const_tok,
2118 .semicolon_token = semi_tok,
2119 }, .{
2120 .visib_token = null,
2121 .eq_token = eq_tok,
2122 .init_node = lit_array,
2123 });
2124 try addTopLevelDecl(rp.c, name, &var_decl_node.base);
2125 return transCreateNodeIdentifier(rp.c, name);
2126}
2127
21281638/// Parse the size of an array back out from an ast Node.
2129fn zigArraySize(c: *Context, node: *ast.Node) TransError!usize {
2130 if (node.castTag(.ArrayType)) |array| {
2131 if (array.len_expr.castTag(.IntegerLiteral)) |int_lit| {
2132 const tok = tokenSlice(c, int_lit.token);
2133 return std.fmt.parseUnsigned(usize, tok, 10) catch error.UnsupportedTranslation;
2134 }
1639fn zigArraySize(c: *Context, node: Node) TransError!usize {
1640 if (node.castTag(.array_type)) |array| {
1641 return array.data.len;
21351642 }
21361643 return error.UnsupportedTranslation;
21371644}
......@@ -2142,11 +1649,11 @@ fn zigArraySize(c: *Context, node: *ast.Node) TransError!usize {
21421649/// than the array, truncate the string. If the array is larger than the
21431650/// string literal, pad the array with 0's
21441651fn transStringLiteralAsArray(
2145 rp: RestorePoint,
1652 c: *Context,
21461653 scope: *Scope,
21471654 stmt: *const clang.StringLiteral,
21481655 array_size: usize,
2149) TransError!*ast.Node {
1656) TransError!Node {
21501657 if (array_size == 0) return error.UnsupportedType;
21511658
21521659 const str_length = stmt.getLength();
......@@ -2155,40 +1662,25 @@ fn transStringLiteralAsArray(
21551662 const ty = expr_base.getType().getTypePtr();
21561663 const const_arr_ty = @ptrCast(*const clang.ConstantArrayType, ty);
21571664
2158 const ty_node = try rp.c.arena.create(ast.Node.ArrayType);
2159 const op_token = try appendToken(rp.c, .LBracket, "[");
2160 const len_expr = try transCreateNodeInt(rp.c, array_size);
2161 _ = try appendToken(rp.c, .RBracket, "]");
2162
2163 ty_node.* = .{
2164 .op_token = op_token,
2165 .rhs = try transQualType(rp, const_arr_ty.getElementType(), expr_base.getBeginLoc()),
2166 .len_expr = len_expr,
2167 };
2168 _ = try appendToken(rp.c, .LBrace, "{");
2169 var init_node = try ast.Node.ArrayInitializer.alloc(rp.c.arena, array_size);
2170 init_node.* = .{
2171 .lhs = &ty_node.base,
2172 .rtoken = undefined,
2173 .list_len = array_size,
2174 };
2175 const init_list = init_node.list();
1665 const elem_type = try transQualType(c, scope, const_arr_ty.getElementType(), expr_base.getBeginLoc());
1666 const arr_type = try Tag.array_type.create(c.arena, .{ .len = array_size, .elem_type = elem_type });
1667 const init_list = try c.arena.alloc(Node, array_size);
21761668
21771669 var i: c_uint = 0;
21781670 const kind = stmt.getKind();
21791671 const narrow = kind == .Ascii or kind == .UTF8;
21801672 while (i < str_length and i < array_size) : (i += 1) {
21811673 const code_unit = stmt.getCodeUnit(i);
2182 init_list[i] = try transCreateCharLitNode(rp.c, narrow, code_unit);
2183 _ = try appendToken(rp.c, .Comma, ",");
1674 init_list[i] = try transCreateCharLitNode(c, narrow, code_unit);
21841675 }
21851676 while (i < array_size) : (i += 1) {
2186 init_list[i] = try transCreateNodeInt(rp.c, 0);
2187 _ = try appendToken(rp.c, .Comma, ",");
1677 init_list[i] = try transCreateNodeNumber(c, 0, .int);
21881678 }
2189 init_node.rtoken = try appendToken(rp.c, .RBrace, "}");
21901679
2191 return &init_node.base;
1680 return Tag.array_init.create(c.arena, .{
1681 .cond = arr_type,
1682 .cases = init_list,
1683 });
21921684}
21931685
21941686fn cIsEnum(qt: clang.QualType) bool {
......@@ -2207,199 +1699,164 @@ fn cIntTypeForEnum(enum_qt: clang.QualType) clang.QualType {
22071699}
22081700
22091701fn transCCast(
2210 rp: RestorePoint,
1702 c: *Context,
22111703 scope: *Scope,
22121704 loc: clang.SourceLocation,
22131705 dst_type: clang.QualType,
22141706 src_type: clang.QualType,
2215 expr: *ast.Node,
2216) !*ast.Node {
1707 expr: Node,
1708) !Node {
22171709 if (qualTypeCanon(dst_type).isVoidType()) return expr;
22181710 if (dst_type.eq(src_type)) return expr;
22191711 if (qualTypeIsPtr(dst_type) and qualTypeIsPtr(src_type))
2220 return transCPtrCast(rp, loc, dst_type, src_type, expr);
1712 return transCPtrCast(c, scope, loc, dst_type, src_type, expr);
1713
1714 const dst_node = try transQualType(c, scope, dst_type, loc);
22211715 if (cIsInteger(dst_type) and (cIsInteger(src_type) or cIsEnum(src_type))) {
22221716 // 1. If src_type is an enum, determine the underlying signed int type
22231717 // 2. Extend or truncate without changing signed-ness.
22241718 // 3. Bit-cast to correct signed-ness
22251719 const src_type_is_signed = cIsSignedInteger(src_type) or cIsEnum(src_type);
22261720 const src_int_type = if (cIsInteger(src_type)) src_type else cIntTypeForEnum(src_type);
2227 var src_int_expr = if (cIsInteger(src_type)) expr else try transEnumToInt(rp.c, expr);
2228
2229 // @bitCast(dest_type, intermediate_value)
2230 const cast_node = try rp.c.createBuiltinCall("@bitCast", 2);
2231 cast_node.params()[0] = try transQualType(rp, dst_type, loc);
2232 _ = try appendToken(rp.c, .Comma, ",");
1721 var src_int_expr = if (cIsInteger(src_type)) expr else try Tag.enum_to_int.create(c.arena, expr);
22331722
22341723 if (isBoolRes(src_int_expr)) {
2235 const bool_to_int_node = try rp.c.createBuiltinCall("@boolToInt", 1);
2236 bool_to_int_node.params()[0] = src_int_expr;
2237 bool_to_int_node.rparen_token = try appendToken(rp.c, .RParen, ")");
2238 src_int_expr = &bool_to_int_node.base;
1724 src_int_expr = try Tag.bool_to_int.create(c.arena, src_int_expr);
22391725 }
22401726
22411727 switch (cIntTypeCmp(dst_type, src_int_type)) {
22421728 .lt => {
22431729 // @truncate(SameSignSmallerInt, src_int_expr)
2244 const trunc_node = try rp.c.createBuiltinCall("@truncate", 2);
2245 const ty_node = try transQualTypeIntWidthOf(rp.c, dst_type, src_type_is_signed);
2246 trunc_node.params()[0] = ty_node;
2247 _ = try appendToken(rp.c, .Comma, ",");
2248 trunc_node.params()[1] = src_int_expr;
2249 trunc_node.rparen_token = try appendToken(rp.c, .RParen, ")");
2250
2251 cast_node.params()[1] = &trunc_node.base;
1730 const ty_node = try transQualTypeIntWidthOf(c, dst_type, src_type_is_signed);
1731 src_int_expr = try Tag.truncate.create(c.arena, .{ .lhs = ty_node, .rhs = src_int_expr });
22521732 },
22531733 .gt => {
22541734 // @as(SameSignBiggerInt, src_int_expr)
2255 const as_node = try rp.c.createBuiltinCall("@as", 2);
2256 const ty_node = try transQualTypeIntWidthOf(rp.c, dst_type, src_type_is_signed);
2257 as_node.params()[0] = ty_node;
2258 _ = try appendToken(rp.c, .Comma, ",");
2259 as_node.params()[1] = src_int_expr;
2260 as_node.rparen_token = try appendToken(rp.c, .RParen, ")");
2261
2262 cast_node.params()[1] = &as_node.base;
1735 const ty_node = try transQualTypeIntWidthOf(c, dst_type, src_type_is_signed);
1736 src_int_expr = try Tag.as.create(c.arena, .{ .lhs = ty_node, .rhs = src_int_expr });
22631737 },
22641738 .eq => {
2265 cast_node.params()[1] = src_int_expr;
1739 // src_int_expr = src_int_expr
22661740 },
22671741 }
2268 cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
2269 return &cast_node.base;
1742 // @bitCast(dest_type, intermediate_value)
1743 return Tag.bit_cast.create(c.arena, .{ .lhs = dst_node, .rhs = src_int_expr });
22701744 }
22711745 if (cIsInteger(dst_type) and qualTypeIsPtr(src_type)) {
22721746 // @intCast(dest_type, @ptrToInt(val))
2273 const cast_node = try rp.c.createBuiltinCall("@intCast", 2);
2274 cast_node.params()[0] = try transQualType(rp, dst_type, loc);
2275 _ = try appendToken(rp.c, .Comma, ",");
2276 const builtin_node = try rp.c.createBuiltinCall("@ptrToInt", 1);
2277 builtin_node.params()[0] = expr;
2278 builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
2279 cast_node.params()[1] = &builtin_node.base;
2280 cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
2281 return &cast_node.base;
1747 const ptr_to_int = try Tag.ptr_to_int.create(c.arena, expr);
1748 return Tag.int_cast.create(c.arena, .{ .lhs = dst_node, .rhs = ptr_to_int });
22821749 }
22831750 if (cIsInteger(src_type) and qualTypeIsPtr(dst_type)) {
22841751 // @intToPtr(dest_type, val)
2285 const builtin_node = try rp.c.createBuiltinCall("@intToPtr", 2);
2286 builtin_node.params()[0] = try transQualType(rp, dst_type, loc);
2287 _ = try appendToken(rp.c, .Comma, ",");
2288 builtin_node.params()[1] = expr;
2289 builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
2290 return &builtin_node.base;
1752 return Tag.int_to_ptr.create(c.arena, .{ .lhs = dst_node, .rhs = expr });
22911753 }
22921754 if (cIsFloating(src_type) and cIsFloating(dst_type)) {
2293 const builtin_node = try rp.c.createBuiltinCall("@floatCast", 2);
2294 builtin_node.params()[0] = try transQualType(rp, dst_type, loc);
2295 _ = try appendToken(rp.c, .Comma, ",");
2296 builtin_node.params()[1] = expr;
2297 builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
2298 return &builtin_node.base;
1755 // @floatCast(dest_type, val)
1756 return Tag.float_cast.create(c.arena, .{ .lhs = dst_node, .rhs = expr });
22991757 }
23001758 if (cIsFloating(src_type) and !cIsFloating(dst_type)) {
2301 const builtin_node = try rp.c.createBuiltinCall("@floatToInt", 2);
2302 builtin_node.params()[0] = try transQualType(rp, dst_type, loc);
2303 _ = try appendToken(rp.c, .Comma, ",");
2304 builtin_node.params()[1] = expr;
2305 builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
2306 return &builtin_node.base;
1759 // @floatToInt(dest_type, val)
1760 return Tag.float_to_int.create(c.arena, .{ .lhs = dst_node, .rhs = expr });
23071761 }
23081762 if (!cIsFloating(src_type) and cIsFloating(dst_type)) {
2309 const builtin_node = try rp.c.createBuiltinCall("@intToFloat", 2);
2310 builtin_node.params()[0] = try transQualType(rp, dst_type, loc);
2311 _ = try appendToken(rp.c, .Comma, ",");
2312 builtin_node.params()[1] = expr;
2313 builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
2314 return &builtin_node.base;
1763 // @intToFloat(dest_type, val)
1764 return Tag.int_to_float.create(c.arena, .{ .lhs = dst_node, .rhs = expr });
23151765 }
23161766 if (qualTypeIsBoolean(src_type) and !qualTypeIsBoolean(dst_type)) {
23171767 // @boolToInt returns either a comptime_int or a u1
23181768 // TODO: if dst_type is 1 bit & signed (bitfield) we need @bitCast
23191769 // instead of @as
2320 const builtin_node = try rp.c.createBuiltinCall("@boolToInt", 1);
2321 builtin_node.params()[0] = expr;
2322 builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
2323
2324 const as_node = try rp.c.createBuiltinCall("@as", 2);
2325 as_node.params()[0] = try transQualType(rp, dst_type, loc);
2326 _ = try appendToken(rp.c, .Comma, ",");
2327 as_node.params()[1] = &builtin_node.base;
2328 as_node.rparen_token = try appendToken(rp.c, .RParen, ")");
2329
2330 return &as_node.base;
1770 const bool_to_int = try Tag.bool_to_int.create(c.arena, expr);
1771 return Tag.as.create(c.arena, .{ .lhs = dst_node, .rhs = bool_to_int });
23311772 }
23321773 if (cIsEnum(dst_type)) {
2333 const builtin_node = try rp.c.createBuiltinCall("@intToEnum", 2);
2334 builtin_node.params()[0] = try transQualType(rp, dst_type, loc);
2335 _ = try appendToken(rp.c, .Comma, ",");
2336 builtin_node.params()[1] = expr;
2337 builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
2338 return &builtin_node.base;
1774 // @intToEnum(dest_type, val)
1775 return Tag.int_to_enum.create(c.arena, .{ .lhs = dst_node, .rhs = expr });
23391776 }
23401777 if (cIsEnum(src_type) and !cIsEnum(dst_type)) {
2341 return transEnumToInt(rp.c, expr);
1778 // @enumToInt(val)
1779 return Tag.enum_to_int.create(c.arena, expr);
23421780 }
2343 const cast_node = try rp.c.createBuiltinCall("@as", 2);
2344 cast_node.params()[0] = try transQualType(rp, dst_type, loc);
2345 _ = try appendToken(rp.c, .Comma, ",");
2346 cast_node.params()[1] = expr;
2347 cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
2348 return &cast_node.base;
2349}
2350
2351fn transEnumToInt(c: *Context, enum_expr: *ast.Node) TypeError!*ast.Node {
2352 const builtin_node = try c.createBuiltinCall("@enumToInt", 1);
2353 builtin_node.params()[0] = enum_expr;
2354 builtin_node.rparen_token = try appendToken(c, .RParen, ")");
2355 return &builtin_node.base;
1781 // @as(dest_type, val)
1782 return Tag.as.create(c.arena, .{ .lhs = dst_node, .rhs = expr });
23561783}
23571784
2358fn transExpr(
2359 rp: RestorePoint,
2360 scope: *Scope,
2361 expr: *const clang.Expr,
2362 used: ResultUsed,
2363 lrvalue: LRValue,
2364) TransError!*ast.Node {
2365 return transStmt(rp, scope, @ptrCast(*const clang.Stmt, expr), used, lrvalue);
1785fn transExpr(c: *Context, scope: *Scope, expr: *const clang.Expr, used: ResultUsed) TransError!Node {
1786 return transStmt(c, scope, @ptrCast(*const clang.Stmt, expr), used);
23661787}
23671788
23681789/// Same as `transExpr` but with the knowledge that the operand will be type coerced, and therefore
23691790/// an `@as` would be redundant. This is used to prevent redundant `@as` in integer literals.
2370fn transExprCoercing(
2371 rp: RestorePoint,
2372 scope: *Scope,
2373 expr: *const clang.Expr,
2374 used: ResultUsed,
2375 lrvalue: LRValue,
2376) TransError!*ast.Node {
1791fn transExprCoercing(c: *Context, scope: *Scope, expr: *const clang.Expr, used: ResultUsed) TransError!Node {
23771792 switch (@ptrCast(*const clang.Stmt, expr).getStmtClass()) {
23781793 .IntegerLiteralClass => {
2379 return transIntegerLiteral(rp, scope, @ptrCast(*const clang.IntegerLiteral, expr), .used, .no_as);
1794 return transIntegerLiteral(c, scope, @ptrCast(*const clang.IntegerLiteral, expr), .used, .no_as);
23801795 },
23811796 .CharacterLiteralClass => {
2382 return transCharLiteral(rp, scope, @ptrCast(*const clang.CharacterLiteral, expr), .used, .no_as);
1797 return transCharLiteral(c, scope, @ptrCast(*const clang.CharacterLiteral, expr), .used, .no_as);
23831798 },
23841799 .UnaryOperatorClass => {
23851800 const un_expr = @ptrCast(*const clang.UnaryOperator, expr);
23861801 if (un_expr.getOpcode() == .Extension) {
2387 return transExprCoercing(rp, scope, un_expr.getSubExpr(), used, lrvalue);
1802 return transExprCoercing(c, scope, un_expr.getSubExpr(), used);
1803 }
1804 },
1805 .ImplicitCastExprClass => {
1806 const cast_expr = @ptrCast(*const clang.ImplicitCastExpr, expr);
1807 const sub_expr = cast_expr.getSubExpr();
1808 switch (@ptrCast(*const clang.Stmt, sub_expr).getStmtClass()) {
1809 .IntegerLiteralClass, .CharacterLiteralClass => switch (cast_expr.getCastKind()) {
1810 .IntegralToFloating => return transExprCoercing(c, scope, sub_expr, used),
1811 .IntegralCast => {
1812 const dest_type = getExprQualType(c, expr);
1813 if (literalFitsInType(c, sub_expr, dest_type))
1814 return transExprCoercing(c, scope, sub_expr, used);
1815 },
1816 else => {},
1817 },
1818 else => {},
23881819 }
23891820 },
23901821 else => {},
23911822 }
2392 return transExpr(rp, scope, expr, .used, .r_value);
1823 return transExpr(c, scope, expr, .used);
1824}
1825
1826fn literalFitsInType(c: *Context, expr: *const clang.Expr, qt: clang.QualType) bool {
1827 var width = qualTypeIntBitWidth(c, qt) catch 8;
1828 if (width == 0) width = 8; // Byte is the smallest type.
1829 const is_signed = cIsSignedInteger(qt);
1830 const width_max_int = (@as(u64, 1) << math.lossyCast(u6, width - @boolToInt(is_signed))) - 1;
1831
1832 switch (@ptrCast(*const clang.Stmt, expr).getStmtClass()) {
1833 .CharacterLiteralClass => {
1834 const char_lit = @ptrCast(*const clang.CharacterLiteral, expr);
1835 const val = char_lit.getValue();
1836 // If the val is less than the max int then it fits.
1837 return val <= width_max_int;
1838 },
1839 .IntegerLiteralClass => {
1840 const int_lit = @ptrCast(*const clang.IntegerLiteral, expr);
1841 var eval_result: clang.ExprEvalResult = undefined;
1842 if (!int_lit.EvaluateAsInt(&eval_result, c.clang_context)) {
1843 return false;
1844 }
1845
1846 const int = eval_result.Val.getInt();
1847 return int.lessThanEqual(width_max_int);
1848 },
1849 else => unreachable,
1850 }
23931851}
23941852
23951853fn transInitListExprRecord(
2396 rp: RestorePoint,
1854 c: *Context,
23971855 scope: *Scope,
23981856 loc: clang.SourceLocation,
23991857 expr: *const clang.InitListExpr,
24001858 ty: *const clang.Type,
2401 used: ResultUsed,
2402) TransError!*ast.Node {
1859) TransError!Node {
24031860 var is_union_type = false;
24041861 // Unions and Structs are both represented as RecordDecl
24051862 const record_ty = ty.getAsRecordType() orelse
......@@ -2411,13 +1868,11 @@ fn transInitListExprRecord(
24111868 const record_def = record_decl.getDefinition() orelse
24121869 unreachable;
24131870
2414 const ty_node = try transType(rp, ty, loc);
1871 const ty_node = try transType(c, scope, ty, loc);
24151872 const init_count = expr.getNumInits();
2416 var field_inits = std.ArrayList(*ast.Node).init(rp.c.gpa);
1873 var field_inits = std.ArrayList(ast.Payload.ContainerInit.Initializer).init(c.gpa);
24171874 defer field_inits.deinit();
24181875
2419 _ = try appendToken(rp.c, .LBrace, "{");
2420
24211876 var init_i: c_uint = 0;
24221877 var it = record_def.field_begin();
24231878 const end_it = record_def.field_end();
......@@ -2435,78 +1890,34 @@ fn transInitListExprRecord(
24351890
24361891 // Generate the field assignment expression:
24371892 // .field_name = expr
2438 const period_tok = try appendToken(rp.c, .Period, ".");
2439
2440 var raw_name = try rp.c.str(@ptrCast(*const clang.NamedDecl, field_decl).getName_bytes_begin());
1893 var raw_name = try c.str(@ptrCast(*const clang.NamedDecl, field_decl).getName_bytes_begin());
24411894 if (field_decl.isAnonymousStructOrUnion()) {
2442 const name = rp.c.decl_table.get(@ptrToInt(field_decl.getCanonicalDecl())).?;
2443 raw_name = try mem.dupe(rp.c.arena, u8, name);
1895 const name = c.decl_table.get(@ptrToInt(field_decl.getCanonicalDecl())).?;
1896 raw_name = try mem.dupe(c.arena, u8, name);
24441897 }
2445 const field_name_tok = try appendIdentifier(rp.c, raw_name);
2446
2447 _ = try appendToken(rp.c, .Equal, "=");
24481898
2449 const field_init_node = try rp.c.arena.create(ast.Node.FieldInitializer);
2450 field_init_node.* = .{
2451 .period_token = period_tok,
2452 .name_token = field_name_tok,
2453 .expr = try transExpr(rp, scope, elem_expr, .used, .r_value),
2454 };
2455
2456 try field_inits.append(&field_init_node.base);
2457 _ = try appendToken(rp.c, .Comma, ",");
1899 try field_inits.append(.{
1900 .name = raw_name,
1901 .value = try transExpr(c, scope, elem_expr, .used),
1902 });
24581903 }
24591904
2460 const node = try ast.Node.StructInitializer.alloc(rp.c.arena, field_inits.items.len);
2461 node.* = .{
2462 .lhs = ty_node,
2463 .rtoken = try appendToken(rp.c, .RBrace, "}"),
2464 .list_len = field_inits.items.len,
2465 };
2466 mem.copy(*ast.Node, node.list(), field_inits.items);
2467 return &node.base;
2468}
2469
2470fn transCreateNodeArrayType(
2471 rp: RestorePoint,
2472 source_loc: clang.SourceLocation,
2473 ty: *const clang.Type,
2474 len: anytype,
2475) !*ast.Node {
2476 const node = try rp.c.arena.create(ast.Node.ArrayType);
2477 const op_token = try appendToken(rp.c, .LBracket, "[");
2478 const len_expr = try transCreateNodeInt(rp.c, len);
2479 _ = try appendToken(rp.c, .RBracket, "]");
2480 node.* = .{
2481 .op_token = op_token,
2482 .rhs = try transType(rp, ty, source_loc),
2483 .len_expr = len_expr,
2484 };
2485 return &node.base;
2486}
2487
2488fn transCreateEmptyArray(rp: RestorePoint, loc: clang.SourceLocation, ty: *const clang.Type) TransError!*ast.Node {
2489 const ty_node = try transCreateNodeArrayType(rp, loc, ty, 0);
2490 _ = try appendToken(rp.c, .LBrace, "{");
2491 const filler_init_node = try ast.Node.ArrayInitializer.alloc(rp.c.arena, 0);
2492 filler_init_node.* = .{
1905 return Tag.container_init.create(c.arena, .{
24931906 .lhs = ty_node,
2494 .rtoken = try appendToken(rp.c, .RBrace, "}"),
2495 .list_len = 0,
2496 };
2497 return &filler_init_node.base;
1907 .inits = try c.arena.dupe(ast.Payload.ContainerInit.Initializer, field_inits.items),
1908 });
24981909}
24991910
25001911fn transInitListExprArray(
2501 rp: RestorePoint,
1912 c: *Context,
25021913 scope: *Scope,
25031914 loc: clang.SourceLocation,
25041915 expr: *const clang.InitListExpr,
25051916 ty: *const clang.Type,
2506 used: ResultUsed,
2507) TransError!*ast.Node {
1917) TransError!Node {
25081918 const arr_type = ty.getAsArrayTypeUnsafe();
25091919 const child_qt = arr_type.getElementType();
1920 const child_type = try transQualType(c, scope, child_qt, loc);
25101921 const init_count = expr.getNumInits();
25111922 assert(@ptrCast(*const clang.Type, arr_type).isConstantArrayType());
25121923 const const_arr_ty = @ptrCast(*const clang.ConstantArrayType, arr_type);
......@@ -2515,125 +1926,83 @@ fn transInitListExprArray(
25151926 const leftover_count = all_count - init_count;
25161927
25171928 if (all_count == 0) {
2518 return transCreateEmptyArray(rp, loc, child_qt.getTypePtr());
1929 return Tag.empty_array.create(c.arena, child_type);
25191930 }
25201931
2521 var init_node: *ast.Node.ArrayInitializer = undefined;
2522 var cat_tok: ast.TokenIndex = undefined;
2523 if (init_count != 0) {
2524 const ty_node = try transCreateNodeArrayType(
2525 rp,
2526 loc,
2527 child_qt.getTypePtr(),
2528 init_count,
2529 );
2530 _ = try appendToken(rp.c, .LBrace, "{");
2531 init_node = try ast.Node.ArrayInitializer.alloc(rp.c.arena, init_count);
2532 init_node.* = .{
2533 .lhs = ty_node,
2534 .rtoken = undefined,
2535 .list_len = init_count,
2536 };
2537 const init_list = init_node.list();
1932 const init_node = if (init_count != 0) blk: {
1933 const init_list = try c.arena.alloc(Node, init_count);
25381934
2539 var i: c_uint = 0;
2540 while (i < init_count) : (i += 1) {
2541 const elem_expr = expr.getInit(i);
2542 init_list[i] = try transExpr(rp, scope, elem_expr, .used, .r_value);
2543 _ = try appendToken(rp.c, .Comma, ",");
1935 for (init_list) |*init, i| {
1936 const elem_expr = expr.getInit(@intCast(c_uint, i));
1937 init.* = try transExprCoercing(c, scope, elem_expr, .used);
25441938 }
2545 init_node.rtoken = try appendToken(rp.c, .RBrace, "}");
1939 const init_node = try Tag.array_init.create(c.arena, .{
1940 .cond = try Tag.array_type.create(c.arena, .{ .len = init_count, .elem_type = child_type }),
1941 .cases = init_list,
1942 });
25461943 if (leftover_count == 0) {
2547 return &init_node.base;
1944 return init_node;
25481945 }
2549 cat_tok = try appendToken(rp.c, .PlusPlus, "++");
2550 }
1946 break :blk init_node;
1947 } else null;
25511948
2552 const ty_node = try transCreateNodeArrayType(rp, loc, child_qt.getTypePtr(), 1);
2553 _ = try appendToken(rp.c, .LBrace, "{");
2554 const filler_init_node = try ast.Node.ArrayInitializer.alloc(rp.c.arena, 1);
2555 filler_init_node.* = .{
2556 .lhs = ty_node,
2557 .rtoken = undefined,
2558 .list_len = 1,
2559 };
25601949 const filler_val_expr = expr.getArrayFiller();
2561 filler_init_node.list()[0] = try transExpr(rp, scope, filler_val_expr, .used, .r_value);
2562 filler_init_node.rtoken = try appendToken(rp.c, .RBrace, "}");
2563
2564 const rhs_node = if (leftover_count == 1)
2565 &filler_init_node.base
2566 else blk: {
2567 const mul_tok = try appendToken(rp.c, .AsteriskAsterisk, "**");
2568 const mul_node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
2569 mul_node.* = .{
2570 .base = .{ .tag = .ArrayMult },
2571 .op_token = mul_tok,
2572 .lhs = &filler_init_node.base,
2573 .rhs = try transCreateNodeInt(rp.c, leftover_count),
2574 };
2575 break :blk &mul_node.base;
2576 };
1950 const filler_node = try Tag.array_filler.create(c.arena, .{
1951 .type = child_type,
1952 .filler = try transExprCoercing(c, scope, filler_val_expr, .used),
1953 .count = leftover_count,
1954 });
25771955
2578 if (init_count == 0) {
2579 return rhs_node;
1956 if (init_node) |some| {
1957 return Tag.array_cat.create(c.arena, .{ .lhs = some, .rhs = filler_node });
1958 } else {
1959 return filler_node;
25801960 }
2581
2582 const cat_node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
2583 cat_node.* = .{
2584 .base = .{ .tag = .ArrayCat },
2585 .op_token = cat_tok,
2586 .lhs = &init_node.base,
2587 .rhs = rhs_node,
2588 };
2589 return &cat_node.base;
25901961}
25911962
25921963fn transInitListExpr(
2593 rp: RestorePoint,
1964 c: *Context,
25941965 scope: *Scope,
25951966 expr: *const clang.InitListExpr,
25961967 used: ResultUsed,
2597) TransError!*ast.Node {
2598 const qt = getExprQualType(rp.c, @ptrCast(*const clang.Expr, expr));
1968) TransError!Node {
1969 const qt = getExprQualType(c, @ptrCast(*const clang.Expr, expr));
25991970 var qual_type = qt.getTypePtr();
26001971 const source_loc = @ptrCast(*const clang.Expr, expr).getBeginLoc();
26011972
26021973 if (qual_type.isRecordType()) {
2603 return transInitListExprRecord(
2604 rp,
1974 return maybeSuppressResult(c, scope, used, try transInitListExprRecord(
1975 c,
26051976 scope,
26061977 source_loc,
26071978 expr,
26081979 qual_type,
2609 used,
2610 );
1980 ));
26111981 } else if (qual_type.isArrayType()) {
2612 return transInitListExprArray(
2613 rp,
1982 return maybeSuppressResult(c, scope, used, try transInitListExprArray(
1983 c,
26141984 scope,
26151985 source_loc,
26161986 expr,
26171987 qual_type,
2618 used,
2619 );
1988 ));
26201989 } else {
2621 const type_name = rp.c.str(qual_type.getTypeClassName());
2622 return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported initlist type: '{s}'", .{type_name});
1990 const type_name = c.str(qual_type.getTypeClassName());
1991 return fail(c, error.UnsupportedType, source_loc, "unsupported initlist type: '{s}'", .{type_name});
26231992 }
26241993}
26251994
26261995fn transZeroInitExpr(
2627 rp: RestorePoint,
1996 c: *Context,
26281997 scope: *Scope,
26291998 source_loc: clang.SourceLocation,
26301999 ty: *const clang.Type,
2631) TransError!*ast.Node {
2000) TransError!Node {
26322001 switch (ty.getTypeClass()) {
26332002 .Builtin => {
26342003 const builtin_ty = @ptrCast(*const clang.BuiltinType, ty);
26352004 switch (builtin_ty.getKind()) {
2636 .Bool => return try transCreateNodeBoolLiteral(rp.c, false),
2005 .Bool => return Tag.false_literal.init(),
26372006 .Char_U,
26382007 .UChar,
26392008 .Char_S,
......@@ -2654,126 +2023,112 @@ fn transZeroInitExpr(
26542023 .Float128,
26552024 .Float16,
26562025 .LongDouble,
2657 => return transCreateNodeInt(rp.c, 0),
2658 else => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported builtin type", .{}),
2026 => return Tag.zero_literal.init(),
2027 else => return fail(c, error.UnsupportedType, source_loc, "unsupported builtin type", .{}),
26592028 }
26602029 },
2661 .Pointer => return transCreateNodeNullLiteral(rp.c),
2030 .Pointer => return Tag.null_literal.init(),
26622031 .Typedef => {
26632032 const typedef_ty = @ptrCast(*const clang.TypedefType, ty);
26642033 const typedef_decl = typedef_ty.getDecl();
26652034 return transZeroInitExpr(
2666 rp,
2035 c,
26672036 scope,
26682037 source_loc,
26692038 typedef_decl.getUnderlyingType().getTypePtr(),
26702039 );
26712040 },
2672 else => {},
2041 else => return Tag.std_mem_zeroes.create(c.arena, try transType(c, scope, ty, source_loc)),
26732042 }
2674
2675 return revertAndWarn(rp, error.UnsupportedType, source_loc, "type does not have an implicit init value", .{});
26762043}
26772044
26782045fn transImplicitValueInitExpr(
2679 rp: RestorePoint,
2046 c: *Context,
26802047 scope: *Scope,
26812048 expr: *const clang.Expr,
26822049 used: ResultUsed,
2683) TransError!*ast.Node {
2050) TransError!Node {
26842051 const source_loc = expr.getBeginLoc();
2685 const qt = getExprQualType(rp.c, expr);
2052 const qt = getExprQualType(c, expr);
26862053 const ty = qt.getTypePtr();
2687 return transZeroInitExpr(rp, scope, source_loc, ty);
2054 return transZeroInitExpr(c, scope, source_loc, ty);
26882055}
26892056
26902057fn transIfStmt(
2691 rp: RestorePoint,
2058 c: *Context,
26922059 scope: *Scope,
26932060 stmt: *const clang.IfStmt,
2694) TransError!*ast.Node {
2061) TransError!Node {
26952062 // if (c) t
26962063 // if (c) t else e
2697 const if_node = try transCreateNodeIf(rp.c);
2698
26992064 var cond_scope = Scope.Condition{
27002065 .base = .{
27012066 .parent = scope,
2702 .id = .Condition,
2067 .id = .condition,
27032068 },
27042069 };
27052070 defer cond_scope.deinit();
27062071 const cond_expr = @ptrCast(*const clang.Expr, stmt.getCond());
2707 if_node.condition = try transBoolExpr(rp, &cond_scope.base, cond_expr, .used, .r_value, false);
2708 _ = try appendToken(rp.c, .RParen, ")");
2072 const cond = try transBoolExpr(c, &cond_scope.base, cond_expr, .used);
27092073
2710 if_node.body = try transStmt(rp, scope, stmt.getThen(), .unused, .r_value);
2711
2712 if (stmt.getElse()) |expr| {
2713 if_node.@"else" = try transCreateNodeElse(rp.c);
2714 if_node.@"else".?.body = try transStmt(rp, scope, expr, .unused, .r_value);
2715 }
2716 _ = try appendToken(rp.c, .Semicolon, ";");
2717 return &if_node.base;
2074 const then_body = try transStmt(c, scope, stmt.getThen(), .unused);
2075 const else_body = if (stmt.getElse()) |expr|
2076 try transStmt(c, scope, expr, .unused)
2077 else
2078 null;
2079 return Tag.@"if".create(c.arena, .{ .cond = cond, .then = then_body, .@"else" = else_body });
27182080}
27192081
27202082fn transWhileLoop(
2721 rp: RestorePoint,
2083 c: *Context,
27222084 scope: *Scope,
27232085 stmt: *const clang.WhileStmt,
2724) TransError!*ast.Node {
2725 const while_node = try transCreateNodeWhile(rp.c);
2726
2086) TransError!Node {
27272087 var cond_scope = Scope.Condition{
27282088 .base = .{
27292089 .parent = scope,
2730 .id = .Condition,
2090 .id = .condition,
27312091 },
27322092 };
27332093 defer cond_scope.deinit();
27342094 const cond_expr = @ptrCast(*const clang.Expr, stmt.getCond());
2735 while_node.condition = try transBoolExpr(rp, &cond_scope.base, cond_expr, .used, .r_value, false);
2736 _ = try appendToken(rp.c, .RParen, ")");
2095 const cond = try transBoolExpr(c, &cond_scope.base, cond_expr, .used);
27372096
27382097 var loop_scope = Scope{
27392098 .parent = scope,
2740 .id = .Loop,
2099 .id = .loop,
27412100 };
2742 while_node.body = try transStmt(rp, &loop_scope, stmt.getBody(), .unused, .r_value);
2743 _ = try appendToken(rp.c, .Semicolon, ";");
2744 return &while_node.base;
2101 const body = try transStmt(c, &loop_scope, stmt.getBody(), .unused);
2102 return Tag.@"while".create(c.arena, .{ .cond = cond, .body = body, .cont_expr = null });
27452103}
27462104
27472105fn transDoWhileLoop(
2748 rp: RestorePoint,
2106 c: *Context,
27492107 scope: *Scope,
27502108 stmt: *const clang.DoStmt,
2751) TransError!*ast.Node {
2752 const while_node = try transCreateNodeWhile(rp.c);
2753
2754 while_node.condition = try transCreateNodeBoolLiteral(rp.c, true);
2755 _ = try appendToken(rp.c, .RParen, ")");
2756 var new = false;
2109) TransError!Node {
27572110 var loop_scope = Scope{
27582111 .parent = scope,
2759 .id = .Loop,
2112 .id = .do_loop,
27602113 };
27612114
27622115 // if (!cond) break;
2763 const if_node = try transCreateNodeIf(rp.c);
27642116 var cond_scope = Scope.Condition{
27652117 .base = .{
27662118 .parent = scope,
2767 .id = .Condition,
2119 .id = .condition,
27682120 },
27692121 };
27702122 defer cond_scope.deinit();
2771 const prefix_op = try transCreateNodeSimplePrefixOp(rp.c, .BoolNot, .Bang, "!");
2772 prefix_op.rhs = try transBoolExpr(rp, &cond_scope.base, @ptrCast(*const clang.Expr, stmt.getCond()), .used, .r_value, true);
2773 _ = try appendToken(rp.c, .RParen, ")");
2774 if_node.condition = &prefix_op.base;
2775 if_node.body = &(try transCreateNodeBreak(rp.c, null, null)).base;
2776 _ = try appendToken(rp.c, .Semicolon, ";");
2123 const cond = try transBoolExpr(c, &cond_scope.base, @ptrCast(*const clang.Expr, stmt.getCond()), .used);
2124 const if_not_break = switch (cond.tag()) {
2125 .false_literal => return transStmt(c, scope, stmt.getBody(), .unused),
2126 .true_literal => {
2127 const body_node = try transStmt(c, scope, stmt.getBody(), .unused);
2128 return Tag.while_true.create(c.arena, body_node);
2129 },
2130 else => try Tag.if_not_break.create(c.arena, cond),
2131 };
27772132
27782133 const body_node = if (stmt.getBody().getStmtClass() == .CompoundStmtClass) blk: {
27792134 // there's already a block in C, so we'll append our condition to it.
......@@ -2786,8 +2141,11 @@ fn transDoWhileLoop(
27862141 // zig: b;
27872142 // zig: if (!cond) break;
27882143 // zig: }
2789 const node = try transStmt(rp, &loop_scope, stmt.getBody(), .unused, .r_value);
2790 break :blk node.castTag(.Block).?;
2144 const node = try transStmt(c, &loop_scope, stmt.getBody(), .unused);
2145 const block = node.castTag(.block).?;
2146 block.data.stmts.len += 1; // This is safe since we reserve one extra space in Scope.Block.complete.
2147 block.data.stmts[block.data.stmts.len - 1] = if_not_break;
2148 break :blk node;
27912149 } else blk: {
27922150 // the C statement is without a block, so we need to create a block to contain it.
27932151 // c: do
......@@ -2797,387 +2155,356 @@ fn transDoWhileLoop(
27972155 // zig: a;
27982156 // zig: if (!cond) break;
27992157 // zig: }
2800 new = true;
2801 const block = try rp.c.createBlock(2);
2802 block.statements_len = 1; // over-allocated so we can add another below
2803 block.statements()[0] = try transStmt(rp, &loop_scope, stmt.getBody(), .unused, .r_value);
2804 break :blk block;
2158 const statements = try c.arena.alloc(Node, 2);
2159 statements[0] = try transStmt(c, &loop_scope, stmt.getBody(), .unused);
2160 statements[1] = if_not_break;
2161 break :blk try Tag.block.create(c.arena, .{ .label = null, .stmts = statements });
28052162 };
2806
2807 // In both cases above, we reserved 1 extra statement.
2808 body_node.statements_len += 1;
2809 body_node.statements()[body_node.statements_len - 1] = &if_node.base;
2810 if (new)
2811 body_node.rbrace = try appendToken(rp.c, .RBrace, "}");
2812 while_node.body = &body_node.base;
2813 return &while_node.base;
2163 return Tag.while_true.create(c.arena, body_node);
28142164}
28152165
28162166fn transForLoop(
2817 rp: RestorePoint,
2167 c: *Context,
28182168 scope: *Scope,
28192169 stmt: *const clang.ForStmt,
2820) TransError!*ast.Node {
2170) TransError!Node {
28212171 var loop_scope = Scope{
28222172 .parent = scope,
2823 .id = .Loop,
2173 .id = .loop,
28242174 };
28252175
28262176 var block_scope: ?Scope.Block = null;
28272177 defer if (block_scope) |*bs| bs.deinit();
28282178
28292179 if (stmt.getInit()) |init| {
2830 block_scope = try Scope.Block.init(rp.c, scope, false);
2180 block_scope = try Scope.Block.init(c, scope, false);
28312181 loop_scope.parent = &block_scope.?.base;
2832 const init_node = try transStmt(rp, &block_scope.?.base, init, .unused, .r_value);
2833 try block_scope.?.statements.append(init_node);
2182 const init_node = try transStmt(c, &block_scope.?.base, init, .unused);
2183 if (init_node.tag() != .declaration) try block_scope.?.statements.append(init_node);
28342184 }
28352185 var cond_scope = Scope.Condition{
28362186 .base = .{
28372187 .parent = &loop_scope,
2838 .id = .Condition,
2188 .id = .condition,
28392189 },
28402190 };
28412191 defer cond_scope.deinit();
28422192
2843 const while_node = try transCreateNodeWhile(rp.c);
2844 while_node.condition = if (stmt.getCond()) |cond|
2845 try transBoolExpr(rp, &cond_scope.base, cond, .used, .r_value, false)
2193 const cond = if (stmt.getCond()) |cond|
2194 try transBoolExpr(c, &cond_scope.base, cond, .used)
28462195 else
2847 try transCreateNodeBoolLiteral(rp.c, true);
2848 _ = try appendToken(rp.c, .RParen, ")");
2196 Tag.true_literal.init();
28492197
2850 if (stmt.getInc()) |incr| {
2851 _ = try appendToken(rp.c, .Colon, ":");
2852 _ = try appendToken(rp.c, .LParen, "(");
2853 while_node.continue_expr = try transExpr(rp, &cond_scope.base, incr, .unused, .r_value);
2854 _ = try appendToken(rp.c, .RParen, ")");
2855 }
2198 const cont_expr = if (stmt.getInc()) |incr|
2199 try transExpr(c, &cond_scope.base, incr, .unused)
2200 else
2201 null;
28562202
2857 while_node.body = try transStmt(rp, &loop_scope, stmt.getBody(), .unused, .r_value);
2203 const body = try transStmt(c, &loop_scope, stmt.getBody(), .unused);
2204 const while_node = try Tag.@"while".create(c.arena, .{ .cond = cond, .body = body, .cont_expr = cont_expr });
28582205 if (block_scope) |*bs| {
2859 try bs.statements.append(&while_node.base);
2860 return try bs.complete(rp.c);
2206 try bs.statements.append(while_node);
2207 return try bs.complete(c);
28612208 } else {
2862 _ = try appendToken(rp.c, .Semicolon, ";");
2863 return &while_node.base;
2209 return while_node;
28642210 }
28652211}
28662212
2867fn getSwitchCaseCount(stmt: *const clang.SwitchStmt) usize {
2868 const body = stmt.getBody();
2869 assert(body.getStmtClass() == .CompoundStmtClass);
2870 const comp = @ptrCast(*const clang.CompoundStmt, body);
2871 // TODO https://github.com/ziglang/zig/issues/1738
2872 // return comp.body_end() - comp.body_begin();
2873 const start_addr = @ptrToInt(comp.body_begin());
2874 const end_addr = @ptrToInt(comp.body_end());
2875 return (end_addr - start_addr) / @sizeOf(*clang.Stmt);
2876}
2877
28782213fn transSwitch(
2879 rp: RestorePoint,
2214 c: *Context,
28802215 scope: *Scope,
28812216 stmt: *const clang.SwitchStmt,
2882) TransError!*ast.Node {
2883 const switch_tok = try appendToken(rp.c, .Keyword_switch, "switch");
2884 _ = try appendToken(rp.c, .LParen, "(");
2885
2886 const cases_len = getSwitchCaseCount(stmt);
2887
2217) TransError!Node {
28882218 var cond_scope = Scope.Condition{
28892219 .base = .{
28902220 .parent = scope,
2891 .id = .Condition,
2221 .id = .condition,
28922222 },
28932223 };
28942224 defer cond_scope.deinit();
2895 const switch_expr = try transExpr(rp, &cond_scope.base, stmt.getCond(), .used, .r_value);
2896 _ = try appendToken(rp.c, .RParen, ")");
2897 _ = try appendToken(rp.c, .LBrace, "{");
2898 // reserve +1 case in case there is no default case
2899 const switch_node = try ast.Node.Switch.alloc(rp.c.arena, cases_len + 1);
2900 switch_node.* = .{
2901 .switch_token = switch_tok,
2902 .expr = switch_expr,
2903 .cases_len = cases_len + 1,
2904 .rbrace = try appendToken(rp.c, .RBrace, "}"),
2905 };
2225 const switch_expr = try transExpr(c, &cond_scope.base, stmt.getCond(), .used);
29062226
2907 var switch_scope = Scope.Switch{
2908 .base = .{
2909 .id = .Switch,
2910 .parent = scope,
2911 },
2912 .cases = switch_node.cases(),
2913 .case_index = 0,
2914 .pending_block = undefined,
2915 .default_label = null,
2916 .switch_label = null,
2917 };
2227 var cases = std.ArrayList(Node).init(c.gpa);
2228 defer cases.deinit();
2229 var has_default = false;
29182230
2919 // tmp block that all statements will go before being picked up by a case or default
2920 var block_scope = try Scope.Block.init(rp.c, &switch_scope.base, false);
2921 defer block_scope.deinit();
2231 const body = stmt.getBody();
2232 assert(body.getStmtClass() == .CompoundStmtClass);
2233 const compound_stmt = @ptrCast(*const clang.CompoundStmt, body);
2234 var it = compound_stmt.body_begin();
2235 const end_it = compound_stmt.body_end();
2236 // Iterate over switch body and collect all cases.
2237 // Fallthrough is handled by duplicating statements.
2238 while (it != end_it) : (it += 1) {
2239 switch (it[0].getStmtClass()) {
2240 .CaseStmtClass => {
2241 var items = std.ArrayList(Node).init(c.gpa);
2242 defer items.deinit();
2243 const sub = try transCaseStmt(c, scope, it[0], &items);
2244 const res = try transSwitchProngStmt(c, scope, sub, it, end_it);
2245
2246 if (items.items.len == 0) {
2247 has_default = true;
2248 const switch_else = try Tag.switch_else.create(c.arena, res);
2249 try cases.append(switch_else);
2250 } else {
2251 const switch_prong = try Tag.switch_prong.create(c.arena, .{
2252 .cases = try c.arena.dupe(Node, items.items),
2253 .cond = res,
2254 });
2255 try cases.append(switch_prong);
2256 }
2257 },
2258 .DefaultStmtClass => {
2259 has_default = true;
2260 const default_stmt = @ptrCast(*const clang.DefaultStmt, it[0]);
2261
2262 var sub = default_stmt.getSubStmt();
2263 while (true) switch (sub.getStmtClass()) {
2264 .CaseStmtClass => sub = @ptrCast(*const clang.CaseStmt, sub).getSubStmt(),
2265 .DefaultStmtClass => sub = @ptrCast(*const clang.DefaultStmt, sub).getSubStmt(),
2266 else => break,
2267 };
29222268
2923 // Note that we do not defer a deinit here; the switch_scope.pending_block field
2924 // has its own memory management. This resource is freed inside `transCase` and
2925 // then the final pending_block is freed at the bottom of this function with
2926 // pending_block.deinit().
2927 switch_scope.pending_block = try Scope.Block.init(rp.c, scope, false);
2928 try switch_scope.pending_block.statements.append(&switch_node.base);
2929
2930 const last = try transStmt(rp, &block_scope.base, stmt.getBody(), .unused, .r_value);
2931 _ = try appendToken(rp.c, .Semicolon, ";");
2932
2933 // take all pending statements
2934 const last_block_stmts = last.cast(ast.Node.Block).?.statements();
2935 try switch_scope.pending_block.statements.ensureCapacity(
2936 switch_scope.pending_block.statements.items.len + last_block_stmts.len,
2937 );
2938 for (last_block_stmts) |n| {
2939 switch_scope.pending_block.statements.appendAssumeCapacity(n);
2940 }
2941
2942 if (switch_scope.default_label == null) {
2943 switch_scope.switch_label = try block_scope.makeMangledName(rp.c, "switch");
2944 }
2945 if (switch_scope.switch_label) |l| {
2946 switch_scope.pending_block.label = try appendIdentifier(rp.c, l);
2947 _ = try appendToken(rp.c, .Colon, ":");
2948 }
2949 if (switch_scope.default_label == null) {
2950 const else_prong = try transCreateNodeSwitchCase(rp.c, try transCreateNodeSwitchElse(rp.c));
2951 else_prong.expr = blk: {
2952 var br = try CtrlFlow.init(rp.c, .Break, switch_scope.switch_label.?);
2953 break :blk &(try br.finish(null)).base;
2954 };
2955 _ = try appendToken(rp.c, .Comma, ",");
2269 const res = try transSwitchProngStmt(c, scope, sub, it, end_it);
29562270
2957 if (switch_scope.case_index >= switch_scope.cases.len)
2958 return revertAndWarn(rp, error.UnsupportedTranslation, @ptrCast(*const clang.Stmt, stmt).getBeginLoc(), "TODO complex switch cases", .{});
2959 switch_scope.cases[switch_scope.case_index] = &else_prong.base;
2960 switch_scope.case_index += 1;
2271 const switch_else = try Tag.switch_else.create(c.arena, res);
2272 try cases.append(switch_else);
2273 },
2274 else => {}, // collected in transSwitchProngStmt
2275 }
29612276 }
2962 // We overallocated in case there was no default, so now we correct
2963 // the number of cases in the AST node.
2964 switch_node.cases_len = switch_scope.case_index;
2965
2966 const result_node = try switch_scope.pending_block.complete(rp.c);
2967 switch_scope.pending_block.deinit();
2968 return result_node;
2969}
2970
2971fn transCase(
2972 rp: RestorePoint,
2973 scope: *Scope,
2974 stmt: *const clang.CaseStmt,
2975) TransError!*ast.Node {
2976 const block_scope = scope.findBlockScope(rp.c) catch unreachable;
2977 const switch_scope = scope.getSwitch();
2978 const label = try block_scope.makeMangledName(rp.c, "case");
2979 _ = try appendToken(rp.c, .Semicolon, ";");
2980
2981 const expr = if (stmt.getRHS()) |rhs| blk: {
2982 const lhs_node = try transExpr(rp, scope, stmt.getLHS(), .used, .r_value);
2983 const ellips = try appendToken(rp.c, .Ellipsis3, "...");
2984 const rhs_node = try transExpr(rp, scope, rhs, .used, .r_value);
2985
2986 const node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
2987 node.* = .{
2988 .base = .{ .tag = .Range },
2989 .op_token = ellips,
2990 .lhs = lhs_node,
2991 .rhs = rhs_node,
2992 };
2993 break :blk &node.base;
2994 } else
2995 try transExpr(rp, scope, stmt.getLHS(), .used, .r_value);
29962277
2997 const switch_prong = try transCreateNodeSwitchCase(rp.c, expr);
2998 switch_prong.expr = blk: {
2999 var br = try CtrlFlow.init(rp.c, .Break, label);
3000 break :blk &(try br.finish(null)).base;
3001 };
3002 _ = try appendToken(rp.c, .Comma, ",");
2278 if (!has_default) {
2279 const else_prong = try Tag.switch_else.create(c.arena, Tag.empty_block.init());
2280 try cases.append(else_prong);
2281 }
30032282
3004 if (switch_scope.case_index >= switch_scope.cases.len)
3005 return revertAndWarn(rp, error.UnsupportedTranslation, @ptrCast(*const clang.Stmt, stmt).getBeginLoc(), "TODO complex switch cases", .{});
3006 switch_scope.cases[switch_scope.case_index] = &switch_prong.base;
3007 switch_scope.case_index += 1;
2283 return Tag.@"switch".create(c.arena, .{
2284 .cond = switch_expr,
2285 .cases = try c.arena.dupe(Node, cases.items),
2286 });
2287}
30082288
3009 switch_scope.pending_block.label = try appendIdentifier(rp.c, label);
3010 _ = try appendToken(rp.c, .Colon, ":");
2289/// Collects all items for this case, returns the first statement after the labels.
2290/// If items ends up empty, the prong should be translated as an else.
2291fn transCaseStmt(c: *Context, scope: *Scope, stmt: *const clang.Stmt, items: *std.ArrayList(Node)) TransError!*const clang.Stmt {
2292 var sub = stmt;
2293 var seen_default = false;
2294 while (true) {
2295 switch (sub.getStmtClass()) {
2296 .DefaultStmtClass => {
2297 seen_default = true;
2298 items.items.len = 0;
2299 const default_stmt = @ptrCast(*const clang.DefaultStmt, sub);
2300 sub = default_stmt.getSubStmt();
2301 },
2302 .CaseStmtClass => {
2303 const case_stmt = @ptrCast(*const clang.CaseStmt, sub);
30112304
3012 // take all pending statements
3013 try switch_scope.pending_block.statements.appendSlice(block_scope.statements.items);
3014 block_scope.statements.shrinkAndFree(0);
2305 if (seen_default) {
2306 items.items.len = 0;
2307 sub = case_stmt.getSubStmt();
2308 continue;
2309 }
30152310
3016 const pending_node = try switch_scope.pending_block.complete(rp.c);
3017 switch_scope.pending_block.deinit();
3018 switch_scope.pending_block = try Scope.Block.init(rp.c, scope, false);
2311 const expr = if (case_stmt.getRHS()) |rhs| blk: {
2312 const lhs_node = try transExprCoercing(c, scope, case_stmt.getLHS(), .used);
2313 const rhs_node = try transExprCoercing(c, scope, rhs, .used);
30192314
3020 try switch_scope.pending_block.statements.append(pending_node);
2315 break :blk try Tag.ellipsis3.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node });
2316 } else
2317 try transExprCoercing(c, scope, case_stmt.getLHS(), .used);
30212318
3022 return transStmt(rp, scope, stmt.getSubStmt(), .unused, .r_value);
2319 try items.append(expr);
2320 sub = case_stmt.getSubStmt();
2321 },
2322 else => return sub,
2323 }
2324 }
30232325}
30242326
3025fn transDefault(
3026 rp: RestorePoint,
2327/// Collects all statements seen by this case into a block.
2328/// Avoids creating a block if the first statement is a break or return.
2329fn transSwitchProngStmt(
2330 c: *Context,
30272331 scope: *Scope,
3028 stmt: *const clang.DefaultStmt,
3029) TransError!*ast.Node {
3030 const block_scope = scope.findBlockScope(rp.c) catch unreachable;
3031 const switch_scope = scope.getSwitch();
3032 switch_scope.default_label = try block_scope.makeMangledName(rp.c, "default");
3033 _ = try appendToken(rp.c, .Semicolon, ";");
3034
3035 const else_prong = try transCreateNodeSwitchCase(rp.c, try transCreateNodeSwitchElse(rp.c));
3036 else_prong.expr = blk: {
3037 var br = try CtrlFlow.init(rp.c, .Break, switch_scope.default_label.?);
3038 break :blk &(try br.finish(null)).base;
3039 };
3040 _ = try appendToken(rp.c, .Comma, ",");
3041
3042 if (switch_scope.case_index >= switch_scope.cases.len)
3043 return revertAndWarn(rp, error.UnsupportedTranslation, @ptrCast(*const clang.Stmt, stmt).getBeginLoc(), "TODO complex switch cases", .{});
3044 switch_scope.cases[switch_scope.case_index] = &else_prong.base;
3045 switch_scope.case_index += 1;
3046
3047 switch_scope.pending_block.label = try appendIdentifier(rp.c, switch_scope.default_label.?);
3048 _ = try appendToken(rp.c, .Colon, ":");
3049
3050 // take all pending statements
3051 try switch_scope.pending_block.statements.appendSlice(block_scope.statements.items);
3052 block_scope.statements.shrinkAndFree(0);
2332 stmt: *const clang.Stmt,
2333 parent_it: clang.CompoundStmt.ConstBodyIterator,
2334 parent_end_it: clang.CompoundStmt.ConstBodyIterator,
2335) TransError!Node {
2336 switch (stmt.getStmtClass()) {
2337 .BreakStmtClass => return Tag.empty_block.init(),
2338 .ReturnStmtClass => return transStmt(c, scope, stmt, .unused),
2339 .CaseStmtClass, .DefaultStmtClass => unreachable,
2340 else => {
2341 var block_scope = try Scope.Block.init(c, scope, false);
2342 defer block_scope.deinit();
30532343
3054 const pending_node = try switch_scope.pending_block.complete(rp.c);
3055 switch_scope.pending_block.deinit();
3056 switch_scope.pending_block = try Scope.Block.init(rp.c, scope, false);
3057 try switch_scope.pending_block.statements.append(pending_node);
2344 // we do not need to translate `stmt` since it is the first stmt of `parent_it`
2345 try transSwitchProngStmtInline(c, &block_scope, parent_it, parent_end_it);
2346 return try block_scope.complete(c);
2347 },
2348 }
2349}
30582350
3059 return transStmt(rp, scope, stmt.getSubStmt(), .unused, .r_value);
2351/// Collects all statements seen by this case into a block.
2352fn transSwitchProngStmtInline(
2353 c: *Context,
2354 block: *Scope.Block,
2355 start_it: clang.CompoundStmt.ConstBodyIterator,
2356 end_it: clang.CompoundStmt.ConstBodyIterator,
2357) TransError!void {
2358 var it = start_it;
2359 while (it != end_it) : (it += 1) {
2360 switch (it[0].getStmtClass()) {
2361 .ReturnStmtClass => {
2362 const result = try transStmt(c, &block.base, it[0], .unused);
2363 try block.statements.append(result);
2364 return;
2365 },
2366 .BreakStmtClass => return,
2367 .CaseStmtClass => {
2368 var sub = @ptrCast(*const clang.CaseStmt, it[0]).getSubStmt();
2369 while (true) switch (sub.getStmtClass()) {
2370 .CaseStmtClass => sub = @ptrCast(*const clang.CaseStmt, sub).getSubStmt(),
2371 .DefaultStmtClass => sub = @ptrCast(*const clang.DefaultStmt, sub).getSubStmt(),
2372 else => break,
2373 };
2374 const result = try transStmt(c, &block.base, sub, .unused);
2375 assert(result.tag() != .declaration);
2376 try block.statements.append(result);
2377 if (result.isNoreturn(true)) {
2378 return;
2379 }
2380 },
2381 .DefaultStmtClass => {
2382 var sub = @ptrCast(*const clang.DefaultStmt, it[0]).getSubStmt();
2383 while (true) switch (sub.getStmtClass()) {
2384 .CaseStmtClass => sub = @ptrCast(*const clang.CaseStmt, sub).getSubStmt(),
2385 .DefaultStmtClass => sub = @ptrCast(*const clang.DefaultStmt, sub).getSubStmt(),
2386 else => break,
2387 };
2388 const result = try transStmt(c, &block.base, sub, .unused);
2389 assert(result.tag() != .declaration);
2390 try block.statements.append(result);
2391 if (result.isNoreturn(true)) {
2392 return;
2393 }
2394 },
2395 .CompoundStmtClass => {
2396 const result = try transCompoundStmt(c, &block.base, @ptrCast(*const clang.CompoundStmt, it[0]));
2397 try block.statements.append(result);
2398 if (result.isNoreturn(true)) {
2399 return;
2400 }
2401 },
2402 else => {
2403 const result = try transStmt(c, &block.base, it[0], .unused);
2404 switch (result.tag()) {
2405 .declaration, .empty_block => {},
2406 else => try block.statements.append(result),
2407 }
2408 },
2409 }
2410 }
2411 return;
30602412}
30612413
3062fn transConstantExpr(rp: RestorePoint, scope: *Scope, expr: *const clang.Expr, used: ResultUsed) TransError!*ast.Node {
2414fn transConstantExpr(c: *Context, scope: *Scope, expr: *const clang.Expr, used: ResultUsed) TransError!Node {
30632415 var result: clang.ExprEvalResult = undefined;
3064 if (!expr.EvaluateAsConstantExpr(&result, .EvaluateForCodeGen, rp.c.clang_context))
3065 return revertAndWarn(rp, error.UnsupportedTranslation, expr.getBeginLoc(), "invalid constant expression", .{});
2416 if (!expr.evaluateAsConstantExpr(&result, .EvaluateForCodeGen, c.clang_context))
2417 return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "invalid constant expression", .{});
30662418
3067 var val_node: ?*ast.Node = null;
30682419 switch (result.Val.getKind()) {
30692420 .Int => {
30702421 // See comment in `transIntegerLiteral` for why this code is here.
30712422 // @as(T, x)
30722423 const expr_base = @ptrCast(*const clang.Expr, expr);
3073 const as_node = try rp.c.createBuiltinCall("@as", 2);
3074 const ty_node = try transQualType(rp, expr_base.getType(), expr_base.getBeginLoc());
3075 as_node.params()[0] = ty_node;
3076 _ = try appendToken(rp.c, .Comma, ",");
3077
3078 const int_lit_node = try transCreateNodeAPInt(rp.c, result.Val.getInt());
3079 as_node.params()[1] = int_lit_node;
3080
3081 as_node.rparen_token = try appendToken(rp.c, .RParen, ")");
3082
3083 return maybeSuppressResult(rp, scope, used, &as_node.base);
2424 const as_node = try Tag.as.create(c.arena, .{
2425 .lhs = try transQualType(c, scope, expr_base.getType(), expr_base.getBeginLoc()),
2426 .rhs = try transCreateNodeAPInt(c, result.Val.getInt()),
2427 });
2428 return maybeSuppressResult(c, scope, used, as_node);
30842429 },
30852430 else => {
3086 return revertAndWarn(rp, error.UnsupportedTranslation, expr.getBeginLoc(), "unsupported constant expression kind", .{});
2431 return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "unsupported constant expression kind", .{});
30872432 },
30882433 }
30892434}
30902435
3091fn transPredefinedExpr(rp: RestorePoint, scope: *Scope, expr: *const clang.PredefinedExpr, used: ResultUsed) TransError!*ast.Node {
3092 return transStringLiteral(rp, scope, expr.getFunctionName(), used);
2436fn transPredefinedExpr(c: *Context, scope: *Scope, expr: *const clang.PredefinedExpr, used: ResultUsed) TransError!Node {
2437 return transStringLiteral(c, scope, expr.getFunctionName(), used);
30932438}
30942439
3095fn transCreateCharLitNode(c: *Context, narrow: bool, val: u32) TransError!*ast.Node {
3096 const node = try c.arena.create(ast.Node.OneToken);
3097 node.* = .{
3098 .base = .{ .tag = .CharLiteral },
3099 .token = undefined,
3100 };
3101 if (narrow) {
3102 const val_array = [_]u8{@intCast(u8, val)};
3103 node.token = try appendTokenFmt(c, .CharLiteral, "'{}'", .{std.zig.fmtEscapes(&val_array)});
3104 } else {
3105 node.token = try appendTokenFmt(c, .CharLiteral, "'\\u{{{x}}}'", .{val});
3106 }
3107 return &node.base;
2440fn transCreateCharLitNode(c: *Context, narrow: bool, val: u32) TransError!Node {
2441 return Tag.char_literal.create(c.arena, if (narrow)
2442 try std.fmt.allocPrint(c.arena, "'{s}'", .{std.zig.fmtEscapes(&.{@intCast(u8, val)})})
2443 else
2444 try std.fmt.allocPrint(c.arena, "'\\u{{{x}}}'", .{val}));
31082445}
31092446
31102447fn transCharLiteral(
3111 rp: RestorePoint,
2448 c: *Context,
31122449 scope: *Scope,
31132450 stmt: *const clang.CharacterLiteral,
31142451 result_used: ResultUsed,
31152452 suppress_as: SuppressCast,
3116) TransError!*ast.Node {
2453) TransError!Node {
31172454 const kind = stmt.getKind();
31182455 const val = stmt.getValue();
31192456 const narrow = kind == .Ascii or kind == .UTF8;
31202457 // C has a somewhat obscure feature called multi-character character constant
31212458 // e.g. 'abcd'
31222459 const int_lit_node = if (kind == .Ascii and val > 255)
3123 try transCreateNodeInt(rp.c, val)
2460 try transCreateNodeNumber(c, val, .int)
31242461 else
3125 try transCreateCharLitNode(rp.c, narrow, val);
2462 try transCreateCharLitNode(c, narrow, val);
31262463
31272464 if (suppress_as == .no_as) {
3128 return maybeSuppressResult(rp, scope, result_used, int_lit_node);
2465 return maybeSuppressResult(c, scope, result_used, int_lit_node);
31292466 }
31302467 // See comment in `transIntegerLiteral` for why this code is here.
31312468 // @as(T, x)
31322469 const expr_base = @ptrCast(*const clang.Expr, stmt);
3133 const as_node = try rp.c.createBuiltinCall("@as", 2);
3134 const ty_node = try transQualType(rp, expr_base.getType(), expr_base.getBeginLoc());
3135 as_node.params()[0] = ty_node;
3136 _ = try appendToken(rp.c, .Comma, ",");
3137 as_node.params()[1] = int_lit_node;
3138
3139 as_node.rparen_token = try appendToken(rp.c, .RParen, ")");
3140 return maybeSuppressResult(rp, scope, result_used, &as_node.base);
2470 const as_node = try Tag.as.create(c.arena, .{
2471 .lhs = try transQualType(c, scope, expr_base.getType(), expr_base.getBeginLoc()),
2472 .rhs = int_lit_node,
2473 });
2474 return maybeSuppressResult(c, scope, result_used, as_node);
31412475}
31422476
3143fn transStmtExpr(rp: RestorePoint, scope: *Scope, stmt: *const clang.StmtExpr, used: ResultUsed) TransError!*ast.Node {
2477fn transStmtExpr(c: *Context, scope: *Scope, stmt: *const clang.StmtExpr, used: ResultUsed) TransError!Node {
31442478 const comp = stmt.getSubStmt();
31452479 if (used == .unused) {
3146 return transCompoundStmt(rp, scope, comp);
2480 return transCompoundStmt(c, scope, comp);
31472481 }
3148 const lparen = try appendToken(rp.c, .LParen, "(");
3149 var block_scope = try Scope.Block.init(rp.c, scope, true);
2482 var block_scope = try Scope.Block.init(c, scope, true);
31502483 defer block_scope.deinit();
31512484
31522485 var it = comp.body_begin();
31532486 const end_it = comp.body_end();
31542487 while (it != end_it - 1) : (it += 1) {
3155 const result = try transStmt(rp, &block_scope.base, it[0], .unused, .r_value);
3156 try block_scope.statements.append(result);
2488 const result = try transStmt(c, &block_scope.base, it[0], .unused);
2489 switch (result.tag()) {
2490 .declaration, .empty_block => {},
2491 else => try block_scope.statements.append(result),
2492 }
31572493 }
3158 const break_node = blk: {
3159 var tmp = try CtrlFlow.init(rp.c, .Break, "blk");
3160 const rhs = try transStmt(rp, &block_scope.base, it[0], .used, .r_value);
3161 break :blk try tmp.finish(rhs);
3162 };
3163 _ = try appendToken(rp.c, .Semicolon, ";");
3164 try block_scope.statements.append(&break_node.base);
3165 const block_node = try block_scope.complete(rp.c);
3166 const rparen = try appendToken(rp.c, .RParen, ")");
3167 const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
3168 grouped_expr.* = .{
3169 .lparen = lparen,
3170 .expr = block_node,
3171 .rparen = rparen,
3172 };
3173 return maybeSuppressResult(rp, scope, used, &grouped_expr.base);
2494 const break_node = try Tag.break_val.create(c.arena, .{
2495 .label = block_scope.label,
2496 .val = try transStmt(c, &block_scope.base, it[0], .used),
2497 });
2498 try block_scope.statements.append(break_node);
2499 const res = try block_scope.complete(c);
2500 return maybeSuppressResult(c, scope, used, res);
31742501}
31752502
3176fn transMemberExpr(rp: RestorePoint, scope: *Scope, stmt: *const clang.MemberExpr, result_used: ResultUsed) TransError!*ast.Node {
3177 var container_node = try transExpr(rp, scope, stmt.getBase(), .used, .r_value);
2503fn transMemberExpr(c: *Context, scope: *Scope, stmt: *const clang.MemberExpr, result_used: ResultUsed) TransError!Node {
2504 var container_node = try transExpr(c, scope, stmt.getBase(), .used);
31782505
31792506 if (stmt.isArrow()) {
3180 container_node = try transCreateNodePtrDeref(rp.c, container_node);
2507 container_node = try Tag.deref.create(c.arena, container_node);
31812508 }
31822509
31832510 const member_decl = stmt.getMemberDecl();
......@@ -3188,19 +2515,18 @@ fn transMemberExpr(rp: RestorePoint, scope: *Scope, stmt: *const clang.MemberExp
31882515 if (decl_kind == .Field) {
31892516 const field_decl = @ptrCast(*const clang.FieldDecl, member_decl);
31902517 if (field_decl.isAnonymousStructOrUnion()) {
3191 const name = rp.c.decl_table.get(@ptrToInt(field_decl.getCanonicalDecl())).?;
3192 break :blk try mem.dupe(rp.c.arena, u8, name);
2518 const name = c.decl_table.get(@ptrToInt(field_decl.getCanonicalDecl())).?;
2519 break :blk try mem.dupe(c.arena, u8, name);
31932520 }
31942521 }
31952522 const decl = @ptrCast(*const clang.NamedDecl, member_decl);
3196 break :blk try rp.c.str(decl.getName_bytes_begin());
2523 break :blk try c.str(decl.getName_bytes_begin());
31972524 };
3198
3199 const node = try transCreateNodeFieldAccess(rp.c, container_node, name);
3200 return maybeSuppressResult(rp, scope, result_used, node);
2525 const node = try Tag.field_access.create(c.arena, .{ .lhs = container_node, .field_name = name });
2526 return maybeSuppressResult(c, scope, result_used, node);
32012527}
32022528
3203fn transArrayAccess(rp: RestorePoint, scope: *Scope, stmt: *const clang.ArraySubscriptExpr, result_used: ResultUsed) TransError!*ast.Node {
2529fn transArrayAccess(c: *Context, scope: *Scope, stmt: *const clang.ArraySubscriptExpr, result_used: ResultUsed) TransError!Node {
32042530 var base_stmt = stmt.getBase();
32052531
32062532 // Unwrap the base statement if it's an array decayed to a bare pointer type
......@@ -3213,30 +2539,26 @@ fn transArrayAccess(rp: RestorePoint, scope: *Scope, stmt: *const clang.ArraySub
32132539 }
32142540 }
32152541
3216 const container_node = try transExpr(rp, scope, base_stmt, .used, .r_value);
3217 const node = try transCreateNodeArrayAccess(rp.c, container_node);
2542 const container_node = try transExpr(c, scope, base_stmt, .used);
32182543
32192544 // cast if the index is long long or signed
32202545 const subscr_expr = stmt.getIdx();
3221 const qt = getExprQualType(rp.c, subscr_expr);
2546 const qt = getExprQualType(c, subscr_expr);
32222547 const is_longlong = cIsLongLongInteger(qt);
32232548 const is_signed = cIsSignedInteger(qt);
32242549
3225 if (is_longlong or is_signed) {
3226 const cast_node = try rp.c.createBuiltinCall("@intCast", 2);
2550 const rhs = if (is_longlong or is_signed) blk: {
32272551 // check if long long first so that signed long long doesn't just become unsigned long long
3228 var typeid_node = if (is_longlong) try transCreateNodeIdentifier(rp.c, "usize") else try transQualTypeIntWidthOf(rp.c, qt, false);
3229 cast_node.params()[0] = typeid_node;
3230 _ = try appendToken(rp.c, .Comma, ",");
3231 cast_node.params()[1] = try transExpr(rp, scope, subscr_expr, .used, .r_value);
3232 cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
3233 node.rtoken = try appendToken(rp.c, .RBrace, "]");
3234 node.index_expr = &cast_node.base;
3235 } else {
3236 node.index_expr = try transExpr(rp, scope, subscr_expr, .used, .r_value);
3237 node.rtoken = try appendToken(rp.c, .RBrace, "]");
3238 }
3239 return maybeSuppressResult(rp, scope, result_used, &node.base);
2552 var typeid_node = if (is_longlong) try Tag.identifier.create(c.arena, "usize") else try transQualTypeIntWidthOf(c, qt, false);
2553 break :blk try Tag.int_cast.create(c.arena, .{ .lhs = typeid_node, .rhs = try transExpr(c, scope, subscr_expr, .used) });
2554 } else
2555 try transExpr(c, scope, subscr_expr, .used);
2556
2557 const node = try Tag.array_access.create(c.arena, .{
2558 .lhs = container_node,
2559 .rhs = rhs,
2560 });
2561 return maybeSuppressResult(c, scope, result_used, node);
32402562}
32412563
32422564/// Check if an expression is ultimately a reference to a function declaration
......@@ -3271,29 +2593,25 @@ fn cIsFunctionDeclRef(expr: *const clang.Expr) bool {
32712593 }
32722594}
32732595
3274fn transCallExpr(rp: RestorePoint, scope: *Scope, stmt: *const clang.CallExpr, result_used: ResultUsed) TransError!*ast.Node {
2596fn transCallExpr(c: *Context, scope: *Scope, stmt: *const clang.CallExpr, result_used: ResultUsed) TransError!Node {
32752597 const callee = stmt.getCallee();
3276 var raw_fn_expr = try transExpr(rp, scope, callee, .used, .r_value);
2598 var raw_fn_expr = try transExpr(c, scope, callee, .used);
32772599
32782600 var is_ptr = false;
32792601 const fn_ty = qualTypeGetFnProto(callee.getType(), &is_ptr);
32802602
32812603 const fn_expr = if (is_ptr and fn_ty != null and !cIsFunctionDeclRef(callee))
3282 try transCreateNodeUnwrapNull(rp.c, raw_fn_expr)
2604 try Tag.unwrap.create(c.arena, raw_fn_expr)
32832605 else
32842606 raw_fn_expr;
32852607
32862608 const num_args = stmt.getNumArgs();
3287 const node = try rp.c.createCall(fn_expr, num_args);
3288 const call_params = node.params();
2609 const args = try c.arena.alloc(Node, num_args);
32892610
3290 const args = stmt.getArgs();
2611 const c_args = stmt.getArgs();
32912612 var i: usize = 0;
32922613 while (i < num_args) : (i += 1) {
3293 if (i != 0) {
3294 _ = try appendToken(rp.c, .Comma, ",");
3295 }
3296 var call_param = try transExpr(rp, scope, args[i], .used, .r_value);
2614 var arg = try transExpr(c, scope, c_args[i], .used);
32972615
32982616 // In C the result type of a boolean expression is int. If this result is passed as
32992617 // an argument to a function whose parameter is also int, there is no cast. Therefore
......@@ -3304,31 +2622,26 @@ fn transCallExpr(rp: RestorePoint, scope: *Scope, stmt: *const clang.CallExpr, r
33042622 const param_count = fn_proto.getNumParams();
33052623 if (i < param_count) {
33062624 const param_qt = fn_proto.getParamType(@intCast(c_uint, i));
3307 if (isBoolRes(call_param) and cIsNativeInt(param_qt)) {
3308 const builtin_node = try rp.c.createBuiltinCall("@boolToInt", 1);
3309 builtin_node.params()[0] = call_param;
3310 builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
3311 call_param = &builtin_node.base;
2625 if (isBoolRes(arg) and cIsNativeInt(param_qt)) {
2626 arg = try Tag.bool_to_int.create(c.arena, arg);
33122627 }
33132628 }
33142629 },
33152630 else => {},
33162631 }
33172632 }
3318 call_params[i] = call_param;
2633 args[i] = arg;
33192634 }
3320 node.rtoken = try appendToken(rp.c, .RParen, ")");
3321
2635 const node = try Tag.call.create(c.arena, .{ .lhs = fn_expr, .args = args });
33222636 if (fn_ty) |ty| {
33232637 const canon = ty.getReturnType().getCanonicalType();
33242638 const ret_ty = canon.getTypePtr();
33252639 if (ret_ty.isVoidType()) {
3326 _ = try appendToken(rp.c, .Semicolon, ";");
3327 return &node.base;
2640 return node;
33282641 }
33292642 }
33302643
3331 return maybeSuppressResult(rp, scope, result_used, &node.base);
2644 return maybeSuppressResult(c, scope, result_used, node);
33322645}
33332646
33342647const ClangFunctionType = union(enum) {
......@@ -3363,38 +2676,29 @@ fn qualTypeGetFnProto(qt: clang.QualType, is_ptr: *bool) ?ClangFunctionType {
33632676}
33642677
33652678fn transUnaryExprOrTypeTraitExpr(
3366 rp: RestorePoint,
2679 c: *Context,
33672680 scope: *Scope,
33682681 stmt: *const clang.UnaryExprOrTypeTraitExpr,
33692682 result_used: ResultUsed,
3370) TransError!*ast.Node {
2683) TransError!Node {
33712684 const loc = stmt.getBeginLoc();
3372 const type_node = try transQualType(
3373 rp,
3374 stmt.getTypeOfArgument(),
3375 loc,
3376 );
2685 const type_node = try transQualType(c, scope, stmt.getTypeOfArgument(), loc);
33772686
33782687 const kind = stmt.getKind();
3379 const kind_str = switch (kind) {
3380 .SizeOf => "@sizeOf",
3381 .AlignOf => "@alignOf",
2688 switch (kind) {
2689 .SizeOf => return Tag.sizeof.create(c.arena, type_node),
2690 .AlignOf => return Tag.alignof.create(c.arena, type_node),
33822691 .PreferredAlignOf,
33832692 .VecStep,
33842693 .OpenMPRequiredSimdAlign,
3385 => return revertAndWarn(
3386 rp,
2694 => return fail(
2695 c,
33872696 error.UnsupportedTranslation,
33882697 loc,
33892698 "Unsupported type trait kind {}",
33902699 .{kind},
33912700 ),
3392 };
3393
3394 const builtin_node = try rp.c.createBuiltinCall(kind_str, 1);
3395 builtin_node.params()[0] = type_node;
3396 builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
3397 return maybeSuppressResult(rp, scope, result_used, &builtin_node.base);
2701 }
33982702}
33992703
34002704fn qualTypeHasWrappingOverflow(qt: clang.QualType) bool {
......@@ -3407,95 +2711,79 @@ fn qualTypeHasWrappingOverflow(qt: clang.QualType) bool {
34072711 }
34082712}
34092713
3410fn transUnaryOperator(rp: RestorePoint, scope: *Scope, stmt: *const clang.UnaryOperator, used: ResultUsed) TransError!*ast.Node {
2714fn transUnaryOperator(c: *Context, scope: *Scope, stmt: *const clang.UnaryOperator, used: ResultUsed) TransError!Node {
34112715 const op_expr = stmt.getSubExpr();
34122716 switch (stmt.getOpcode()) {
34132717 .PostInc => if (qualTypeHasWrappingOverflow(stmt.getType()))
3414 return transCreatePostCrement(rp, scope, stmt, .AssignAddWrap, .PlusPercentEqual, "+%=", used)
2718 return transCreatePostCrement(c, scope, stmt, .add_wrap_assign, used)
34152719 else
3416 return transCreatePostCrement(rp, scope, stmt, .AssignAdd, .PlusEqual, "+=", used),
2720 return transCreatePostCrement(c, scope, stmt, .add_assign, used),
34172721 .PostDec => if (qualTypeHasWrappingOverflow(stmt.getType()))
3418 return transCreatePostCrement(rp, scope, stmt, .AssignSubWrap, .MinusPercentEqual, "-%=", used)
2722 return transCreatePostCrement(c, scope, stmt, .sub_wrap_assign, used)
34192723 else
3420 return transCreatePostCrement(rp, scope, stmt, .AssignSub, .MinusEqual, "-=", used),
2724 return transCreatePostCrement(c, scope, stmt, .sub_assign, used),
34212725 .PreInc => if (qualTypeHasWrappingOverflow(stmt.getType()))
3422 return transCreatePreCrement(rp, scope, stmt, .AssignAddWrap, .PlusPercentEqual, "+%=", used)
2726 return transCreatePreCrement(c, scope, stmt, .add_wrap_assign, used)
34232727 else
3424 return transCreatePreCrement(rp, scope, stmt, .AssignAdd, .PlusEqual, "+=", used),
2728 return transCreatePreCrement(c, scope, stmt, .add_assign, used),
34252729 .PreDec => if (qualTypeHasWrappingOverflow(stmt.getType()))
3426 return transCreatePreCrement(rp, scope, stmt, .AssignSubWrap, .MinusPercentEqual, "-%=", used)
2730 return transCreatePreCrement(c, scope, stmt, .sub_wrap_assign, used)
34272731 else
3428 return transCreatePreCrement(rp, scope, stmt, .AssignSub, .MinusEqual, "-=", used),
2732 return transCreatePreCrement(c, scope, stmt, .sub_assign, used),
34292733 .AddrOf => {
34302734 if (cIsFunctionDeclRef(op_expr)) {
3431 return transExpr(rp, scope, op_expr, used, .r_value);
2735 return transExpr(c, scope, op_expr, used);
34322736 }
3433 const op_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
3434 op_node.rhs = try transExpr(rp, scope, op_expr, used, .r_value);
3435 return &op_node.base;
2737 return Tag.address_of.create(c.arena, try transExpr(c, scope, op_expr, used));
34362738 },
34372739 .Deref => {
3438 const value_node = try transExpr(rp, scope, op_expr, used, .r_value);
2740 const node = try transExpr(c, scope, op_expr, used);
34392741 var is_ptr = false;
34402742 const fn_ty = qualTypeGetFnProto(op_expr.getType(), &is_ptr);
34412743 if (fn_ty != null and is_ptr)
3442 return value_node;
3443 const unwrapped = try transCreateNodeUnwrapNull(rp.c, value_node);
3444 return transCreateNodePtrDeref(rp.c, unwrapped);
2744 return node;
2745 const unwrapped = try Tag.unwrap.create(c.arena, node);
2746 return Tag.deref.create(c.arena, unwrapped);
34452747 },
3446 .Plus => return transExpr(rp, scope, op_expr, used, .r_value),
2748 .Plus => return transExpr(c, scope, op_expr, used),
34472749 .Minus => {
34482750 if (!qualTypeHasWrappingOverflow(op_expr.getType())) {
3449 const op_node = try transCreateNodeSimplePrefixOp(rp.c, .Negation, .Minus, "-");
3450 op_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
3451 return &op_node.base;
2751 return Tag.negate.create(c.arena, try transExpr(c, scope, op_expr, .used));
34522752 } else if (cIsUnsignedInteger(op_expr.getType())) {
3453 // we gotta emit 0 -% x
3454 const zero = try transCreateNodeInt(rp.c, 0);
3455 const token = try appendToken(rp.c, .MinusPercent, "-%");
3456 const expr = try transExpr(rp, scope, op_expr, .used, .r_value);
3457 return transCreateNodeInfixOp(rp, scope, zero, .SubWrap, token, expr, used, true);
2753 // use -% x for unsigned integers
2754 return Tag.negate_wrap.create(c.arena, try transExpr(c, scope, op_expr, .used));
34582755 } else
3459 return revertAndWarn(rp, error.UnsupportedTranslation, stmt.getBeginLoc(), "C negation with non float non integer", .{});
2756 return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "C negation with non float non integer", .{});
34602757 },
34612758 .Not => {
3462 const op_node = try transCreateNodeSimplePrefixOp(rp.c, .BitNot, .Tilde, "~");
3463 op_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
3464 return &op_node.base;
2759 return Tag.bit_not.create(c.arena, try transExpr(c, scope, op_expr, .used));
34652760 },
34662761 .LNot => {
3467 const op_node = try transCreateNodeSimplePrefixOp(rp.c, .BoolNot, .Bang, "!");
3468 op_node.rhs = try transBoolExpr(rp, scope, op_expr, .used, .r_value, true);
3469 return &op_node.base;
2762 return Tag.not.create(c.arena, try transBoolExpr(c, scope, op_expr, .used));
34702763 },
34712764 .Extension => {
3472 return transExpr(rp, scope, stmt.getSubExpr(), used, .l_value);
2765 return transExpr(c, scope, stmt.getSubExpr(), used);
34732766 },
3474 else => return revertAndWarn(rp, error.UnsupportedTranslation, stmt.getBeginLoc(), "unsupported C translation {}", .{stmt.getOpcode()}),
2767 else => return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "unsupported C translation {}", .{stmt.getOpcode()}),
34752768 }
34762769}
34772770
34782771fn transCreatePreCrement(
3479 rp: RestorePoint,
2772 c: *Context,
34802773 scope: *Scope,
34812774 stmt: *const clang.UnaryOperator,
3482 op: ast.Node.Tag,
3483 op_tok_id: std.zig.Token.Id,
3484 bytes: []const u8,
2775 op: Tag,
34852776 used: ResultUsed,
3486) TransError!*ast.Node {
2777) TransError!Node {
34872778 const op_expr = stmt.getSubExpr();
34882779
34892780 if (used == .unused) {
34902781 // common case
34912782 // c: ++expr
34922783 // zig: expr += 1
3493 const expr = try transExpr(rp, scope, op_expr, .used, .r_value);
3494 const token = try appendToken(rp.c, op_tok_id, bytes);
3495 const one = try transCreateNodeInt(rp.c, 1);
3496 if (scope.id != .Condition)
3497 _ = try appendToken(rp.c, .Semicolon, ";");
3498 return transCreateNodeInfixOp(rp, scope, expr, op, token, one, .used, false);
2784 const lhs = try transExpr(c, scope, op_expr, .used);
2785 const rhs = Tag.one_literal.init();
2786 return transCreateNodeInfixOp(c, scope, op, lhs, rhs, .used);
34992787 }
35002788 // worst case
35012789 // c: ++expr
......@@ -3504,71 +2792,44 @@ fn transCreatePreCrement(
35042792 // zig: _ref.* += 1;
35052793 // zig: break :blk _ref.*
35062794 // zig: })
3507 var block_scope = try Scope.Block.init(rp.c, scope, true);
2795 var block_scope = try Scope.Block.init(c, scope, true);
35082796 defer block_scope.deinit();
3509 const ref = try block_scope.makeMangledName(rp.c, "ref");
3510
3511 const mut_tok = try appendToken(rp.c, .Keyword_const, "const");
3512 const name_tok = try appendIdentifier(rp.c, ref);
3513 const eq_token = try appendToken(rp.c, .Equal, "=");
3514 const rhs_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
3515 rhs_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
3516 const init_node = &rhs_node.base;
3517 const semicolon_token = try appendToken(rp.c, .Semicolon, ";");
3518 const node = try ast.Node.VarDecl.create(rp.c.arena, .{
3519 .name_token = name_tok,
3520 .mut_token = mut_tok,
3521 .semicolon_token = semicolon_token,
3522 }, .{
3523 .eq_token = eq_token,
3524 .init_node = init_node,
3525 });
3526 try block_scope.statements.append(&node.base);
3527
3528 const lhs_node = try transCreateNodeIdentifier(rp.c, ref);
3529 const ref_node = try transCreateNodePtrDeref(rp.c, lhs_node);
3530 _ = try appendToken(rp.c, .Semicolon, ";");
3531 const token = try appendToken(rp.c, op_tok_id, bytes);
3532 const one = try transCreateNodeInt(rp.c, 1);
3533 _ = try appendToken(rp.c, .Semicolon, ";");
3534 const assign = try transCreateNodeInfixOp(rp, scope, ref_node, op, token, one, .used, false);
3535 try block_scope.statements.append(assign);
2797 const ref = try block_scope.makeMangledName(c, "ref");
35362798
3537 const break_node = try transCreateNodeBreak(rp.c, block_scope.label, ref_node);
3538 try block_scope.statements.append(&break_node.base);
3539 const block_node = try block_scope.complete(rp.c);
3540 // semicolon must immediately follow rbrace because it is the last token in a block
3541 _ = try appendToken(rp.c, .Semicolon, ";");
3542 const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
3543 grouped_expr.* = .{
3544 .lparen = try appendToken(rp.c, .LParen, "("),
3545 .expr = block_node,
3546 .rparen = try appendToken(rp.c, .RParen, ")"),
3547 };
3548 return &grouped_expr.base;
2799 const expr = try transExpr(c, &block_scope.base, op_expr, .used);
2800 const addr_of = try Tag.address_of.create(c.arena, expr);
2801 const ref_decl = try Tag.var_simple.create(c.arena, .{ .name = ref, .init = addr_of });
2802 try block_scope.statements.append(ref_decl);
2803
2804 const lhs_node = try Tag.identifier.create(c.arena, ref);
2805 const ref_node = try Tag.deref.create(c.arena, lhs_node);
2806 const node = try transCreateNodeInfixOp(c, &block_scope.base, op, ref_node, Tag.one_literal.init(), .used);
2807 try block_scope.statements.append(node);
2808
2809 const break_node = try Tag.break_val.create(c.arena, .{
2810 .label = block_scope.label,
2811 .val = ref_node,
2812 });
2813 try block_scope.statements.append(break_node);
2814 return block_scope.complete(c);
35492815}
35502816
35512817fn transCreatePostCrement(
3552 rp: RestorePoint,
2818 c: *Context,
35532819 scope: *Scope,
35542820 stmt: *const clang.UnaryOperator,
3555 op: ast.Node.Tag,
3556 op_tok_id: std.zig.Token.Id,
3557 bytes: []const u8,
2821 op: Tag,
35582822 used: ResultUsed,
3559) TransError!*ast.Node {
2823) TransError!Node {
35602824 const op_expr = stmt.getSubExpr();
35612825
35622826 if (used == .unused) {
35632827 // common case
3564 // c: ++expr
2828 // c: expr++
35652829 // zig: expr += 1
3566 const expr = try transExpr(rp, scope, op_expr, .used, .r_value);
3567 const token = try appendToken(rp.c, op_tok_id, bytes);
3568 const one = try transCreateNodeInt(rp.c, 1);
3569 if (scope.id != .Condition)
3570 _ = try appendToken(rp.c, .Semicolon, ";");
3571 return transCreateNodeInfixOp(rp, scope, expr, op, token, one, .used, false);
2830 const lhs = try transExpr(c, scope, op_expr, .used);
2831 const rhs = Tag.one_literal.init();
2832 return transCreateNodeInfixOp(c, scope, op, lhs, rhs, .used);
35722833 }
35732834 // worst case
35742835 // c: expr++
......@@ -3578,93 +2839,56 @@ fn transCreatePostCrement(
35782839 // zig: _ref.* += 1;
35792840 // zig: break :blk _tmp
35802841 // zig: })
3581 var block_scope = try Scope.Block.init(rp.c, scope, true);
2842 var block_scope = try Scope.Block.init(c, scope, true);
35822843 defer block_scope.deinit();
3583 const ref = try block_scope.makeMangledName(rp.c, "ref");
3584
3585 const mut_tok = try appendToken(rp.c, .Keyword_const, "const");
3586 const name_tok = try appendIdentifier(rp.c, ref);
3587 const eq_token = try appendToken(rp.c, .Equal, "=");
3588 const rhs_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
3589 rhs_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
3590 const init_node = &rhs_node.base;
3591 const semicolon_token = try appendToken(rp.c, .Semicolon, ";");
3592 const node = try ast.Node.VarDecl.create(rp.c.arena, .{
3593 .name_token = name_tok,
3594 .mut_token = mut_tok,
3595 .semicolon_token = semicolon_token,
3596 }, .{
3597 .eq_token = eq_token,
3598 .init_node = init_node,
3599 });
3600 try block_scope.statements.append(&node.base);
3601
3602 const lhs_node = try transCreateNodeIdentifier(rp.c, ref);
3603 const ref_node = try transCreateNodePtrDeref(rp.c, lhs_node);
3604 _ = try appendToken(rp.c, .Semicolon, ";");
3605
3606 const tmp = try block_scope.makeMangledName(rp.c, "tmp");
3607 const tmp_mut_tok = try appendToken(rp.c, .Keyword_const, "const");
3608 const tmp_name_tok = try appendIdentifier(rp.c, tmp);
3609 const tmp_eq_token = try appendToken(rp.c, .Equal, "=");
3610 const tmp_init_node = ref_node;
3611 const tmp_semicolon_token = try appendToken(rp.c, .Semicolon, ";");
3612 const tmp_node = try ast.Node.VarDecl.create(rp.c.arena, .{
3613 .name_token = tmp_name_tok,
3614 .mut_token = tmp_mut_tok,
3615 .semicolon_token = semicolon_token,
3616 }, .{
3617 .eq_token = tmp_eq_token,
3618 .init_node = tmp_init_node,
3619 });
3620 try block_scope.statements.append(&tmp_node.base);
2844 const ref = try block_scope.makeMangledName(c, "ref");
36212845
3622 const token = try appendToken(rp.c, op_tok_id, bytes);
3623 const one = try transCreateNodeInt(rp.c, 1);
3624 _ = try appendToken(rp.c, .Semicolon, ";");
3625 const assign = try transCreateNodeInfixOp(rp, scope, ref_node, op, token, one, .used, false);
3626 try block_scope.statements.append(assign);
2846 const expr = try transExpr(c, &block_scope.base, op_expr, .used);
2847 const addr_of = try Tag.address_of.create(c.arena, expr);
2848 const ref_decl = try Tag.var_simple.create(c.arena, .{ .name = ref, .init = addr_of });
2849 try block_scope.statements.append(ref_decl);
36272850
3628 const break_node = blk: {
3629 var tmp_ctrl_flow = try CtrlFlow.initToken(rp.c, .Break, block_scope.label);
3630 const rhs = try transCreateNodeIdentifier(rp.c, tmp);
3631 break :blk try tmp_ctrl_flow.finish(rhs);
3632 };
3633 try block_scope.statements.append(&break_node.base);
3634 _ = try appendToken(rp.c, .Semicolon, ";");
3635 const block_node = try block_scope.complete(rp.c);
3636 const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
3637 grouped_expr.* = .{
3638 .lparen = try appendToken(rp.c, .LParen, "("),
3639 .expr = block_node,
3640 .rparen = try appendToken(rp.c, .RParen, ")"),
3641 };
3642 return &grouped_expr.base;
2851 const lhs_node = try Tag.identifier.create(c.arena, ref);
2852 const ref_node = try Tag.deref.create(c.arena, lhs_node);
2853
2854 const tmp = try block_scope.makeMangledName(c, "tmp");
2855 const tmp_decl = try Tag.var_simple.create(c.arena, .{ .name = tmp, .init = ref_node });
2856 try block_scope.statements.append(tmp_decl);
2857
2858 const node = try transCreateNodeInfixOp(c, &block_scope.base, op, ref_node, Tag.one_literal.init(), .used);
2859 try block_scope.statements.append(node);
2860
2861 const break_node = try Tag.break_val.create(c.arena, .{
2862 .label = block_scope.label,
2863 .val = try Tag.identifier.create(c.arena, tmp),
2864 });
2865 try block_scope.statements.append(break_node);
2866 return block_scope.complete(c);
36432867}
36442868
3645fn transCompoundAssignOperator(rp: RestorePoint, scope: *Scope, stmt: *const clang.CompoundAssignOperator, used: ResultUsed) TransError!*ast.Node {
2869fn transCompoundAssignOperator(c: *Context, scope: *Scope, stmt: *const clang.CompoundAssignOperator, used: ResultUsed) TransError!Node {
36462870 switch (stmt.getOpcode()) {
36472871 .MulAssign => if (qualTypeHasWrappingOverflow(stmt.getType()))
3648 return transCreateCompoundAssign(rp, scope, stmt, .AssignMulWrap, .AsteriskPercentEqual, "*%=", .MulWrap, .AsteriskPercent, "*%", used)
2872 return transCreateCompoundAssign(c, scope, stmt, .mul_wrap_assign, used)
36492873 else
3650 return transCreateCompoundAssign(rp, scope, stmt, .AssignMul, .AsteriskEqual, "*=", .Mul, .Asterisk, "*", used),
2874 return transCreateCompoundAssign(c, scope, stmt, .mul_assign, used),
36512875 .AddAssign => if (qualTypeHasWrappingOverflow(stmt.getType()))
3652 return transCreateCompoundAssign(rp, scope, stmt, .AssignAddWrap, .PlusPercentEqual, "+%=", .AddWrap, .PlusPercent, "+%", used)
2876 return transCreateCompoundAssign(c, scope, stmt, .add_wrap_assign, used)
36532877 else
3654 return transCreateCompoundAssign(rp, scope, stmt, .AssignAdd, .PlusEqual, "+=", .Add, .Plus, "+", used),
2878 return transCreateCompoundAssign(c, scope, stmt, .add_assign, used),
36552879 .SubAssign => if (qualTypeHasWrappingOverflow(stmt.getType()))
3656 return transCreateCompoundAssign(rp, scope, stmt, .AssignSubWrap, .MinusPercentEqual, "-%=", .SubWrap, .MinusPercent, "-%", used)
2880 return transCreateCompoundAssign(c, scope, stmt, .sub_wrap_assign, used)
36572881 else
3658 return transCreateCompoundAssign(rp, scope, stmt, .AssignSub, .MinusPercentEqual, "-=", .Sub, .Minus, "-", used),
3659 .DivAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignDiv, .SlashEqual, "/=", .Div, .Slash, "/", used),
3660 .RemAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignMod, .PercentEqual, "%=", .Mod, .Percent, "%", used),
3661 .ShlAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitShiftLeft, .AngleBracketAngleBracketLeftEqual, "<<=", .BitShiftLeft, .AngleBracketAngleBracketLeft, "<<", used),
3662 .ShrAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitShiftRight, .AngleBracketAngleBracketRightEqual, ">>=", .BitShiftRight, .AngleBracketAngleBracketRight, ">>", used),
3663 .AndAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitAnd, .AmpersandEqual, "&=", .BitAnd, .Ampersand, "&", used),
3664 .XorAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitXor, .CaretEqual, "^=", .BitXor, .Caret, "^", used),
3665 .OrAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitOr, .PipeEqual, "|=", .BitOr, .Pipe, "|", used),
3666 else => return revertAndWarn(
3667 rp,
2882 return transCreateCompoundAssign(c, scope, stmt, .sub_assign, used),
2883 .DivAssign => return transCreateCompoundAssign(c, scope, stmt, .div_assign, used),
2884 .RemAssign => return transCreateCompoundAssign(c, scope, stmt, .mod_assign, used),
2885 .ShlAssign => return transCreateCompoundAssign(c, scope, stmt, .shl_assign, used),
2886 .ShrAssign => return transCreateCompoundAssign(c, scope, stmt, .shr_assign, used),
2887 .AndAssign => return transCreateCompoundAssign(c, scope, stmt, .bit_and_assign, used),
2888 .XorAssign => return transCreateCompoundAssign(c, scope, stmt, .bit_xor_assign, used),
2889 .OrAssign => return transCreateCompoundAssign(c, scope, stmt, .bit_or_assign, used),
2890 else => return fail(
2891 c,
36682892 error.UnsupportedTranslation,
36692893 stmt.getBeginLoc(),
36702894 "unsupported C translation {}",
......@@ -3674,25 +2898,20 @@ fn transCompoundAssignOperator(rp: RestorePoint, scope: *Scope, stmt: *const cla
36742898}
36752899
36762900fn transCreateCompoundAssign(
3677 rp: RestorePoint,
2901 c: *Context,
36782902 scope: *Scope,
36792903 stmt: *const clang.CompoundAssignOperator,
3680 assign_op: ast.Node.Tag,
3681 assign_tok_id: std.zig.Token.Id,
3682 assign_bytes: []const u8,
3683 bin_op: ast.Node.Tag,
3684 bin_tok_id: std.zig.Token.Id,
3685 bin_bytes: []const u8,
2904 op: Tag,
36862905 used: ResultUsed,
3687) TransError!*ast.Node {
3688 const is_shift = bin_op == .BitShiftLeft or bin_op == .BitShiftRight;
3689 const is_div = bin_op == .Div;
3690 const is_mod = bin_op == .Mod;
2906) TransError!Node {
2907 const is_shift = op == .shl_assign or op == .shr_assign;
2908 const is_div = op == .div_assign;
2909 const is_mod = op == .mod_assign;
36912910 const lhs = stmt.getLHS();
36922911 const rhs = stmt.getRHS();
36932912 const loc = stmt.getBeginLoc();
3694 const lhs_qt = getExprQualType(rp.c, lhs);
3695 const rhs_qt = getExprQualType(rp.c, rhs);
2913 const lhs_qt = getExprQualType(c, lhs);
2914 const rhs_qt = getExprQualType(c, rhs);
36962915 const is_signed = cIsSignedInteger(lhs_qt);
36972916 const requires_int_cast = blk: {
36982917 const are_integers = cIsInteger(lhs_qt) and cIsInteger(rhs_qt);
......@@ -3704,146 +2923,100 @@ fn transCreateCompoundAssign(
37042923 // c: lhs += rhs
37052924 // zig: lhs += rhs
37062925 if ((is_mod or is_div) and is_signed) {
3707 const op_token = try appendToken(rp.c, .Equal, "=");
3708 const op_node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
3709 const builtin = if (is_mod) "@rem" else "@divTrunc";
3710 const builtin_node = try rp.c.createBuiltinCall(builtin, 2);
3711 const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value);
3712 builtin_node.params()[0] = lhs_node;
3713 _ = try appendToken(rp.c, .Comma, ",");
3714 builtin_node.params()[1] = try transExpr(rp, scope, rhs, .used, .r_value);
3715 builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
3716 op_node.* = .{
3717 .base = .{ .tag = .Assign },
3718 .op_token = op_token,
3719 .lhs = lhs_node,
3720 .rhs = &builtin_node.base,
3721 };
3722 _ = try appendToken(rp.c, .Semicolon, ";");
3723 return &op_node.base;
2926 const lhs_node = try transExpr(c, scope, lhs, .used);
2927 const rhs_node = try transExpr(c, scope, rhs, .used);
2928 const builtin = if (is_mod)
2929 try Tag.rem.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node })
2930 else
2931 try Tag.div_trunc.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node });
2932
2933 return transCreateNodeInfixOp(c, scope, .assign, lhs_node, builtin, .used);
37242934 }
37252935
3726 const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value);
3727 const eq_token = try appendToken(rp.c, assign_tok_id, assign_bytes);
2936 const lhs_node = try transExpr(c, scope, lhs, .used);
37282937 var rhs_node = if (is_shift or requires_int_cast)
3729 try transExprCoercing(rp, scope, rhs, .used, .r_value)
2938 try transExprCoercing(c, scope, rhs, .used)
37302939 else
3731 try transExpr(rp, scope, rhs, .used, .r_value);
2940 try transExpr(c, scope, rhs, .used);
37322941
37332942 if (is_shift or requires_int_cast) {
3734 const cast_node = try rp.c.createBuiltinCall("@intCast", 2);
2943 // @intCast(rhs)
37352944 const cast_to_type = if (is_shift)
3736 try qualTypeToLog2IntRef(rp, getExprQualType(rp.c, rhs), loc)
2945 try qualTypeToLog2IntRef(c, scope, getExprQualType(c, rhs), loc)
37372946 else
3738 try transQualType(rp, getExprQualType(rp.c, lhs), loc);
3739 cast_node.params()[0] = cast_to_type;
3740 _ = try appendToken(rp.c, .Comma, ",");
3741 cast_node.params()[1] = rhs_node;
3742 cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
3743 rhs_node = &cast_node.base;
2947 try transQualType(c, scope, getExprQualType(c, lhs), loc);
2948
2949 rhs_node = try Tag.int_cast.create(c.arena, .{ .lhs = cast_to_type, .rhs = rhs_node });
37442950 }
3745 if (scope.id != .Condition)
3746 _ = try appendToken(rp.c, .Semicolon, ";");
3747 return transCreateNodeInfixOp(rp, scope, lhs_node, assign_op, eq_token, rhs_node, .used, false);
2951
2952 return transCreateNodeInfixOp(c, scope, op, lhs_node, rhs_node, .used);
37482953 }
37492954 // worst case
37502955 // c: lhs += rhs
37512956 // zig: (blk: {
37522957 // zig: const _ref = &lhs;
3753 // zig: _ref.* = _ref.* + rhs;
2958 // zig: _ref.* += rhs;
37542959 // zig: break :blk _ref.*
37552960 // zig: })
3756 var block_scope = try Scope.Block.init(rp.c, scope, true);
2961 var block_scope = try Scope.Block.init(c, scope, true);
37572962 defer block_scope.deinit();
3758 const ref = try block_scope.makeMangledName(rp.c, "ref");
3759
3760 const mut_tok = try appendToken(rp.c, .Keyword_const, "const");
3761 const name_tok = try appendIdentifier(rp.c, ref);
3762 const eq_token = try appendToken(rp.c, .Equal, "=");
3763 const addr_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
3764 addr_node.rhs = try transExpr(rp, scope, lhs, .used, .l_value);
3765 const init_node = &addr_node.base;
3766 const semicolon_token = try appendToken(rp.c, .Semicolon, ";");
3767 const node = try ast.Node.VarDecl.create(rp.c.arena, .{
3768 .name_token = name_tok,
3769 .mut_token = mut_tok,
3770 .semicolon_token = semicolon_token,
3771 }, .{
3772 .eq_token = eq_token,
3773 .init_node = init_node,
3774 });
3775 try block_scope.statements.append(&node.base);
2963 const ref = try block_scope.makeMangledName(c, "ref");
2964
2965 const expr = try transExpr(c, &block_scope.base, lhs, .used);
2966 const addr_of = try Tag.address_of.create(c.arena, expr);
2967 const ref_decl = try Tag.var_simple.create(c.arena, .{ .name = ref, .init = addr_of });
2968 try block_scope.statements.append(ref_decl);
37762969
3777 const lhs_node = try transCreateNodeIdentifier(rp.c, ref);
3778 const ref_node = try transCreateNodePtrDeref(rp.c, lhs_node);
3779 _ = try appendToken(rp.c, .Semicolon, ";");
2970 const lhs_node = try Tag.identifier.create(c.arena, ref);
2971 const ref_node = try Tag.deref.create(c.arena, lhs_node);
37802972
37812973 if ((is_mod or is_div) and is_signed) {
3782 const op_token = try appendToken(rp.c, .Equal, "=");
3783 const op_node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
3784 const builtin = if (is_mod) "@rem" else "@divTrunc";
3785 const builtin_node = try rp.c.createBuiltinCall(builtin, 2);
3786 builtin_node.params()[0] = try transCreateNodePtrDeref(rp.c, lhs_node);
3787 _ = try appendToken(rp.c, .Comma, ",");
3788 builtin_node.params()[1] = try transExpr(rp, scope, rhs, .used, .r_value);
3789 builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
3790 _ = try appendToken(rp.c, .Semicolon, ";");
3791 op_node.* = .{
3792 .base = .{ .tag = .Assign },
3793 .op_token = op_token,
3794 .lhs = ref_node,
3795 .rhs = &builtin_node.base,
3796 };
3797 _ = try appendToken(rp.c, .Semicolon, ";");
3798 try block_scope.statements.append(&op_node.base);
2974 const rhs_node = try transExpr(c, &block_scope.base, rhs, .used);
2975 const builtin = if (is_mod)
2976 try Tag.rem.create(c.arena, .{ .lhs = ref_node, .rhs = rhs_node })
2977 else
2978 try Tag.div_trunc.create(c.arena, .{ .lhs = ref_node, .rhs = rhs_node });
2979
2980 const assign = try transCreateNodeInfixOp(c, &block_scope.base, .assign, ref_node, builtin, .used);
2981 try block_scope.statements.append(assign);
37992982 } else {
3800 const bin_token = try appendToken(rp.c, bin_tok_id, bin_bytes);
3801 var rhs_node = try transExpr(rp, scope, rhs, .used, .r_value);
2983 var rhs_node = try transExpr(c, &block_scope.base, rhs, .used);
38022984
38032985 if (is_shift or requires_int_cast) {
3804 const cast_node = try rp.c.createBuiltinCall("@intCast", 2);
2986 // @intCast(rhs)
38052987 const cast_to_type = if (is_shift)
3806 try qualTypeToLog2IntRef(rp, getExprQualType(rp.c, rhs), loc)
2988 try qualTypeToLog2IntRef(c, scope, getExprQualType(c, rhs), loc)
38072989 else
3808 try transQualType(rp, getExprQualType(rp.c, lhs), loc);
3809 cast_node.params()[0] = cast_to_type;
3810 _ = try appendToken(rp.c, .Comma, ",");
3811 cast_node.params()[1] = rhs_node;
3812 cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
3813 rhs_node = &cast_node.base;
3814 }
2990 try transQualType(c, scope, getExprQualType(c, lhs), loc);
38152991
3816 const rhs_bin = try transCreateNodeInfixOp(rp, scope, ref_node, bin_op, bin_token, rhs_node, .used, false);
3817 _ = try appendToken(rp.c, .Semicolon, ";");
2992 rhs_node = try Tag.int_cast.create(c.arena, .{ .lhs = cast_to_type, .rhs = rhs_node });
2993 }
38182994
3819 const ass_eq_token = try appendToken(rp.c, .Equal, "=");
3820 const assign = try transCreateNodeInfixOp(rp, scope, ref_node, .Assign, ass_eq_token, rhs_bin, .used, false);
2995 const assign = try transCreateNodeInfixOp(c, &block_scope.base, op, ref_node, rhs_node, .used);
38212996 try block_scope.statements.append(assign);
38222997 }
38232998
3824 const break_node = try transCreateNodeBreak(rp.c, block_scope.label, ref_node);
3825 try block_scope.statements.append(&break_node.base);
3826 const block_node = try block_scope.complete(rp.c);
3827 const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
3828 grouped_expr.* = .{
3829 .lparen = try appendToken(rp.c, .LParen, "("),
3830 .expr = block_node,
3831 .rparen = try appendToken(rp.c, .RParen, ")"),
3832 };
3833 return &grouped_expr.base;
2999 const break_node = try Tag.break_val.create(c.arena, .{
3000 .label = block_scope.label,
3001 .val = ref_node,
3002 });
3003 try block_scope.statements.append(break_node);
3004 return block_scope.complete(c);
38343005}
38353006
38363007fn transCPtrCast(
3837 rp: RestorePoint,
3008 c: *Context,
3009 scope: *Scope,
38383010 loc: clang.SourceLocation,
38393011 dst_type: clang.QualType,
38403012 src_type: clang.QualType,
3841 expr: *ast.Node,
3842) !*ast.Node {
3013 expr: Node,
3014) !Node {
38433015 const ty = dst_type.getTypePtr();
38443016 const child_type = ty.getPointeeType();
38453017 const src_ty = src_type.getTypePtr();
38463018 const src_child_type = src_ty.getPointeeType();
3019 const dst_type_node = try transType(c, scope, ty, loc);
38473020
38483021 if ((src_child_type.isConstQualified() and
38493022 !child_type.isConstQualified()) or
......@@ -3851,80 +3024,47 @@ fn transCPtrCast(
38513024 !child_type.isVolatileQualified()))
38523025 {
38533026 // Casting away const or volatile requires us to use @intToPtr
3854 const inttoptr_node = try rp.c.createBuiltinCall("@intToPtr", 2);
3855 const dst_type_node = try transType(rp, ty, loc);
3856 inttoptr_node.params()[0] = dst_type_node;
3857 _ = try appendToken(rp.c, .Comma, ",");
3858
3859 const ptrtoint_node = try rp.c.createBuiltinCall("@ptrToInt", 1);
3860 ptrtoint_node.params()[0] = expr;
3861 ptrtoint_node.rparen_token = try appendToken(rp.c, .RParen, ")");
3862
3863 inttoptr_node.params()[1] = &ptrtoint_node.base;
3864 inttoptr_node.rparen_token = try appendToken(rp.c, .RParen, ")");
3865 return &inttoptr_node.base;
3027 const ptr_to_int = try Tag.ptr_to_int.create(c.arena, expr);
3028 const int_to_ptr = try Tag.int_to_ptr.create(c.arena, .{ .lhs = dst_type_node, .rhs = ptr_to_int });
3029 return int_to_ptr;
38663030 } else {
38673031 // Implicit downcasting from higher to lower alignment values is forbidden,
38683032 // use @alignCast to side-step this problem
3869 const ptrcast_node = try rp.c.createBuiltinCall("@ptrCast", 2);
3870 const dst_type_node = try transType(rp, ty, loc);
3871 ptrcast_node.params()[0] = dst_type_node;
3872 _ = try appendToken(rp.c, .Comma, ",");
3873
3874 if (qualTypeCanon(child_type).isVoidType()) {
3033 const rhs = if (qualTypeCanon(child_type).isVoidType())
38753034 // void has 1-byte alignment, so @alignCast is not needed
3876 ptrcast_node.params()[1] = expr;
3877 } else if (typeIsOpaque(rp.c, qualTypeCanon(child_type), loc)) {
3035 expr
3036 else if (typeIsOpaque(c, qualTypeCanon(child_type), loc))
38783037 // For opaque types a ptrCast is enough
3879 ptrcast_node.params()[1] = expr;
3880 } else {
3881 const aligncast_node = try rp.c.createBuiltinCall("@alignCast", 2);
3882 const alignof_node = try rp.c.createBuiltinCall("@alignOf", 1);
3883 const child_type_node = try transQualType(rp, child_type, loc);
3884 alignof_node.params()[0] = child_type_node;
3885 alignof_node.rparen_token = try appendToken(rp.c, .RParen, ")");
3886 aligncast_node.params()[0] = &alignof_node.base;
3887 _ = try appendToken(rp.c, .Comma, ",");
3888 aligncast_node.params()[1] = expr;
3889 aligncast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
3890 ptrcast_node.params()[1] = &aligncast_node.base;
3891 }
3892 ptrcast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
3893
3894 return &ptrcast_node.base;
3038 expr
3039 else blk: {
3040 const child_type_node = try transQualType(c, scope, child_type, loc);
3041 const alignof = try Tag.alignof.create(c.arena, child_type_node);
3042 const align_cast = try Tag.align_cast.create(c.arena, .{ .lhs = alignof, .rhs = expr });
3043 break :blk align_cast;
3044 };
3045 return Tag.ptr_cast.create(c.arena, .{ .lhs = dst_type_node, .rhs = rhs });
38953046 }
38963047}
38973048
3898fn transBreak(rp: RestorePoint, scope: *Scope) TransError!*ast.Node {
3899 const break_scope = scope.getBreakableScope();
3900 const label_text: ?[]const u8 = if (break_scope.id == .Switch) blk: {
3901 const swtch = @fieldParentPtr(Scope.Switch, "base", break_scope);
3902 const block_scope = try scope.findBlockScope(rp.c);
3903 swtch.switch_label = try block_scope.makeMangledName(rp.c, "switch");
3904 break :blk swtch.switch_label;
3905 } else
3906 null;
3907
3908 var cf = try CtrlFlow.init(rp.c, .Break, label_text);
3909 const br = try cf.finish(null);
3910 _ = try appendToken(rp.c, .Semicolon, ";");
3911 return &br.base;
3912}
3913
3914fn transFloatingLiteral(rp: RestorePoint, scope: *Scope, stmt: *const clang.FloatingLiteral, used: ResultUsed) TransError!*ast.Node {
3049fn transFloatingLiteral(c: *Context, scope: *Scope, stmt: *const clang.FloatingLiteral, used: ResultUsed) TransError!Node {
39153050 // TODO use something more accurate
3916 const dbl = stmt.getValueAsApproximateDouble();
3917 const node = try rp.c.arena.create(ast.Node.OneToken);
3918 node.* = .{
3919 .base = .{ .tag = .FloatLiteral },
3920 .token = try appendTokenFmt(rp.c, .FloatLiteral, "{d}", .{dbl}),
3921 };
3922 return maybeSuppressResult(rp, scope, used, &node.base);
3051 var dbl = stmt.getValueAsApproximateDouble();
3052 const is_negative = dbl < 0;
3053 if (is_negative) dbl = -dbl;
3054 const str = try std.fmt.allocPrint(c.arena, "{d}", .{dbl});
3055 var node = if (dbl == std.math.floor(dbl))
3056 try Tag.integer_literal.create(c.arena, str)
3057 else
3058 try Tag.float_literal.create(c.arena, str);
3059 if (is_negative) node = try Tag.negate.create(c.arena, node);
3060 return maybeSuppressResult(c, scope, used, node);
39233061}
39243062
3925fn transBinaryConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const clang.BinaryConditionalOperator, used: ResultUsed) TransError!*ast.Node {
3063fn transBinaryConditionalOperator(c: *Context, scope: *Scope, stmt: *const clang.BinaryConditionalOperator, used: ResultUsed) TransError!Node {
39263064 // GNU extension of the ternary operator where the middle expression is
39273065 // omitted, the conditition itself is returned if it evaluates to true
3066 const qt = @ptrCast(*const clang.Expr, stmt).getType();
3067 const res_is_bool = qualTypeIsBoolean(qt);
39283068 const casted_stmt = @ptrCast(*const clang.AbstractConditionalOperator, stmt);
39293069 const cond_expr = casted_stmt.getCond();
39303070 const true_expr = casted_stmt.getTrueExpr();
......@@ -3935,184 +3075,149 @@ fn transBinaryConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const
39353075 // const _cond_temp = (cond_expr);
39363076 // break :blk if (_cond_temp) _cond_temp else (false_expr);
39373077 // })
3938 const lparen = try appendToken(rp.c, .LParen, "(");
3939
3940 var block_scope = try Scope.Block.init(rp.c, scope, true);
3078 var block_scope = try Scope.Block.init(c, scope, true);
39413079 defer block_scope.deinit();
39423080
3943 const mangled_name = try block_scope.makeMangledName(rp.c, "cond_temp");
3944 const mut_tok = try appendToken(rp.c, .Keyword_const, "const");
3945 const name_tok = try appendIdentifier(rp.c, mangled_name);
3946 const eq_token = try appendToken(rp.c, .Equal, "=");
3947 const init_node = try transExpr(rp, &block_scope.base, cond_expr, .used, .r_value);
3948 const semicolon_token = try appendToken(rp.c, .Semicolon, ";");
3949 const tmp_var = try ast.Node.VarDecl.create(rp.c.arena, .{
3950 .name_token = name_tok,
3951 .mut_token = mut_tok,
3952 .semicolon_token = semicolon_token,
3953 }, .{
3954 .eq_token = eq_token,
3955 .init_node = init_node,
3956 });
3957 try block_scope.statements.append(&tmp_var.base);
3958
3959 var break_node_tmp = try CtrlFlow.initToken(rp.c, .Break, block_scope.label);
3081 const mangled_name = try block_scope.makeMangledName(c, "cond_temp");
3082 const init_node = try transExpr(c, &block_scope.base, cond_expr, .used);
3083 const ref_decl = try Tag.var_simple.create(c.arena, .{ .name = mangled_name, .init = init_node });
3084 try block_scope.statements.append(ref_decl);
39603085
3961 const if_node = try transCreateNodeIf(rp.c);
39623086 var cond_scope = Scope.Condition{
39633087 .base = .{
39643088 .parent = &block_scope.base,
3965 .id = .Condition,
3089 .id = .condition,
39663090 },
39673091 };
39683092 defer cond_scope.deinit();
3969 const tmp_var_node = try transCreateNodeIdentifier(rp.c, mangled_name);
3970
3971 const ty = getExprQualType(rp.c, cond_expr).getTypePtr();
3972 const cond_node = try finishBoolExpr(rp, &cond_scope.base, cond_expr.getBeginLoc(), ty, tmp_var_node, used);
3973 if_node.condition = cond_node;
3974 _ = try appendToken(rp.c, .RParen, ")");
3975
3976 if_node.body = try transCreateNodeIdentifier(rp.c, mangled_name);
3977 if_node.@"else" = try transCreateNodeElse(rp.c);
3978 if_node.@"else".?.body = try transExpr(rp, &block_scope.base, false_expr, .used, .r_value);
3979 _ = try appendToken(rp.c, .Semicolon, ";");
3980
3981 const break_node = try break_node_tmp.finish(&if_node.base);
3982 _ = try appendToken(rp.c, .Semicolon, ";");
3983 try block_scope.statements.append(&break_node.base);
3984 const block_node = try block_scope.complete(rp.c);
3985
3986 const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
3987 grouped_expr.* = .{
3988 .lparen = lparen,
3989 .expr = block_node,
3990 .rparen = try appendToken(rp.c, .RParen, ")"),
3991 };
3992 return maybeSuppressResult(rp, scope, used, &grouped_expr.base);
3093
3094 const cond_ident = try Tag.identifier.create(c.arena, mangled_name);
3095 const ty = getExprQualType(c, cond_expr).getTypePtr();
3096 const cond_node = try finishBoolExpr(c, &cond_scope.base, cond_expr.getBeginLoc(), ty, cond_ident, .used);
3097 var then_body = cond_ident;
3098 if (!res_is_bool and isBoolRes(init_node)) {
3099 then_body = try Tag.bool_to_int.create(c.arena, then_body);
3100 }
3101
3102 var else_body = try transExpr(c, &block_scope.base, false_expr, .used);
3103 if (!res_is_bool and isBoolRes(else_body)) {
3104 else_body = try Tag.bool_to_int.create(c.arena, else_body);
3105 }
3106 const if_node = try Tag.@"if".create(c.arena, .{
3107 .cond = cond_node,
3108 .then = then_body,
3109 .@"else" = else_body,
3110 });
3111 const break_node = try Tag.break_val.create(c.arena, .{
3112 .label = block_scope.label,
3113 .val = if_node,
3114 });
3115 try block_scope.statements.append(break_node);
3116 const res = try block_scope.complete(c);
3117 return maybeSuppressResult(c, scope, used, res);
39933118}
39943119
3995fn transConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const clang.ConditionalOperator, used: ResultUsed) TransError!*ast.Node {
3996 const grouped = scope.id == .Condition;
3997 const lparen = if (grouped) try appendToken(rp.c, .LParen, "(") else undefined;
3998 const if_node = try transCreateNodeIf(rp.c);
3120fn transConditionalOperator(c: *Context, scope: *Scope, stmt: *const clang.ConditionalOperator, used: ResultUsed) TransError!Node {
39993121 var cond_scope = Scope.Condition{
40003122 .base = .{
40013123 .parent = scope,
4002 .id = .Condition,
3124 .id = .condition,
40033125 },
40043126 };
40053127 defer cond_scope.deinit();
40063128
3129 const qt = @ptrCast(*const clang.Expr, stmt).getType();
3130 const res_is_bool = qualTypeIsBoolean(qt);
40073131 const casted_stmt = @ptrCast(*const clang.AbstractConditionalOperator, stmt);
40083132 const cond_expr = casted_stmt.getCond();
40093133 const true_expr = casted_stmt.getTrueExpr();
40103134 const false_expr = casted_stmt.getFalseExpr();
40113135
4012 if_node.condition = try transBoolExpr(rp, &cond_scope.base, cond_expr, .used, .r_value, false);
4013 _ = try appendToken(rp.c, .RParen, ")");
4014
4015 if_node.body = try transExpr(rp, scope, true_expr, .used, .r_value);
3136 const cond = try transBoolExpr(c, &cond_scope.base, cond_expr, .used);
40163137
4017 if_node.@"else" = try transCreateNodeElse(rp.c);
4018 if_node.@"else".?.body = try transExpr(rp, scope, false_expr, .used, .r_value);
3138 var then_body = try transExpr(c, scope, true_expr, used);
3139 if (!res_is_bool and isBoolRes(then_body)) {
3140 then_body = try Tag.bool_to_int.create(c.arena, then_body);
3141 }
40193142
4020 if (grouped) {
4021 const rparen = try appendToken(rp.c, .RParen, ")");
4022 const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
4023 grouped_expr.* = .{
4024 .lparen = lparen,
4025 .expr = &if_node.base,
4026 .rparen = rparen,
4027 };
4028 return maybeSuppressResult(rp, scope, used, &grouped_expr.base);
4029 } else {
4030 return maybeSuppressResult(rp, scope, used, &if_node.base);
3143 var else_body = try transExpr(c, scope, false_expr, used);
3144 if (!res_is_bool and isBoolRes(else_body)) {
3145 else_body = try Tag.bool_to_int.create(c.arena, else_body);
40313146 }
3147
3148 const if_node = try Tag.@"if".create(c.arena, .{
3149 .cond = cond,
3150 .then = then_body,
3151 .@"else" = else_body,
3152 });
3153 // Clang inserts ImplicitCast(ToVoid)'s to both rhs and lhs so we don't need to supress the result here.
3154 return if_node;
40323155}
40333156
40343157fn maybeSuppressResult(
4035 rp: RestorePoint,
3158 c: *Context,
40363159 scope: *Scope,
40373160 used: ResultUsed,
4038 result: *ast.Node,
4039) TransError!*ast.Node {
3161 result: Node,
3162) TransError!Node {
40403163 if (used == .used) return result;
4041 if (scope.id != .Condition) {
4042 // NOTE: This is backwards, but the semicolon must immediately follow the node.
4043 _ = try appendToken(rp.c, .Semicolon, ";");
4044 } else { // TODO is there a way to avoid this hack?
4045 // this parenthesis must come immediately following the node
4046 _ = try appendToken(rp.c, .RParen, ")");
4047 // these need to come before _
4048 _ = try appendToken(rp.c, .Colon, ":");
4049 _ = try appendToken(rp.c, .LParen, "(");
4050 }
4051 const lhs = try transCreateNodeIdentifier(rp.c, "_");
4052 const op_token = try appendToken(rp.c, .Equal, "=");
4053 const op_node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
4054 op_node.* = .{
4055 .base = .{ .tag = .Assign },
4056 .op_token = op_token,
4057 .lhs = lhs,
4058 .rhs = result,
4059 };
4060 return &op_node.base;
3164 return Tag.discard.create(c.arena, result);
40613165}
40623166
4063fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: *ast.Node) !void {
4064 try c.root_decls.append(c.gpa, decl_node);
3167fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: Node) !void {
40653168 _ = try c.global_scope.sym_table.put(name, decl_node);
3169 try c.global_scope.nodes.append(decl_node);
40663170}
40673171
40683172/// Translate a qual type for a variable with an initializer. The initializer
40693173/// only matters for incomplete arrays, since the size of the array is determined
40703174/// by the size of the initializer
40713175fn transQualTypeInitialized(
4072 rp: RestorePoint,
3176 c: *Context,
3177 scope: *Scope,
40733178 qt: clang.QualType,
40743179 decl_init: *const clang.Expr,
40753180 source_loc: clang.SourceLocation,
4076) TypeError!*ast.Node {
3181) TypeError!Node {
40773182 const ty = qt.getTypePtr();
40783183 if (ty.getTypeClass() == .IncompleteArray) {
40793184 const incomplete_array_ty = @ptrCast(*const clang.IncompleteArrayType, ty);
4080 const elem_ty = incomplete_array_ty.getElementType().getTypePtr();
3185 const elem_ty = try transType(c, scope, incomplete_array_ty.getElementType().getTypePtr(), source_loc);
40813186
40823187 switch (decl_init.getStmtClass()) {
40833188 .StringLiteralClass => {
40843189 const string_lit = @ptrCast(*const clang.StringLiteral, decl_init);
40853190 const string_lit_size = string_lit.getLength() + 1; // +1 for null terminator
40863191 const array_size = @intCast(usize, string_lit_size);
4087 return transCreateNodeArrayType(rp, source_loc, elem_ty, array_size);
3192 return Tag.array_type.create(c.arena, .{ .len = array_size, .elem_type = elem_ty });
40883193 },
40893194 .InitListExprClass => {
40903195 const init_expr = @ptrCast(*const clang.InitListExpr, decl_init);
40913196 const size = init_expr.getNumInits();
4092 return transCreateNodeArrayType(rp, source_loc, elem_ty, size);
3197 return Tag.array_type.create(c.arena, .{ .len = size, .elem_type = elem_ty });
40933198 },
40943199 else => {},
40953200 }
40963201 }
4097 return transQualType(rp, qt, source_loc);
3202 return transQualType(c, scope, qt, source_loc);
40983203}
40993204
4100fn transQualType(rp: RestorePoint, qt: clang.QualType, source_loc: clang.SourceLocation) TypeError!*ast.Node {
4101 return transType(rp, qt.getTypePtr(), source_loc);
3205fn transQualType(c: *Context, scope: *Scope, qt: clang.QualType, source_loc: clang.SourceLocation) TypeError!Node {
3206 return transType(c, scope, qt.getTypePtr(), source_loc);
41023207}
41033208
41043209/// Produces a Zig AST node by translating a Clang QualType, respecting the width, but modifying the signed-ness.
41053210/// Asserts the type is an integer.
4106fn transQualTypeIntWidthOf(c: *Context, ty: clang.QualType, is_signed: bool) TypeError!*ast.Node {
3211fn transQualTypeIntWidthOf(c: *Context, ty: clang.QualType, is_signed: bool) TypeError!Node {
41073212 return transTypeIntWidthOf(c, qualTypeCanon(ty), is_signed);
41083213}
41093214
41103215/// Produces a Zig AST node by translating a Clang Type, respecting the width, but modifying the signed-ness.
41113216/// Asserts the type is an integer.
4112fn transTypeIntWidthOf(c: *Context, ty: *const clang.Type, is_signed: bool) TypeError!*ast.Node {
3217fn transTypeIntWidthOf(c: *Context, ty: *const clang.Type, is_signed: bool) TypeError!Node {
41133218 assert(ty.getTypeClass() == .Builtin);
41143219 const builtin_ty = @ptrCast(*const clang.BuiltinType, ty);
4115 return transCreateNodeIdentifier(c, switch (builtin_ty.getKind()) {
3220 return Tag.type.create(c.arena, switch (builtin_ty.getKind()) {
41163221 .Char_U, .Char_S, .UChar, .SChar, .Char8 => if (is_signed) "i8" else "u8",
41173222 .UShort, .Short => if (is_signed) "c_short" else "c_ushort",
41183223 .UInt, .Int => if (is_signed) "c_int" else "c_uint",
......@@ -4141,7 +3246,7 @@ fn qualTypeIsBoolean(qt: clang.QualType) bool {
41413246 return qualTypeCanon(qt).isBooleanType();
41423247}
41433248
4144fn qualTypeIntBitWidth(rp: RestorePoint, qt: clang.QualType, source_loc: clang.SourceLocation) !u32 {
3249fn qualTypeIntBitWidth(c: *Context, qt: clang.QualType) !u32 {
41453250 const ty = qt.getTypePtr();
41463251
41473252 switch (ty.getTypeClass()) {
......@@ -4165,7 +3270,7 @@ fn qualTypeIntBitWidth(rp: RestorePoint, qt: clang.QualType, source_loc: clang.S
41653270 .Typedef => {
41663271 const typedef_ty = @ptrCast(*const clang.TypedefType, ty);
41673272 const typedef_decl = typedef_ty.getDecl();
4168 const type_name = try rp.c.str(@ptrCast(*const clang.NamedDecl, typedef_decl).getName_bytes_begin());
3273 const type_name = try c.str(@ptrCast(*const clang.NamedDecl, typedef_decl).getName_bytes_begin());
41693274
41703275 if (mem.eql(u8, type_name, "uint8_t") or mem.eql(u8, type_name, "int8_t")) {
41713276 return 8;
......@@ -4181,55 +3286,19 @@ fn qualTypeIntBitWidth(rp: RestorePoint, qt: clang.QualType, source_loc: clang.S
41813286 },
41823287 else => return 0,
41833288 }
4184
4185 unreachable;
41863289}
41873290
4188fn qualTypeToLog2IntRef(rp: RestorePoint, qt: clang.QualType, source_loc: clang.SourceLocation) !*ast.Node {
4189 const int_bit_width = try qualTypeIntBitWidth(rp, qt, source_loc);
3291fn qualTypeToLog2IntRef(c: *Context, scope: *Scope, qt: clang.QualType, source_loc: clang.SourceLocation) !Node {
3292 const int_bit_width = try qualTypeIntBitWidth(c, qt);
41903293
41913294 if (int_bit_width != 0) {
41923295 // we can perform the log2 now.
41933296 const cast_bit_width = math.log2_int(u64, int_bit_width);
4194 const node = try rp.c.arena.create(ast.Node.OneToken);
4195 node.* = .{
4196 .base = .{ .tag = .IntegerLiteral },
4197 .token = try appendTokenFmt(rp.c, .Identifier, "u{d}", .{cast_bit_width}),
4198 };
4199 return &node.base;
4200 }
4201
4202 const zig_type_node = try transQualType(rp, qt, source_loc);
4203
4204 // @import("std").math.Log2Int(c_long);
4205 //
4206 // FnCall
4207 // FieldAccess
4208 // FieldAccess
4209 // FnCall (.builtin = true)
4210 // Symbol "import"
4211 // StringLiteral "std"
4212 // Symbol "math"
4213 // Symbol "Log2Int"
4214 // Symbol <zig_type_node> (var from above)
4215
4216 const import_fn_call = try rp.c.createBuiltinCall("@import", 1);
4217 const std_token = try appendToken(rp.c, .StringLiteral, "\"std\"");
4218 const std_node = try rp.c.arena.create(ast.Node.OneToken);
4219 std_node.* = .{
4220 .base = .{ .tag = .StringLiteral },
4221 .token = std_token,
4222 };
4223 import_fn_call.params()[0] = &std_node.base;
4224 import_fn_call.rparen_token = try appendToken(rp.c, .RParen, ")");
4225
4226 const inner_field_access = try transCreateNodeFieldAccess(rp.c, &import_fn_call.base, "math");
4227 const outer_field_access = try transCreateNodeFieldAccess(rp.c, inner_field_access, "Log2Int");
4228 const log2int_fn_call = try rp.c.createCall(outer_field_access, 1);
4229 log2int_fn_call.params()[0] = zig_type_node;
4230 log2int_fn_call.rtoken = try appendToken(rp.c, .RParen, ")");
3297 return Tag.log2_int_type.create(c.arena, cast_bit_width);
3298 }
42313299
4232 return &log2int_fn_call.base;
3300 const zig_type = try transQualType(c, scope, qt, source_loc);
3301 return Tag.std_math_Log2Int.create(c.arena, zig_type);
42333302}
42343303
42353304fn qualTypeChildIsFnProto(qt: clang.QualType) bool {
......@@ -4393,28 +3462,22 @@ fn cIsLongLongInteger(qt: clang.QualType) bool {
43933462 };
43943463}
43953464fn transCreateNodeAssign(
4396 rp: RestorePoint,
3465 c: *Context,
43973466 scope: *Scope,
43983467 result_used: ResultUsed,
43993468 lhs: *const clang.Expr,
44003469 rhs: *const clang.Expr,
4401) !*ast.Node {
3470) !Node {
44023471 // common case
44033472 // c: lhs = rhs
44043473 // zig: lhs = rhs
44053474 if (result_used == .unused) {
4406 const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value);
4407 const eq_token = try appendToken(rp.c, .Equal, "=");
4408 var rhs_node = try transExprCoercing(rp, scope, rhs, .used, .r_value);
3475 const lhs_node = try transExpr(c, scope, lhs, .used);
3476 var rhs_node = try transExprCoercing(c, scope, rhs, .used);
44093477 if (!exprIsBooleanType(lhs) and isBoolRes(rhs_node)) {
4410 const builtin_node = try rp.c.createBuiltinCall("@boolToInt", 1);
4411 builtin_node.params()[0] = rhs_node;
4412 builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
4413 rhs_node = &builtin_node.base;
3478 rhs_node = try Tag.bool_to_int.create(c.arena, rhs_node);
44143479 }
4415 if (scope.id != .Condition)
4416 _ = try appendToken(rp.c, .Semicolon, ";");
4417 return transCreateNodeInfixOp(rp, scope, lhs_node, .Assign, eq_token, rhs_node, .used, false);
3480 return transCreateNodeInfixOp(c, scope, .assign, lhs_node, rhs_node, .used);
44183481 }
44193482
44203483 // worst case
......@@ -4424,176 +3487,62 @@ fn transCreateNodeAssign(
44243487 // zig: lhs = _tmp;
44253488 // zig: break :blk _tmp
44263489 // zig: })
4427 var block_scope = try Scope.Block.init(rp.c, scope, true);
3490 var block_scope = try Scope.Block.init(c, scope, true);
44283491 defer block_scope.deinit();
44293492
4430 const tmp = try block_scope.makeMangledName(rp.c, "tmp");
4431 const mut_tok = try appendToken(rp.c, .Keyword_const, "const");
4432 const name_tok = try appendIdentifier(rp.c, tmp);
4433 const eq_token = try appendToken(rp.c, .Equal, "=");
4434 var rhs_node = try transExpr(rp, &block_scope.base, rhs, .used, .r_value);
4435 if (!exprIsBooleanType(lhs) and isBoolRes(rhs_node)) {
4436 const builtin_node = try rp.c.createBuiltinCall("@boolToInt", 1);
4437 builtin_node.params()[0] = rhs_node;
4438 builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
4439 rhs_node = &builtin_node.base;
4440 }
4441 const init_node = rhs_node;
4442 const semicolon_token = try appendToken(rp.c, .Semicolon, ";");
4443 const node = try ast.Node.VarDecl.create(rp.c.arena, .{
4444 .name_token = name_tok,
4445 .mut_token = mut_tok,
4446 .semicolon_token = semicolon_token,
4447 }, .{
4448 .eq_token = eq_token,
4449 .init_node = init_node,
4450 });
4451 try block_scope.statements.append(&node.base);
4452
4453 const lhs_node = try transExpr(rp, &block_scope.base, lhs, .used, .l_value);
4454 const lhs_eq_token = try appendToken(rp.c, .Equal, "=");
4455 const ident = try transCreateNodeIdentifier(rp.c, tmp);
4456 _ = try appendToken(rp.c, .Semicolon, ";");
3493 const tmp = try block_scope.makeMangledName(c, "tmp");
3494 const rhs_node = try transExpr(c, &block_scope.base, rhs, .used);
3495 const tmp_decl = try Tag.var_simple.create(c.arena, .{ .name = tmp, .init = rhs_node });
3496 try block_scope.statements.append(tmp_decl);
44573497
4458 const assign = try transCreateNodeInfixOp(rp, &block_scope.base, lhs_node, .Assign, lhs_eq_token, ident, .used, false);
3498 const lhs_node = try transExpr(c, &block_scope.base, lhs, .used);
3499 const tmp_ident = try Tag.identifier.create(c.arena, tmp);
3500 const assign = try transCreateNodeInfixOp(c, &block_scope.base, .assign, lhs_node, tmp_ident, .used);
44593501 try block_scope.statements.append(assign);
44603502
4461 const break_node = blk: {
4462 var tmp_ctrl_flow = try CtrlFlow.init(rp.c, .Break, tokenSlice(rp.c, block_scope.label.?));
4463 const rhs_expr = try transCreateNodeIdentifier(rp.c, tmp);
4464 break :blk try tmp_ctrl_flow.finish(rhs_expr);
4465 };
4466 _ = try appendToken(rp.c, .Semicolon, ";");
4467 try block_scope.statements.append(&break_node.base);
4468 const block_node = try block_scope.complete(rp.c);
4469 // semicolon must immediately follow rbrace because it is the last token in a block
4470 _ = try appendToken(rp.c, .Semicolon, ";");
4471 return block_node;
4472}
4473
4474fn transCreateNodeFieldAccess(c: *Context, container: *ast.Node, field_name: []const u8) !*ast.Node {
4475 const field_access_node = try c.arena.create(ast.Node.SimpleInfixOp);
4476 field_access_node.* = .{
4477 .base = .{ .tag = .Period },
4478 .op_token = try appendToken(c, .Period, "."),
4479 .lhs = container,
4480 .rhs = try transCreateNodeIdentifier(c, field_name),
4481 };
4482 return &field_access_node.base;
4483}
4484
4485fn transCreateNodeSimplePrefixOp(
4486 c: *Context,
4487 comptime tag: ast.Node.Tag,
4488 op_tok_id: std.zig.Token.Id,
4489 bytes: []const u8,
4490) !*ast.Node.SimplePrefixOp {
4491 const node = try c.arena.create(ast.Node.SimplePrefixOp);
4492 node.* = .{
4493 .base = .{ .tag = tag },
4494 .op_token = try appendToken(c, op_tok_id, bytes),
4495 .rhs = undefined, // translate and set afterward
4496 };
4497 return node;
3503 const break_node = try Tag.break_val.create(c.arena, .{
3504 .label = block_scope.label,
3505 .val = tmp_ident,
3506 });
3507 try block_scope.statements.append(break_node);
3508 return block_scope.complete(c);
44983509}
44993510
45003511fn transCreateNodeInfixOp(
4501 rp: RestorePoint,
3512 c: *Context,
45023513 scope: *Scope,
4503 lhs_node: *ast.Node,
4504 op: ast.Node.Tag,
4505 op_token: ast.TokenIndex,
4506 rhs_node: *ast.Node,
3514 op: Tag,
3515 lhs: Node,
3516 rhs: Node,
45073517 used: ResultUsed,
4508 grouped: bool,
4509) !*ast.Node {
4510 var lparen = if (grouped)
4511 try appendToken(rp.c, .LParen, "(")
4512 else
4513 null;
4514 const node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
4515 node.* = .{
3518) !Node {
3519 const payload = try c.arena.create(ast.Payload.BinOp);
3520 payload.* = .{
45163521 .base = .{ .tag = op },
4517 .op_token = op_token,
4518 .lhs = lhs_node,
4519 .rhs = rhs_node,
4520 };
4521 if (!grouped) return maybeSuppressResult(rp, scope, used, &node.base);
4522 const rparen = try appendToken(rp.c, .RParen, ")");
4523 const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
4524 grouped_expr.* = .{
4525 .lparen = lparen.?,
4526 .expr = &node.base,
4527 .rparen = rparen,
3522 .data = .{
3523 .lhs = lhs,
3524 .rhs = rhs,
3525 },
45283526 };
4529 return maybeSuppressResult(rp, scope, used, &grouped_expr.base);
3527 return maybeSuppressResult(c, scope, used, Node.initPayload(&payload.base));
45303528}
45313529
45323530fn transCreateNodeBoolInfixOp(
4533 rp: RestorePoint,
3531 c: *Context,
45343532 scope: *Scope,
45353533 stmt: *const clang.BinaryOperator,
4536 op: ast.Node.Tag,
3534 op: Tag,
45373535 used: ResultUsed,
4538 grouped: bool,
4539) !*ast.Node {
4540 std.debug.assert(op == .BoolAnd or op == .BoolOr);
4541
4542 const lhs_hode = try transBoolExpr(rp, scope, stmt.getLHS(), .used, .l_value, true);
4543 const op_token = if (op == .BoolAnd)
4544 try appendToken(rp.c, .Keyword_and, "and")
4545 else
4546 try appendToken(rp.c, .Keyword_or, "or");
4547 const rhs = try transBoolExpr(rp, scope, stmt.getRHS(), .used, .r_value, true);
3536) !Node {
3537 std.debug.assert(op == .@"and" or op == .@"or");
45483538
4549 return transCreateNodeInfixOp(
4550 rp,
4551 scope,
4552 lhs_hode,
4553 op,
4554 op_token,
4555 rhs,
4556 used,
4557 grouped,
4558 );
4559}
3539 const lhs = try transBoolExpr(c, scope, stmt.getLHS(), .used);
3540 const rhs = try transBoolExpr(c, scope, stmt.getRHS(), .used);
45603541
4561fn transCreateNodePtrType(
4562 c: *Context,
4563 is_const: bool,
4564 is_volatile: bool,
4565 op_tok_id: std.zig.Token.Id,
4566) !*ast.Node.PtrType {
4567 const node = try c.arena.create(ast.Node.PtrType);
4568 const op_token = switch (op_tok_id) {
4569 .LBracket => blk: {
4570 const lbracket = try appendToken(c, .LBracket, "[");
4571 _ = try appendToken(c, .Asterisk, "*");
4572 _ = try appendToken(c, .RBracket, "]");
4573 break :blk lbracket;
4574 },
4575 .Identifier => blk: {
4576 const lbracket = try appendToken(c, .LBracket, "["); // Rendering checks if this token + 2 == .Identifier, so needs to return this token
4577 _ = try appendToken(c, .Asterisk, "*");
4578 _ = try appendIdentifier(c, "c");
4579 _ = try appendToken(c, .RBracket, "]");
4580 break :blk lbracket;
4581 },
4582 .Asterisk => try appendToken(c, .Asterisk, "*"),
4583 else => unreachable,
4584 };
4585 node.* = .{
4586 .op_token = op_token,
4587 .ptr_info = .{
4588 .const_token = if (is_const) try appendToken(c, .Keyword_const, "const") else null,
4589 .volatile_token = if (is_volatile) try appendToken(c, .Keyword_volatile, "volatile") else null,
4590 },
4591 .rhs = undefined, // translate and set afterward
4592 };
4593 return node;
3542 return transCreateNodeInfixOp(c, scope, op, lhs, rhs, used);
45943543}
45953544
4596fn transCreateNodeAPInt(c: *Context, int: *const clang.APSInt) !*ast.Node {
3545fn transCreateNodeAPInt(c: *Context, int: *const clang.APSInt) !Node {
45973546 const num_limbs = math.cast(usize, int.getNumWords()) catch |err| switch (err) {
45983547 error.Overflow => return error.OutOfMemory,
45993548 };
......@@ -4629,418 +3578,96 @@ fn transCreateNodeAPInt(c: *Context, int: *const clang.APSInt) !*ast.Node {
46293578 else => @compileError("unimplemented"),
46303579 }
46313580
4632 const big: math.big.int.Const = .{ .limbs = limbs, .positive = !is_negative };
3581 const big: math.big.int.Const = .{ .limbs = limbs, .positive = true };
46333582 const str = big.toStringAlloc(c.arena, 10, false) catch |err| switch (err) {
46343583 error.OutOfMemory => return error.OutOfMemory,
46353584 };
4636 defer c.arena.free(str);
4637 const token = try appendToken(c, .IntegerLiteral, str);
4638 const node = try c.arena.create(ast.Node.OneToken);
4639 node.* = .{
4640 .base = .{ .tag = .IntegerLiteral },
4641 .token = token,
4642 };
4643 return &node.base;
4644}
4645
4646fn transCreateNodeUndefinedLiteral(c: *Context) !*ast.Node {
4647 const token = try appendToken(c, .Keyword_undefined, "undefined");
4648 const node = try c.arena.create(ast.Node.OneToken);
4649 node.* = .{
4650 .base = .{ .tag = .UndefinedLiteral },
4651 .token = token,
4652 };
4653 return &node.base;
4654}
4655
4656fn transCreateNodeNullLiteral(c: *Context) !*ast.Node {
4657 const token = try appendToken(c, .Keyword_null, "null");
4658 const node = try c.arena.create(ast.Node.OneToken);
4659 node.* = .{
4660 .base = .{ .tag = .NullLiteral },
4661 .token = token,
4662 };
4663 return &node.base;
3585 const res = try Tag.integer_literal.create(c.arena, str);
3586 if (is_negative) return Tag.negate.create(c.arena, res);
3587 return res;
46643588}
46653589
4666fn transCreateNodeBoolLiteral(c: *Context, value: bool) !*ast.Node {
4667 const token = if (value)
4668 try appendToken(c, .Keyword_true, "true")
3590fn transCreateNodeNumber(c: *Context, num: anytype, num_kind: enum { int, float }) !Node {
3591 const fmt_s = if (comptime std.meta.trait.isNumber(@TypeOf(num))) "{d}" else "{s}";
3592 const str = try std.fmt.allocPrint(c.arena, fmt_s, .{num});
3593 if (num_kind == .float)
3594 return Tag.float_literal.create(c.arena, str)
46693595 else
4670 try appendToken(c, .Keyword_false, "false");
4671 const node = try c.arena.create(ast.Node.OneToken);
4672 node.* = .{
4673 .base = .{ .tag = .BoolLiteral },
4674 .token = token,
4675 };
4676 return &node.base;
4677}
4678
4679fn transCreateNodeInt(c: *Context, int: anytype) !*ast.Node {
4680 const fmt_s = if (comptime std.meta.trait.isIntegerNumber(@TypeOf(int))) "{d}" else "{s}";
4681 const token = try appendTokenFmt(c, .IntegerLiteral, fmt_s, .{int});
4682 const node = try c.arena.create(ast.Node.OneToken);
4683 node.* = .{
4684 .base = .{ .tag = .IntegerLiteral },
4685 .token = token,
4686 };
4687 return &node.base;
3596 return Tag.integer_literal.create(c.arena, str);
46883597}
46893598
4690fn transCreateNodeFloat(c: *Context, str: []const u8) !*ast.Node {
4691 const token = try appendTokenFmt(c, .FloatLiteral, "{s}", .{str});
4692 const node = try c.arena.create(ast.Node.OneToken);
4693 node.* = .{
4694 .base = .{ .tag = .FloatLiteral },
4695 .token = token,
4696 };
4697 return &node.base;
4698}
4699
4700fn transCreateNodeOpaqueType(c: *Context) !*ast.Node {
4701 const container_tok = try appendToken(c, .Keyword_opaque, "opaque");
4702 const lbrace_token = try appendToken(c, .LBrace, "{");
4703 const container_node = try ast.Node.ContainerDecl.alloc(c.arena, 0);
4704 container_node.* = .{
4705 .kind_token = container_tok,
4706 .layout_token = null,
4707 .lbrace_token = lbrace_token,
4708 .rbrace_token = try appendToken(c, .RBrace, "}"),
4709 .fields_and_decls_len = 0,
4710 .init_arg_expr = .None,
4711 };
4712 return &container_node.base;
4713}
4714
4715fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: *ast.Node, proto_alias: *ast.Node.FnProto) !*ast.Node {
3599fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: Node, proto_alias: *ast.Payload.Func) !Node {
47163600 const scope = &c.global_scope.base;
47173601
4718 const pub_tok = try appendToken(c, .Keyword_pub, "pub");
4719 const fn_tok = try appendToken(c, .Keyword_fn, "fn");
4720 const name_tok = try appendIdentifier(c, name);
4721 _ = try appendToken(c, .LParen, "(");
4722
4723 var fn_params = std.ArrayList(ast.Node.FnProto.ParamDecl).init(c.gpa);
3602 var fn_params = std.ArrayList(ast.Payload.Param).init(c.gpa);
47243603 defer fn_params.deinit();
47253604
4726 for (proto_alias.params()) |param, i| {
4727 if (i != 0) {
4728 _ = try appendToken(c, .Comma, ",");
4729 }
4730 const param_name_tok = param.name_token orelse
4731 try appendTokenFmt(c, .Identifier, "arg_{d}", .{c.getMangle()});
4732
4733 _ = try appendToken(c, .Colon, ":");
3605 for (proto_alias.data.params) |param, i| {
3606 const param_name = param.name orelse
3607 try std.fmt.allocPrint(c.arena, "arg_{d}", .{c.getMangle()});
47343608
4735 (try fn_params.addOne()).* = .{
4736 .doc_comments = null,
4737 .comptime_token = null,
4738 .noalias_token = param.noalias_token,
4739 .name_token = param_name_tok,
4740 .param_type = param.param_type,
4741 };
3609 try fn_params.append(.{
3610 .name = param_name,
3611 .type = param.type,
3612 .is_noalias = param.is_noalias,
3613 });
47423614 }
47433615
4744 _ = try appendToken(c, .RParen, ")");
4745
4746 _ = try appendToken(c, .Keyword_callconv, "callconv");
4747 _ = try appendToken(c, .LParen, "(");
4748 const callconv_expr = try transCreateNodeEnumLiteral(c, "Inline");
4749 _ = try appendToken(c, .RParen, ")");
4750
4751 const block_lbrace = try appendToken(c, .LBrace, "{");
4752
4753 const return_kw = try appendToken(c, .Keyword_return, "return");
4754 const unwrap_expr = try transCreateNodeUnwrapNull(c, ref.cast(ast.Node.VarDecl).?.getInitNode().?);
4755
4756 const call_expr = try c.createCall(unwrap_expr, fn_params.items.len);
4757 const call_params = call_expr.params();
3616 const init = if (ref.castTag(.var_decl)) |v|
3617 v.data.init.?
3618 else if (ref.castTag(.var_simple) orelse ref.castTag(.pub_var_simple)) |v|
3619 v.data.init
3620 else
3621 unreachable;
47583622
3623 const unwrap_expr = try Tag.unwrap.create(c.arena, init);
3624 const args = try c.arena.alloc(Node, fn_params.items.len);
47593625 for (fn_params.items) |param, i| {
4760 if (i != 0) {
4761 _ = try appendToken(c, .Comma, ",");
4762 }
4763 call_params[i] = try transCreateNodeIdentifier(c, tokenSlice(c, param.name_token.?));
3626 args[i] = try Tag.identifier.create(c.arena, param.name.?);
47643627 }
4765 call_expr.rtoken = try appendToken(c, .RParen, ")");
4766
4767 const return_expr = try ast.Node.ControlFlowExpression.create(c.arena, .{
4768 .ltoken = return_kw,
4769 .tag = .Return,
4770 }, .{
4771 .rhs = &call_expr.base,
3628 const call_expr = try Tag.call.create(c.arena, .{
3629 .lhs = unwrap_expr,
3630 .args = args,
47723631 });
4773 _ = try appendToken(c, .Semicolon, ";");
4774
4775 const block = try ast.Node.Block.alloc(c.arena, 1);
4776 block.* = .{
4777 .lbrace = block_lbrace,
4778 .statements_len = 1,
4779 .rbrace = try appendToken(c, .RBrace, "}"),
4780 };
4781 block.statements()[0] = &return_expr.base;
4782
4783 const fn_proto = try ast.Node.FnProto.create(c.arena, .{
4784 .params_len = fn_params.items.len,
4785 .fn_token = fn_tok,
4786 .return_type = proto_alias.return_type,
4787 }, .{
4788 .visib_token = pub_tok,
4789 .name_token = name_tok,
4790 .body_node = &block.base,
4791 .callconv_expr = callconv_expr,
3632 const return_expr = try Tag.@"return".create(c.arena, call_expr);
3633 const block = try Tag.block_single.create(c.arena, return_expr);
3634
3635 return Tag.pub_inline_fn.create(c.arena, .{
3636 .name = name,
3637 .params = try c.arena.dupe(ast.Payload.Param, fn_params.items),
3638 .return_type = proto_alias.data.return_type,
3639 .body = block,
47923640 });
4793 mem.copy(ast.Node.FnProto.ParamDecl, fn_proto.params(), fn_params.items);
4794 return &fn_proto.base;
4795}
4796
4797fn transCreateNodeUnwrapNull(c: *Context, wrapped: *ast.Node) !*ast.Node {
4798 _ = try appendToken(c, .Period, ".");
4799 const qm = try appendToken(c, .QuestionMark, "?");
4800 const node = try c.arena.create(ast.Node.SimpleSuffixOp);
4801 node.* = .{
4802 .base = .{ .tag = .UnwrapOptional },
4803 .lhs = wrapped,
4804 .rtoken = qm,
4805 };
4806 return &node.base;
4807}
4808
4809fn transCreateNodeEnumLiteral(c: *Context, name: []const u8) !*ast.Node {
4810 const node = try c.arena.create(ast.Node.EnumLiteral);
4811 node.* = .{
4812 .dot = try appendToken(c, .Period, "."),
4813 .name = try appendIdentifier(c, name),
4814 };
4815 return &node.base;
4816}
4817
4818fn transCreateNodeStringLiteral(c: *Context, str: []const u8) !*ast.Node {
4819 const node = try c.arena.create(ast.Node.OneToken);
4820 node.* = .{
4821 .base = .{ .tag = .StringLiteral },
4822 .token = try appendToken(c, .StringLiteral, str),
4823 };
4824 return &node.base;
4825}
4826
4827fn transCreateNodeIf(c: *Context) !*ast.Node.If {
4828 const if_tok = try appendToken(c, .Keyword_if, "if");
4829 _ = try appendToken(c, .LParen, "(");
4830 const node = try c.arena.create(ast.Node.If);
4831 node.* = .{
4832 .if_token = if_tok,
4833 .condition = undefined,
4834 .payload = null,
4835 .body = undefined,
4836 .@"else" = null,
4837 };
4838 return node;
4839}
4840
4841fn transCreateNodeElse(c: *Context) !*ast.Node.Else {
4842 const node = try c.arena.create(ast.Node.Else);
4843 node.* = .{
4844 .else_token = try appendToken(c, .Keyword_else, "else"),
4845 .payload = null,
4846 .body = undefined,
4847 };
4848 return node;
4849}
4850
4851fn transCreateNodeBreak(
4852 c: *Context,
4853 label: ?ast.TokenIndex,
4854 rhs: ?*ast.Node,
4855) !*ast.Node.ControlFlowExpression {
4856 var ctrl_flow = try CtrlFlow.init(c, .Break, if (label) |l| tokenSlice(c, l) else null);
4857 return ctrl_flow.finish(rhs);
4858}
4859
4860const CtrlFlow = struct {
4861 c: *Context,
4862 ltoken: ast.TokenIndex,
4863 label_token: ?ast.TokenIndex,
4864 tag: ast.Node.Tag,
4865
4866 /// Does everything except the RHS.
4867 fn init(c: *Context, tag: ast.Node.Tag, label: ?[]const u8) !CtrlFlow {
4868 const kw: Token.Id = switch (tag) {
4869 .Break => .Keyword_break,
4870 .Continue => .Keyword_continue,
4871 .Return => .Keyword_return,
4872 else => unreachable,
4873 };
4874 const kw_text = switch (tag) {
4875 .Break => "break",
4876 .Continue => "continue",
4877 .Return => "return",
4878 else => unreachable,
4879 };
4880 const ltoken = try appendToken(c, kw, kw_text);
4881 const label_token = if (label) |l| blk: {
4882 _ = try appendToken(c, .Colon, ":");
4883 break :blk try appendIdentifier(c, l);
4884 } else null;
4885 return CtrlFlow{
4886 .c = c,
4887 .ltoken = ltoken,
4888 .label_token = label_token,
4889 .tag = tag,
4890 };
4891 }
4892
4893 fn initToken(c: *Context, tag: ast.Node.Tag, label: ?ast.TokenIndex) !CtrlFlow {
4894 const other_token = label orelse return init(c, tag, null);
4895 const loc = c.token_locs.items[other_token];
4896 const label_name = c.source_buffer.items[loc.start..loc.end];
4897 return init(c, tag, label_name);
4898 }
4899
4900 fn finish(self: *CtrlFlow, rhs: ?*ast.Node) !*ast.Node.ControlFlowExpression {
4901 return ast.Node.ControlFlowExpression.create(self.c.arena, .{
4902 .ltoken = self.ltoken,
4903 .tag = self.tag,
4904 }, .{
4905 .label = self.label_token,
4906 .rhs = rhs,
4907 });
4908 }
4909};
4910
4911fn transCreateNodeWhile(c: *Context) !*ast.Node.While {
4912 const while_tok = try appendToken(c, .Keyword_while, "while");
4913 _ = try appendToken(c, .LParen, "(");
4914
4915 const node = try c.arena.create(ast.Node.While);
4916 node.* = .{
4917 .label = null,
4918 .inline_token = null,
4919 .while_token = while_tok,
4920 .condition = undefined,
4921 .payload = null,
4922 .continue_expr = null,
4923 .body = undefined,
4924 .@"else" = null,
4925 };
4926 return node;
4927}
4928
4929fn transCreateNodeContinue(c: *Context) !*ast.Node {
4930 const ltoken = try appendToken(c, .Keyword_continue, "continue");
4931 const node = try ast.Node.ControlFlowExpression.create(c.arena, .{
4932 .ltoken = ltoken,
4933 .tag = .Continue,
4934 }, .{});
4935 _ = try appendToken(c, .Semicolon, ";");
4936 return &node.base;
4937}
4938
4939fn transCreateNodeSwitchCase(c: *Context, lhs: *ast.Node) !*ast.Node.SwitchCase {
4940 const arrow_tok = try appendToken(c, .EqualAngleBracketRight, "=>");
4941
4942 const node = try ast.Node.SwitchCase.alloc(c.arena, 1);
4943 node.* = .{
4944 .items_len = 1,
4945 .arrow_token = arrow_tok,
4946 .payload = null,
4947 .expr = undefined,
4948 };
4949 node.items()[0] = lhs;
4950 return node;
4951}
4952
4953fn transCreateNodeSwitchElse(c: *Context) !*ast.Node {
4954 const node = try c.arena.create(ast.Node.SwitchElse);
4955 node.* = .{
4956 .token = try appendToken(c, .Keyword_else, "else"),
4957 };
4958 return &node.base;
49593641}
49603642
49613643fn transCreateNodeShiftOp(
4962 rp: RestorePoint,
3644 c: *Context,
49633645 scope: *Scope,
49643646 stmt: *const clang.BinaryOperator,
4965 op: ast.Node.Tag,
4966 op_tok_id: std.zig.Token.Id,
4967 bytes: []const u8,
4968) !*ast.Node {
4969 std.debug.assert(op == .BitShiftLeft or op == .BitShiftRight);
3647 op: Tag,
3648 used: ResultUsed,
3649) !Node {
3650 std.debug.assert(op == .shl or op == .shr);
49703651
49713652 const lhs_expr = stmt.getLHS();
49723653 const rhs_expr = stmt.getRHS();
49733654 const rhs_location = rhs_expr.getBeginLoc();
49743655 // lhs >> @as(u5, rh)
49753656
4976 const lhs = try transExpr(rp, scope, lhs_expr, .used, .l_value);
4977 const op_token = try appendToken(rp.c, op_tok_id, bytes);
4978
4979 const cast_node = try rp.c.createBuiltinCall("@intCast", 2);
4980 const rhs_type = try qualTypeToLog2IntRef(rp, stmt.getType(), rhs_location);
4981 cast_node.params()[0] = rhs_type;
4982 _ = try appendToken(rp.c, .Comma, ",");
4983 const rhs = try transExprCoercing(rp, scope, rhs_expr, .used, .r_value);
4984 cast_node.params()[1] = rhs;
4985 cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
4986
4987 const node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
4988 node.* = .{
4989 .base = .{ .tag = op },
4990 .op_token = op_token,
4991 .lhs = lhs,
4992 .rhs = &cast_node.base,
4993 };
3657 const lhs = try transExpr(c, scope, lhs_expr, .used);
49943658
4995 return &node.base;
4996}
3659 const rhs_type = try qualTypeToLog2IntRef(c, scope, stmt.getType(), rhs_location);
3660 const rhs = try transExprCoercing(c, scope, rhs_expr, .used);
3661 const rhs_casted = try Tag.int_cast.create(c.arena, .{ .lhs = rhs_type, .rhs = rhs });
49973662
4998fn transCreateNodePtrDeref(c: *Context, lhs: *ast.Node) !*ast.Node {
4999 const node = try c.arena.create(ast.Node.SimpleSuffixOp);
5000 node.* = .{
5001 .base = .{ .tag = .Deref },
5002 .lhs = lhs,
5003 .rtoken = try appendToken(c, .PeriodAsterisk, ".*"),
5004 };
5005 return &node.base;
3663 return transCreateNodeInfixOp(c, scope, op, lhs, rhs_casted, used);
50063664}
50073665
5008fn transCreateNodeArrayAccess(c: *Context, lhs: *ast.Node) !*ast.Node.ArrayAccess {
5009 _ = try appendToken(c, .LBrace, "[");
5010 const node = try c.arena.create(ast.Node.ArrayAccess);
5011 node.* = .{
5012 .lhs = lhs,
5013 .index_expr = undefined,
5014 .rtoken = undefined,
5015 };
5016 return node;
5017}
5018
5019const RestorePoint = struct {
5020 c: *Context,
5021 token_index: ast.TokenIndex,
5022 src_buf_index: usize,
5023
5024 fn activate(self: RestorePoint) void {
5025 self.c.token_ids.shrinkAndFree(self.c.gpa, self.token_index);
5026 self.c.token_locs.shrinkAndFree(self.c.gpa, self.token_index);
5027 self.c.source_buffer.shrinkAndFree(self.src_buf_index);
5028 }
5029};
5030
5031fn makeRestorePoint(c: *Context) RestorePoint {
5032 return RestorePoint{
5033 .c = c,
5034 .token_index = c.token_ids.items.len,
5035 .src_buf_index = c.source_buffer.items.len,
5036 };
5037}
5038
5039fn transType(rp: RestorePoint, ty: *const clang.Type, source_loc: clang.SourceLocation) TypeError!*ast.Node {
3666fn transType(c: *Context, scope: *Scope, ty: *const clang.Type, source_loc: clang.SourceLocation) TypeError!Node {
50403667 switch (ty.getTypeClass()) {
50413668 .Builtin => {
50423669 const builtin_ty = @ptrCast(*const clang.BuiltinType, ty);
5043 return transCreateNodeIdentifier(rp.c, switch (builtin_ty.getKind()) {
3670 return Tag.type.create(c.arena, switch (builtin_ty.getKind()) {
50443671 .Void => "c_void",
50453672 .Bool => "bool",
50463673 .Char_U, .UChar, .Char_S, .Char8 => "u8",
......@@ -5060,112 +3687,115 @@ fn transType(rp: RestorePoint, ty: *const clang.Type, source_loc: clang.SourceLo
50603687 .Float128 => "f128",
50613688 .Float16 => "f16",
50623689 .LongDouble => "c_longdouble",
5063 else => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported builtin type", .{}),
3690 else => return fail(c, error.UnsupportedType, source_loc, "unsupported builtin type", .{}),
50643691 });
50653692 },
50663693 .FunctionProto => {
50673694 const fn_proto_ty = @ptrCast(*const clang.FunctionProtoType, ty);
5068 const fn_proto = try transFnProto(rp, null, fn_proto_ty, source_loc, null, false);
5069 return &fn_proto.base;
3695 const fn_proto = try transFnProto(c, null, fn_proto_ty, source_loc, null, false);
3696 return Node.initPayload(&fn_proto.base);
50703697 },
50713698 .FunctionNoProto => {
50723699 const fn_no_proto_ty = @ptrCast(*const clang.FunctionType, ty);
5073 const fn_proto = try transFnNoProto(rp, fn_no_proto_ty, source_loc, null, false);
5074 return &fn_proto.base;
3700 const fn_proto = try transFnNoProto(c, fn_no_proto_ty, source_loc, null, false);
3701 return Node.initPayload(&fn_proto.base);
50753702 },
50763703 .Paren => {
50773704 const paren_ty = @ptrCast(*const clang.ParenType, ty);
5078 return transQualType(rp, paren_ty.getInnerType(), source_loc);
3705 return transQualType(c, scope, paren_ty.getInnerType(), source_loc);
50793706 },
50803707 .Pointer => {
50813708 const child_qt = ty.getPointeeType();
50823709 if (qualTypeChildIsFnProto(child_qt)) {
5083 const optional_node = try transCreateNodeSimplePrefixOp(rp.c, .OptionalType, .QuestionMark, "?");
5084 optional_node.rhs = try transQualType(rp, child_qt, source_loc);
5085 return &optional_node.base;
3710 return Tag.optional_type.create(c.arena, try transQualType(c, scope, child_qt, source_loc));
50863711 }
5087 if (typeIsOpaque(rp.c, child_qt.getTypePtr(), source_loc) or qualTypeWasDemotedToOpaque(rp.c, child_qt)) {
5088 const optional_node = try transCreateNodeSimplePrefixOp(rp.c, .OptionalType, .QuestionMark, "?");
5089 const pointer_node = try transCreateNodePtrType(
5090 rp.c,
5091 child_qt.isConstQualified(),
5092 child_qt.isVolatileQualified(),
5093 .Asterisk,
5094 );
5095 optional_node.rhs = &pointer_node.base;
5096 pointer_node.rhs = try transQualType(rp, child_qt, source_loc);
5097 return &optional_node.base;
3712 const is_const = child_qt.isConstQualified();
3713 const is_volatile = child_qt.isVolatileQualified();
3714 const elem_type = try transQualType(c, scope, child_qt, source_loc);
3715 if (typeIsOpaque(c, child_qt.getTypePtr(), source_loc) or qualTypeWasDemotedToOpaque(c, child_qt)) {
3716 const ptr = try Tag.single_pointer.create(c.arena, .{ .is_const = is_const, .is_volatile = is_volatile, .elem_type = elem_type });
3717 return Tag.optional_type.create(c.arena, ptr);
50983718 }
5099 const pointer_node = try transCreateNodePtrType(
5100 rp.c,
5101 child_qt.isConstQualified(),
5102 child_qt.isVolatileQualified(),
5103 .Identifier,
5104 );
5105 pointer_node.rhs = try transQualType(rp, child_qt, source_loc);
5106 return &pointer_node.base;
3719
3720 return Tag.c_pointer.create(c.arena, .{ .is_const = is_const, .is_volatile = is_volatile, .elem_type = elem_type });
51073721 },
51083722 .ConstantArray => {
51093723 const const_arr_ty = @ptrCast(*const clang.ConstantArrayType, ty);
51103724
51113725 const size_ap_int = const_arr_ty.getSize();
51123726 const size = size_ap_int.getLimitedValue(math.maxInt(usize));
5113 const elem_ty = const_arr_ty.getElementType().getTypePtr();
5114 return try transCreateNodeArrayType(rp, source_loc, elem_ty, size);
3727 const elem_type = try transType(c, scope, const_arr_ty.getElementType().getTypePtr(), source_loc);
3728
3729 return Tag.array_type.create(c.arena, .{ .len = size, .elem_type = elem_type });
51153730 },
51163731 .IncompleteArray => {
51173732 const incomplete_array_ty = @ptrCast(*const clang.IncompleteArrayType, ty);
51183733
51193734 const child_qt = incomplete_array_ty.getElementType();
5120 var node = try transCreateNodePtrType(
5121 rp.c,
5122 child_qt.isConstQualified(),
5123 child_qt.isVolatileQualified(),
5124 .Identifier,
5125 );
5126 node.rhs = try transQualType(rp, child_qt, source_loc);
5127 return &node.base;
3735 const is_const = child_qt.isConstQualified();
3736 const is_volatile = child_qt.isVolatileQualified();
3737 const elem_type = try transQualType(c, scope, child_qt, source_loc);
3738
3739 return Tag.c_pointer.create(c.arena, .{ .is_const = is_const, .is_volatile = is_volatile, .elem_type = elem_type });
51283740 },
51293741 .Typedef => {
51303742 const typedef_ty = @ptrCast(*const clang.TypedefType, ty);
51313743
51323744 const typedef_decl = typedef_ty.getDecl();
5133 return (try transTypeDef(rp.c, typedef_decl, false)) orelse
5134 revertAndWarn(rp, error.UnsupportedType, source_loc, "unable to translate typedef declaration", .{});
3745 var trans_scope = scope;
3746 if (@ptrCast(*const clang.Decl, typedef_decl).castToNamedDecl()) |named_decl| {
3747 const decl_name = try c.str(named_decl.getName_bytes_begin());
3748 if (c.global_names.get(decl_name)) |_| trans_scope = &c.global_scope.base;
3749 }
3750 try transTypeDef(c, trans_scope, typedef_decl);
3751 const name = c.decl_table.get(@ptrToInt(typedef_decl.getCanonicalDecl())).?;
3752 return Tag.identifier.create(c.arena, name);
51353753 },
51363754 .Record => {
51373755 const record_ty = @ptrCast(*const clang.RecordType, ty);
51383756
51393757 const record_decl = record_ty.getDecl();
5140 return (try transRecordDecl(rp.c, record_decl)) orelse
5141 revertAndWarn(rp, error.UnsupportedType, source_loc, "unable to resolve record declaration", .{});
3758 var trans_scope = scope;
3759 if (@ptrCast(*const clang.Decl, record_decl).castToNamedDecl()) |named_decl| {
3760 const decl_name = try c.str(named_decl.getName_bytes_begin());
3761 if (c.global_names.get(decl_name)) |_| trans_scope = &c.global_scope.base;
3762 }
3763 try transRecordDecl(c, trans_scope, record_decl);
3764 const name = c.decl_table.get(@ptrToInt(record_decl.getCanonicalDecl())).?;
3765 return Tag.identifier.create(c.arena, name);
51423766 },
51433767 .Enum => {
51443768 const enum_ty = @ptrCast(*const clang.EnumType, ty);
51453769
51463770 const enum_decl = enum_ty.getDecl();
5147 return (try transEnumDecl(rp.c, enum_decl)) orelse
5148 revertAndWarn(rp, error.UnsupportedType, source_loc, "unable to translate enum declaration", .{});
3771 var trans_scope = scope;
3772 if (@ptrCast(*const clang.Decl, enum_decl).castToNamedDecl()) |named_decl| {
3773 const decl_name = try c.str(named_decl.getName_bytes_begin());
3774 if (c.global_names.get(decl_name)) |_| trans_scope = &c.global_scope.base;
3775 }
3776 try transEnumDecl(c, trans_scope, enum_decl);
3777 const name = c.decl_table.get(@ptrToInt(enum_decl.getCanonicalDecl())).?;
3778 return Tag.identifier.create(c.arena, name);
51493779 },
51503780 .Elaborated => {
51513781 const elaborated_ty = @ptrCast(*const clang.ElaboratedType, ty);
5152 return transQualType(rp, elaborated_ty.getNamedType(), source_loc);
3782 return transQualType(c, scope, elaborated_ty.getNamedType(), source_loc);
51533783 },
51543784 .Decayed => {
51553785 const decayed_ty = @ptrCast(*const clang.DecayedType, ty);
5156 return transQualType(rp, decayed_ty.getDecayedType(), source_loc);
3786 return transQualType(c, scope, decayed_ty.getDecayedType(), source_loc);
51573787 },
51583788 .Attributed => {
51593789 const attributed_ty = @ptrCast(*const clang.AttributedType, ty);
5160 return transQualType(rp, attributed_ty.getEquivalentType(), source_loc);
3790 return transQualType(c, scope, attributed_ty.getEquivalentType(), source_loc);
51613791 },
51623792 .MacroQualified => {
51633793 const macroqualified_ty = @ptrCast(*const clang.MacroQualifiedType, ty);
5164 return transQualType(rp, macroqualified_ty.getModifiedType(), source_loc);
3794 return transQualType(c, scope, macroqualified_ty.getModifiedType(), source_loc);
51653795 },
51663796 else => {
5167 const type_name = rp.c.str(ty.getTypeClassName());
5168 return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported type: '{s}'", .{type_name});
3797 const type_name = c.str(ty.getTypeClassName());
3798 return fail(c, error.UnsupportedType, source_loc, "unsupported type: '{s}'", .{type_name});
51693799 },
51703800 }
51713801}
......@@ -5231,7 +3861,7 @@ const FnDeclContext = struct {
52313861};
52323862
52333863fn transCC(
5234 rp: RestorePoint,
3864 c: *Context,
52353865 fn_ty: *const clang.FunctionType,
52363866 source_loc: clang.SourceLocation,
52373867) !CallingConvention {
......@@ -5244,8 +3874,8 @@ fn transCC(
52443874 .X86ThisCall => return CallingConvention.Thiscall,
52453875 .AAPCS => return CallingConvention.AAPCS,
52463876 .AAPCS_VFP => return CallingConvention.AAPCSVFP,
5247 else => return revertAndWarn(
5248 rp,
3877 else => return fail(
3878 c,
52493879 error.UnsupportedType,
52503880 source_loc,
52513881 "unsupported calling convention: {s}",
......@@ -5255,33 +3885,33 @@ fn transCC(
52553885}
52563886
52573887fn transFnProto(
5258 rp: RestorePoint,
3888 c: *Context,
52593889 fn_decl: ?*const clang.FunctionDecl,
52603890 fn_proto_ty: *const clang.FunctionProtoType,
52613891 source_loc: clang.SourceLocation,
52623892 fn_decl_context: ?FnDeclContext,
52633893 is_pub: bool,
5264) !*ast.Node.FnProto {
3894) !*ast.Payload.Func {
52653895 const fn_ty = @ptrCast(*const clang.FunctionType, fn_proto_ty);
5266 const cc = try transCC(rp, fn_ty, source_loc);
3896 const cc = try transCC(c, fn_ty, source_loc);
52673897 const is_var_args = fn_proto_ty.isVariadic();
5268 return finishTransFnProto(rp, fn_decl, fn_proto_ty, fn_ty, source_loc, fn_decl_context, is_var_args, cc, is_pub);
3898 return finishTransFnProto(c, fn_decl, fn_proto_ty, fn_ty, source_loc, fn_decl_context, is_var_args, cc, is_pub);
52693899}
52703900
52713901fn transFnNoProto(
5272 rp: RestorePoint,
3902 c: *Context,
52733903 fn_ty: *const clang.FunctionType,
52743904 source_loc: clang.SourceLocation,
52753905 fn_decl_context: ?FnDeclContext,
52763906 is_pub: bool,
5277) !*ast.Node.FnProto {
5278 const cc = try transCC(rp, fn_ty, source_loc);
3907) !*ast.Payload.Func {
3908 const cc = try transCC(c, fn_ty, source_loc);
52793909 const is_var_args = if (fn_decl_context) |ctx| (!ctx.is_export and ctx.storage_class != .Static) else true;
5280 return finishTransFnProto(rp, null, null, fn_ty, source_loc, fn_decl_context, is_var_args, cc, is_pub);
3910 return finishTransFnProto(c, null, null, fn_ty, source_loc, fn_decl_context, is_var_args, cc, is_pub);
52813911}
52823912
52833913fn finishTransFnProto(
5284 rp: RestorePoint,
3914 c: *Context,
52853915 fn_decl: ?*const clang.FunctionDecl,
52863916 fn_proto_ty: ?*const clang.FunctionProtoType,
52873917 fn_ty: *const clang.FunctionType,
......@@ -5290,128 +3920,78 @@ fn finishTransFnProto(
52903920 is_var_args: bool,
52913921 cc: CallingConvention,
52923922 is_pub: bool,
5293) !*ast.Node.FnProto {
3923) !*ast.Payload.Func {
52943924 const is_export = if (fn_decl_context) |ctx| ctx.is_export else false;
52953925 const is_extern = if (fn_decl_context) |ctx| !ctx.has_body else false;
3926 const scope = &c.global_scope.base;
52963927
52973928 // TODO check for always_inline attribute
52983929 // TODO check for align attribute
52993930
5300 // pub extern fn name(...) T
5301 const pub_tok = if (is_pub) try appendToken(rp.c, .Keyword_pub, "pub") else null;
5302 const extern_export_inline_tok = if (is_export)
5303 try appendToken(rp.c, .Keyword_export, "export")
5304 else if (is_extern)
5305 try appendToken(rp.c, .Keyword_extern, "extern")
5306 else
5307 null;
5308 const fn_tok = try appendToken(rp.c, .Keyword_fn, "fn");
5309 const name_tok = if (fn_decl_context) |ctx| try appendIdentifier(rp.c, ctx.fn_name) else null;
5310 const lparen_tok = try appendToken(rp.c, .LParen, "(");
5311
5312 var fn_params = std.ArrayList(ast.Node.FnProto.ParamDecl).init(rp.c.gpa);
3931 var fn_params = std.ArrayList(ast.Payload.Param).init(c.gpa);
53133932 defer fn_params.deinit();
53143933 const param_count: usize = if (fn_proto_ty != null) fn_proto_ty.?.getNumParams() else 0;
5315 try fn_params.ensureCapacity(param_count + 1); // +1 for possible var args node
3934 try fn_params.ensureCapacity(param_count);
53163935
53173936 var i: usize = 0;
53183937 while (i < param_count) : (i += 1) {
53193938 const param_qt = fn_proto_ty.?.getParamType(@intCast(c_uint, i));
3939 const is_noalias = param_qt.isRestrictQualified();
53203940
5321 const noalias_tok = if (param_qt.isRestrictQualified()) try appendToken(rp.c, .Keyword_noalias, "noalias") else null;
5322
5323 const param_name_tok: ?ast.TokenIndex = blk: {
5324 if (fn_decl) |decl| {
5325 const param = decl.getParamDecl(@intCast(c_uint, i));
5326 const param_name: []const u8 = try rp.c.str(@ptrCast(*const clang.NamedDecl, param).getName_bytes_begin());
5327 if (param_name.len < 1)
5328 break :blk null;
5329
5330 const result = try appendIdentifier(rp.c, param_name);
5331 _ = try appendToken(rp.c, .Colon, ":");
5332 break :blk result;
5333 }
5334 break :blk null;
5335 };
3941 const param_name: ?[]const u8 =
3942 if (fn_decl) |decl|
3943 blk: {
3944 const param = decl.getParamDecl(@intCast(c_uint, i));
3945 const param_name: []const u8 = try c.str(@ptrCast(*const clang.NamedDecl, param).getName_bytes_begin());
3946 if (param_name.len < 1)
3947 break :blk null;
53363948
5337 const type_node = try transQualType(rp, param_qt, source_loc);
3949 break :blk param_name;
3950 } else null;
3951 const type_node = try transQualType(c, scope, param_qt, source_loc);
53383952
53393953 fn_params.addOneAssumeCapacity().* = .{
5340 .doc_comments = null,
5341 .comptime_token = null,
5342 .noalias_token = noalias_tok,
5343 .name_token = param_name_tok,
5344 .param_type = .{ .type_expr = type_node },
3954 .is_noalias = is_noalias,
3955 .name = param_name,
3956 .type = type_node,
53453957 };
5346
5347 if (i + 1 < param_count) {
5348 _ = try appendToken(rp.c, .Comma, ",");
5349 }
53503958 }
53513959
5352 const var_args_token: ?ast.TokenIndex = if (is_var_args) blk: {
5353 if (param_count > 0) {
5354 _ = try appendToken(rp.c, .Comma, ",");
5355 }
5356 break :blk try appendToken(rp.c, .Ellipsis3, "...");
5357 } else null;
5358
5359 const rparen_tok = try appendToken(rp.c, .RParen, ")");
5360
5361 const linksection_expr = blk: {
3960 const linksection_string = blk: {
53623961 if (fn_decl) |decl| {
53633962 var str_len: usize = undefined;
53643963 if (decl.getSectionAttribute(&str_len)) |str_ptr| {
5365 _ = try appendToken(rp.c, .Keyword_linksection, "linksection");
5366 _ = try appendToken(rp.c, .LParen, "(");
5367 const expr = try transCreateNodeStringLiteral(
5368 rp.c,
5369 try std.fmt.allocPrint(rp.c.arena, "\"{s}\"", .{str_ptr[0..str_len]}),
5370 );
5371 _ = try appendToken(rp.c, .RParen, ")");
5372
5373 break :blk expr;
3964 break :blk str_ptr[0..str_len];
53743965 }
53753966 }
53763967 break :blk null;
53773968 };
53783969
5379 const align_expr = blk: {
3970 const alignment = blk: {
53803971 if (fn_decl) |decl| {
5381 const alignment = decl.getAlignedAttribute(rp.c.clang_context);
3972 const alignment = decl.getAlignedAttribute(c.clang_context);
53823973 if (alignment != 0) {
5383 _ = try appendToken(rp.c, .Keyword_align, "align");
5384 _ = try appendToken(rp.c, .LParen, "(");
53853974 // Clang reports the alignment in bits
5386 const expr = try transCreateNodeInt(rp.c, alignment / 8);
5387 _ = try appendToken(rp.c, .RParen, ")");
5388
5389 break :blk expr;
3975 break :blk alignment / 8;
53903976 }
53913977 }
53923978 break :blk null;
53933979 };
53943980
5395 const callconv_expr = if ((is_export or is_extern) and cc == .C) null else blk: {
5396 _ = try appendToken(rp.c, .Keyword_callconv, "callconv");
5397 _ = try appendToken(rp.c, .LParen, "(");
5398 const expr = try transCreateNodeEnumLiteral(rp.c, @tagName(cc));
5399 _ = try appendToken(rp.c, .RParen, ")");
5400 break :blk expr;
5401 };
3981 const explicit_callconv = if ((is_export or is_extern) and cc == .C) null else cc;
54023982
54033983 const return_type_node = blk: {
54043984 if (fn_ty.getNoReturnAttr()) {
5405 break :blk try transCreateNodeIdentifier(rp.c, "noreturn");
3985 break :blk Tag.noreturn_type.init();
54063986 } else {
54073987 const return_qt = fn_ty.getReturnType();
54083988 if (isCVoid(return_qt)) {
54093989 // convert primitive c_void to actual void (only for return type)
5410 break :blk try transCreateNodeIdentifier(rp.c, "void");
3990 break :blk Tag.void_type.init();
54113991 } else {
5412 break :blk transQualType(rp, return_qt, source_loc) catch |err| switch (err) {
3992 break :blk transQualType(c, scope, return_qt, source_loc) catch |err| switch (err) {
54133993 error.UnsupportedType => {
5414 try emitWarning(rp.c, source_loc, "unsupported function proto return type", .{});
3994 try warn(c, scope, source_loc, "unsupported function proto return type", .{});
54153995 return err;
54163996 },
54173997 error.OutOfMemory => |e| return e,
......@@ -5419,116 +3999,57 @@ fn finishTransFnProto(
54193999 }
54204000 }
54214001 };
4002 const name: ?[]const u8 = if (fn_decl_context) |ctx| ctx.fn_name else null;
4003 const payload = try c.arena.create(ast.Payload.Func);
4004 payload.* = .{
4005 .base = .{ .tag = .func },
4006 .data = .{
4007 .is_pub = is_pub,
4008 .is_extern = is_extern,
4009 .is_export = is_export,
4010 .is_var_args = is_var_args,
4011 .name = name,
4012 .linksection_string = linksection_string,
4013 .explicit_callconv = explicit_callconv,
4014 .params = try c.arena.dupe(ast.Payload.Param, fn_params.items),
4015 .return_type = return_type_node,
4016 .body = null,
4017 .alignment = alignment,
4018 },
4019 };
4020 return payload;
4021}
54224022
5423 // We need to reserve an undefined (but non-null) body node to set later.
5424 var body_node: ?*ast.Node = null;
5425 if (fn_decl_context) |ctx| {
5426 if (ctx.has_body) {
5427 // TODO: we should be able to use undefined here but
5428 // it causes a bug. This is undefined without zig language
5429 // being aware of it.
5430 body_node = @intToPtr(*ast.Node, 0x08);
5431 }
5432 }
5433
5434 const fn_proto = try ast.Node.FnProto.create(rp.c.arena, .{
5435 .params_len = fn_params.items.len,
5436 .return_type = .{ .Explicit = return_type_node },
5437 .fn_token = fn_tok,
5438 }, .{
5439 .visib_token = pub_tok,
5440 .name_token = name_tok,
5441 .extern_export_inline_token = extern_export_inline_tok,
5442 .align_expr = align_expr,
5443 .section_expr = linksection_expr,
5444 .callconv_expr = callconv_expr,
5445 .body_node = body_node,
5446 .var_args_token = var_args_token,
5447 });
5448 mem.copy(ast.Node.FnProto.ParamDecl, fn_proto.params(), fn_params.items);
5449 return fn_proto;
4023fn warn(c: *Context, scope: *Scope, loc: clang.SourceLocation, comptime format: []const u8, args: anytype) !void {
4024 const args_prefix = .{c.locStr(loc)};
4025 const value = try std.fmt.allocPrint(c.arena, "// {s}: warning: " ++ format, args_prefix ++ args);
4026 try scope.appendNode(try Tag.warning.create(c.arena, value));
54504027}
54514028
5452fn revertAndWarn(
5453 rp: RestorePoint,
4029fn fail(
4030 c: *Context,
54544031 err: anytype,
54554032 source_loc: clang.SourceLocation,
54564033 comptime format: []const u8,
54574034 args: anytype,
54584035) (@TypeOf(err) || error{OutOfMemory}) {
5459 rp.activate();
5460 try emitWarning(rp.c, source_loc, format, args);
4036 try warn(c, &c.global_scope.base, source_loc, format, args);
54614037 return err;
54624038}
54634039
5464fn emitWarning(c: *Context, loc: clang.SourceLocation, comptime format: []const u8, args: anytype) !void {
5465 const args_prefix = .{c.locStr(loc)};
5466 _ = try appendTokenFmt(c, .LineComment, "// {s}: warning: " ++ format, args_prefix ++ args);
5467}
5468
5469pub fn failDecl(c: *Context, loc: clang.SourceLocation, name: []const u8, comptime format: []const u8, args: anytype) !void {
4040pub fn failDecl(c: *Context, loc: clang.SourceLocation, name: []const u8, comptime format: []const u8, args: anytype) Error!void {
4041 // location
54704042 // pub const name = @compileError(msg);
5471 const pub_tok = try appendToken(c, .Keyword_pub, "pub");
5472 const const_tok = try appendToken(c, .Keyword_const, "const");
5473 const name_tok = try appendIdentifier(c, name);
5474 const eq_tok = try appendToken(c, .Equal, "=");
5475 const builtin_tok = try appendToken(c, .Builtin, "@compileError");
5476 const lparen_tok = try appendToken(c, .LParen, "(");
5477 const msg_tok = try appendTokenFmt(c, .StringLiteral, "\"" ++ format ++ "\"", args);
5478 const rparen_tok = try appendToken(c, .RParen, ")");
5479 const semi_tok = try appendToken(c, .Semicolon, ";");
5480 _ = try appendTokenFmt(c, .LineComment, "// {s}", .{c.locStr(loc)});
5481
5482 const msg_node = try c.arena.create(ast.Node.OneToken);
5483 msg_node.* = .{
5484 .base = .{ .tag = .StringLiteral },
5485 .token = msg_tok,
5486 };
5487
5488 const call_node = try ast.Node.BuiltinCall.alloc(c.arena, 1);
5489 call_node.* = .{
5490 .builtin_token = builtin_tok,
5491 .params_len = 1,
5492 .rparen_token = rparen_tok,
5493 };
5494 call_node.params()[0] = &msg_node.base;
5495
5496 const var_decl_node = try ast.Node.VarDecl.create(c.arena, .{
5497 .name_token = name_tok,
5498 .mut_token = const_tok,
5499 .semicolon_token = semi_tok,
5500 }, .{
5501 .visib_token = pub_tok,
5502 .eq_token = eq_tok,
5503 .init_node = &call_node.base,
5504 });
5505 try addTopLevelDecl(c, name, &var_decl_node.base);
5506}
5507
5508fn appendToken(c: *Context, token_id: Token.Id, bytes: []const u8) !ast.TokenIndex {
5509 std.debug.assert(token_id != .Identifier); // use appendIdentifier
5510 return appendTokenFmt(c, token_id, "{s}", .{bytes});
4043 const fail_msg = try std.fmt.allocPrint(c.arena, format, args);
4044 try addTopLevelDecl(c, name, try Tag.fail_decl.create(c.arena, .{ .actual = name, .mangled = fail_msg }));
4045 const location_comment = try std.fmt.allocPrint(c.arena, "// {s}", .{c.locStr(loc)});
4046 try c.global_scope.nodes.append(try Tag.warning.create(c.arena, location_comment));
55114047}
55124048
5513fn appendTokenFmt(c: *Context, token_id: Token.Id, comptime format: []const u8, args: anytype) !ast.TokenIndex {
5514 assert(token_id != .Invalid);
5515
5516 try c.token_ids.ensureCapacity(c.gpa, c.token_ids.items.len + 1);
5517 try c.token_locs.ensureCapacity(c.gpa, c.token_locs.items.len + 1);
5518
5519 const start_index = c.source_buffer.items.len;
5520 try c.source_buffer.writer().print(format ++ " ", args);
5521
5522 c.token_ids.appendAssumeCapacity(token_id);
5523 c.token_locs.appendAssumeCapacity(.{
5524 .start = start_index,
5525 .end = c.source_buffer.items.len - 1, // back up before the space
5526 });
5527
5528 return c.token_ids.items.len - 1;
4049pub fn freeErrors(errors: []ClangErrMsg) void {
4050 errors.ptr.delete(errors.len);
55294051}
55304052
5531// TODO hook up with codegen
55324053fn isZigPrimitiveType(name: []const u8) bool {
55334054 if (name.len > 1 and (name[0] == 'u' or name[0] == 'i')) {
55344055 for (name[1..]) |c| {
......@@ -5539,56 +4060,7 @@ fn isZigPrimitiveType(name: []const u8) bool {
55394060 }
55404061 return true;
55414062 }
5542 // void is invalid in c so it doesn't need to be checked.
5543 return mem.eql(u8, name, "comptime_float") or
5544 mem.eql(u8, name, "comptime_int") or
5545 mem.eql(u8, name, "bool") or
5546 mem.eql(u8, name, "isize") or
5547 mem.eql(u8, name, "usize") or
5548 mem.eql(u8, name, "f16") or
5549 mem.eql(u8, name, "f32") or
5550 mem.eql(u8, name, "f64") or
5551 mem.eql(u8, name, "f128") or
5552 mem.eql(u8, name, "c_longdouble") or
5553 mem.eql(u8, name, "noreturn") or
5554 mem.eql(u8, name, "type") or
5555 mem.eql(u8, name, "anyerror") or
5556 mem.eql(u8, name, "c_short") or
5557 mem.eql(u8, name, "c_ushort") or
5558 mem.eql(u8, name, "c_int") or
5559 mem.eql(u8, name, "c_uint") or
5560 mem.eql(u8, name, "c_long") or
5561 mem.eql(u8, name, "c_ulong") or
5562 mem.eql(u8, name, "c_longlong") or
5563 mem.eql(u8, name, "c_ulonglong");
5564}
5565
5566fn appendIdentifier(c: *Context, name: []const u8) !ast.TokenIndex {
5567 return appendTokenFmt(c, .Identifier, "{}", .{std.zig.fmtId(name)});
5568}
5569
5570fn transCreateNodeIdentifier(c: *Context, name: []const u8) !*ast.Node {
5571 const token_index = try appendIdentifier(c, name);
5572 const identifier = try c.arena.create(ast.Node.OneToken);
5573 identifier.* = .{
5574 .base = .{ .tag = .Identifier },
5575 .token = token_index,
5576 };
5577 return &identifier.base;
5578}
5579
5580fn transCreateNodeIdentifierUnchecked(c: *Context, name: []const u8) !*ast.Node {
5581 const token_index = try appendTokenFmt(c, .Identifier, "{s}", .{name});
5582 const identifier = try c.arena.create(ast.Node.OneToken);
5583 identifier.* = .{
5584 .base = .{ .tag = .Identifier },
5585 .token = token_index,
5586 };
5587 return &identifier.base;
5588}
5589
5590pub fn freeErrors(errors: []ClangErrMsg) void {
5591 errors.ptr.delete(errors.len);
4063 return @import("astgen.zig").simple_types.has(name);
55924064}
55934065
55944066const MacroCtx = struct {
......@@ -5709,27 +4181,13 @@ fn transPreprocessorEntities(c: *Context, unit: *clang.ASTUnit) Error!void {
57094181fn transMacroDefine(c: *Context, m: *MacroCtx) ParseError!void {
57104182 const scope = &c.global_scope.base;
57114183
5712 const visib_tok = try appendToken(c, .Keyword_pub, "pub");
5713 const mut_tok = try appendToken(c, .Keyword_const, "const");
5714 const name_tok = try appendIdentifier(c, m.name);
5715 const eq_token = try appendToken(c, .Equal, "=");
5716
57174184 const init_node = try parseCExpr(c, m, scope);
57184185 const last = m.next().?;
57194186 if (last != .Eof and last != .Nl)
57204187 return m.fail(c, "unable to translate C expr: unexpected token .{s}", .{@tagName(last)});
57214188
5722 const semicolon_token = try appendToken(c, .Semicolon, ";");
5723 const node = try ast.Node.VarDecl.create(c.arena, .{
5724 .name_token = name_tok,
5725 .mut_token = mut_tok,
5726 .semicolon_token = semicolon_token,
5727 }, .{
5728 .visib_token = visib_tok,
5729 .eq_token = eq_token,
5730 .init_node = init_node,
5731 });
5732 _ = try c.global_scope.macro_table.put(m.name, &node.base);
4189 const var_decl = try Tag.pub_var_simple.create(c.arena, .{ .name = m.name, .init = init_node });
4190 _ = try c.global_scope.macro_table.put(m.name, var_decl);
57334191}
57344192
57354193fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {
......@@ -5737,16 +4195,11 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {
57374195 defer block_scope.deinit();
57384196 const scope = &block_scope.base;
57394197
5740 const pub_tok = try appendToken(c, .Keyword_pub, "pub");
5741 const fn_tok = try appendToken(c, .Keyword_fn, "fn");
5742 const name_tok = try appendIdentifier(c, m.name);
5743 _ = try appendToken(c, .LParen, "(");
5744
57454198 if (m.next().? != .LParen) {
57464199 return m.fail(c, "unable to translate C expr: expected '('", .{});
57474200 }
57484201
5749 var fn_params = std.ArrayList(ast.Node.FnProto.ParamDecl).init(c.gpa);
4202 var fn_params = std.ArrayList(ast.Payload.Param).init(c.gpa);
57504203 defer fn_params.deinit();
57514204
57524205 while (true) {
......@@ -5754,120 +4207,82 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {
57544207 _ = m.next();
57554208
57564209 const mangled_name = try block_scope.makeMangledName(c, m.slice());
5757 const param_name_tok = try appendIdentifier(c, mangled_name);
5758 _ = try appendToken(c, .Colon, ":");
5759
5760 const any_type = try c.arena.create(ast.Node.OneToken);
5761 any_type.* = .{
5762 .base = .{ .tag = .AnyType },
5763 .token = try appendToken(c, .Keyword_anytype, "anytype"),
5764 };
5765
5766 (try fn_params.addOne()).* = .{
5767 .doc_comments = null,
5768 .comptime_token = null,
5769 .noalias_token = null,
5770 .name_token = param_name_tok,
5771 .param_type = .{ .any_type = &any_type.base },
5772 };
4210 try fn_params.append(.{
4211 .is_noalias = false,
4212 .name = mangled_name,
4213 .type = Tag.@"anytype".init(),
4214 });
57734215
57744216 if (m.peek().? != .Comma) break;
57754217 _ = m.next();
5776 _ = try appendToken(c, .Comma, ",");
57774218 }
57784219
57794220 if (m.next().? != .RParen) {
57804221 return m.fail(c, "unable to translate C expr: expected ')'", .{});
57814222 }
57824223
5783 _ = try appendToken(c, .RParen, ")");
5784
5785 _ = try appendToken(c, .Keyword_callconv, "callconv");
5786 _ = try appendToken(c, .LParen, "(");
5787 const callconv_expr = try transCreateNodeEnumLiteral(c, "Inline");
5788 _ = try appendToken(c, .RParen, ")");
5789
5790 const type_of = try c.createBuiltinCall("@TypeOf", 1);
5791
5792 const return_kw = try appendToken(c, .Keyword_return, "return");
57934224 const expr = try parseCExpr(c, m, scope);
57944225 const last = m.next().?;
57954226 if (last != .Eof and last != .Nl)
57964227 return m.fail(c, "unable to translate C expr: unexpected token .{s}", .{@tagName(last)});
5797 _ = try appendToken(c, .Semicolon, ";");
5798 const type_of_arg = if (!expr.tag.isBlock()) expr else blk: {
5799 const stmts = expr.blockStatements();
4228
4229 const typeof_arg = if (expr.castTag(.block)) |some| blk: {
4230 const stmts = some.data.stmts;
58004231 const blk_last = stmts[stmts.len - 1];
5801 const br = blk_last.cast(ast.Node.ControlFlowExpression).?;
5802 break :blk br.getRHS().?;
5803 };
5804 type_of.params()[0] = type_of_arg;
5805 type_of.rparen_token = try appendToken(c, .RParen, ")");
5806 const return_expr = try ast.Node.ControlFlowExpression.create(c.arena, .{
5807 .ltoken = return_kw,
5808 .tag = .Return,
5809 }, .{
5810 .rhs = expr,
5811 });
4232 const br = blk_last.castTag(.break_val).?;
4233 break :blk br.data.val;
4234 } else expr;
4235 const return_type = if (typeof_arg.castTag(.std_meta_cast)) |some|
4236 some.data.lhs
4237 else
4238 try Tag.typeof.create(c.arena, typeof_arg);
58124239
5813 try block_scope.statements.append(&return_expr.base);
5814 const block_node = try block_scope.complete(c);
5815 const fn_proto = try ast.Node.FnProto.create(c.arena, .{
5816 .fn_token = fn_tok,
5817 .params_len = fn_params.items.len,
5818 .return_type = .{ .Explicit = &type_of.base },
5819 }, .{
5820 .visib_token = pub_tok,
5821 .name_token = name_tok,
5822 .body_node = block_node,
5823 .callconv_expr = callconv_expr,
5824 });
5825 mem.copy(ast.Node.FnProto.ParamDecl, fn_proto.params(), fn_params.items);
4240 const return_expr = try Tag.@"return".create(c.arena, expr);
4241 try block_scope.statements.append(return_expr);
58264242
5827 _ = try c.global_scope.macro_table.put(m.name, &fn_proto.base);
4243 const fn_decl = try Tag.pub_inline_fn.create(c.arena, .{
4244 .name = m.name,
4245 .params = try c.arena.dupe(ast.Payload.Param, fn_params.items),
4246 .return_type = return_type,
4247 .body = try block_scope.complete(c),
4248 });
4249 _ = try c.global_scope.macro_table.put(m.name, fn_decl);
58284250}
58294251
58304252const ParseError = Error || error{ParseError};
58314253
5832fn parseCExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
4254fn parseCExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
58334255 // TODO parseCAssignExpr here
58344256 const node = try parseCCondExpr(c, m, scope);
58354257 if (m.next().? != .Comma) {
58364258 m.i -= 1;
58374259 return node;
58384260 }
5839 _ = try appendToken(c, .Semicolon, ";");
58404261 var block_scope = try Scope.Block.init(c, scope, true);
58414262 defer block_scope.deinit();
58424263
58434264 var last = node;
58444265 while (true) {
58454266 // suppress result
5846 const lhs = try transCreateNodeIdentifier(c, "_");
5847 const op_token = try appendToken(c, .Equal, "=");
5848 const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
5849 op_node.* = .{
5850 .base = .{ .tag = .Assign },
5851 .op_token = op_token,
5852 .lhs = lhs,
5853 .rhs = last,
5854 };
5855 try block_scope.statements.append(&op_node.base);
4267 const ignore = try Tag.discard.create(c.arena, last);
4268 try block_scope.statements.append(ignore);
58564269
58574270 last = try parseCCondExpr(c, m, scope);
5858 _ = try appendToken(c, .Semicolon, ";");
58594271 if (m.next().? != .Comma) {
58604272 m.i -= 1;
58614273 break;
58624274 }
58634275 }
58644276
5865 const break_node = try transCreateNodeBreak(c, block_scope.label, last);
5866 try block_scope.statements.append(&break_node.base);
4277 const break_node = try Tag.break_val.create(c.arena, .{
4278 .label = block_scope.label,
4279 .val = last,
4280 });
4281 try block_scope.statements.append(break_node);
58674282 return try block_scope.complete(c);
58684283}
58694284
5870fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!*ast.Node {
4285fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!Node {
58714286 var lit_bytes = m.slice();
58724287
58734288 switch (m.list[m.i].id) {
......@@ -5887,11 +4302,10 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!*ast.Node {
58874302 }
58884303
58894304 if (suffix == .none) {
5890 return transCreateNodeInt(c, lit_bytes);
4305 return transCreateNodeNumber(c, lit_bytes, .int);
58914306 }
58924307
5893 const cast_node = try c.createBuiltinCall("@as", 2);
5894 cast_node.params()[0] = try transCreateNodeIdentifier(c, switch (suffix) {
4308 const type_node = try Tag.type.create(c.arena, switch (suffix) {
58954309 .u => "c_uint",
58964310 .l => "c_long",
58974311 .lu => "c_ulong",
......@@ -5905,27 +4319,22 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!*ast.Node {
59054319 .llu => 3,
59064320 else => unreachable,
59074321 }];
5908 _ = try appendToken(c, .Comma, ",");
5909 cast_node.params()[1] = try transCreateNodeInt(c, lit_bytes);
5910 cast_node.rparen_token = try appendToken(c, .RParen, ")");
5911 return &cast_node.base;
4322 const rhs = try transCreateNodeNumber(c, lit_bytes, .int);
4323 return Tag.as.create(c.arena, .{ .lhs = type_node, .rhs = rhs });
59124324 },
59134325 .FloatLiteral => |suffix| {
59144326 if (lit_bytes[0] == '.')
59154327 lit_bytes = try std.fmt.allocPrint(c.arena, "0{s}", .{lit_bytes});
59164328 if (suffix == .none) {
5917 return transCreateNodeFloat(c, lit_bytes);
4329 return transCreateNodeNumber(c, lit_bytes, .float);
59184330 }
5919 const cast_node = try c.createBuiltinCall("@as", 2);
5920 cast_node.params()[0] = try transCreateNodeIdentifier(c, switch (suffix) {
4331 const type_node = try Tag.type.create(c.arena, switch (suffix) {
59214332 .f => "f32",
59224333 .l => "c_longdouble",
59234334 else => unreachable,
59244335 });
5925 _ = try appendToken(c, .Comma, ",");
5926 cast_node.params()[1] = try transCreateNodeFloat(c, lit_bytes[0 .. lit_bytes.len - 1]);
5927 cast_node.rparen_token = try appendToken(c, .RParen, ")");
5928 return &cast_node.base;
4336 const rhs = try transCreateNodeNumber(c, lit_bytes[0 .. lit_bytes.len - 1], .float);
4337 return Tag.as.create(c.arena, .{ .lhs = type_node, .rhs = rhs });
59294338 },
59304339 else => unreachable,
59314340 }
......@@ -6091,79 +4500,62 @@ fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 {
60914500 return bytes[0..i];
60924501}
60934502
6094fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
4503fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
60954504 const tok = m.next().?;
60964505 const slice = m.slice();
60974506 switch (tok) {
60984507 .CharLiteral => {
60994508 if (slice[0] != '\'' or slice[1] == '\\' or slice.len == 3) {
6100 const token = try appendToken(c, .CharLiteral, try zigifyEscapeSequences(c, m));
6101 const node = try c.arena.create(ast.Node.OneToken);
6102 node.* = .{
6103 .base = .{ .tag = .CharLiteral },
6104 .token = token,
6105 };
6106 return &node.base;
4509 return Tag.char_literal.create(c.arena, try zigifyEscapeSequences(c, m));
61074510 } else {
6108 const token = try appendTokenFmt(c, .IntegerLiteral, "0x{x}", .{slice[1 .. slice.len - 1]});
6109 const node = try c.arena.create(ast.Node.OneToken);
6110 node.* = .{
6111 .base = .{ .tag = .IntegerLiteral },
6112 .token = token,
6113 };
6114 return &node.base;
4511 const str = try std.fmt.allocPrint(c.arena, "0x{x}", .{slice[1 .. slice.len - 1]});
4512 return Tag.integer_literal.create(c.arena, str);
61154513 }
61164514 },
61174515 .StringLiteral => {
6118 const token = try appendToken(c, .StringLiteral, try zigifyEscapeSequences(c, m));
6119 const node = try c.arena.create(ast.Node.OneToken);
6120 node.* = .{
6121 .base = .{ .tag = .StringLiteral },
6122 .token = token,
6123 };
6124 return &node.base;
4516 return Tag.string_literal.create(c.arena, try zigifyEscapeSequences(c, m));
61254517 },
61264518 .IntegerLiteral, .FloatLiteral => {
61274519 return parseCNumLit(c, m);
61284520 },
61294521 // eventually this will be replaced by std.c.parse which will handle these correctly
6130 .Keyword_void => return transCreateNodeIdentifierUnchecked(c, "c_void"),
6131 .Keyword_bool => return transCreateNodeIdentifierUnchecked(c, "bool"),
6132 .Keyword_double => return transCreateNodeIdentifierUnchecked(c, "f64"),
6133 .Keyword_long => return transCreateNodeIdentifierUnchecked(c, "c_long"),
6134 .Keyword_int => return transCreateNodeIdentifierUnchecked(c, "c_int"),
6135 .Keyword_float => return transCreateNodeIdentifierUnchecked(c, "f32"),
6136 .Keyword_short => return transCreateNodeIdentifierUnchecked(c, "c_short"),
6137 .Keyword_char => return transCreateNodeIdentifierUnchecked(c, "u8"),
4522 .Keyword_void => return Tag.type.create(c.arena, "c_void"),
4523 .Keyword_bool => return Tag.type.create(c.arena, "bool"),
4524 .Keyword_double => return Tag.type.create(c.arena, "f64"),
4525 .Keyword_long => return Tag.type.create(c.arena, "c_long"),
4526 .Keyword_int => return Tag.type.create(c.arena, "c_int"),
4527 .Keyword_float => return Tag.type.create(c.arena, "f32"),
4528 .Keyword_short => return Tag.type.create(c.arena, "c_short"),
4529 .Keyword_char => return Tag.type.create(c.arena, "u8"),
61384530 .Keyword_unsigned => if (m.next()) |t| switch (t) {
6139 .Keyword_char => return transCreateNodeIdentifierUnchecked(c, "u8"),
6140 .Keyword_short => return transCreateNodeIdentifierUnchecked(c, "c_ushort"),
6141 .Keyword_int => return transCreateNodeIdentifierUnchecked(c, "c_uint"),
4531 .Keyword_char => return Tag.type.create(c.arena, "u8"),
4532 .Keyword_short => return Tag.type.create(c.arena, "c_ushort"),
4533 .Keyword_int => return Tag.type.create(c.arena, "c_uint"),
61424534 .Keyword_long => if (m.peek() != null and m.peek().? == .Keyword_long) {
61434535 _ = m.next();
6144 return transCreateNodeIdentifierUnchecked(c, "c_ulonglong");
6145 } else return transCreateNodeIdentifierUnchecked(c, "c_ulong"),
4536 return Tag.type.create(c.arena, "c_ulonglong");
4537 } else return Tag.type.create(c.arena, "c_ulong"),
61464538 else => {
61474539 m.i -= 1;
6148 return transCreateNodeIdentifierUnchecked(c, "c_uint");
4540 return Tag.type.create(c.arena, "c_uint");
61494541 },
61504542 } else {
6151 return transCreateNodeIdentifierUnchecked(c, "c_uint");
4543 return Tag.type.create(c.arena, "c_uint");
61524544 },
61534545 .Keyword_signed => if (m.next()) |t| switch (t) {
6154 .Keyword_char => return transCreateNodeIdentifierUnchecked(c, "i8"),
6155 .Keyword_short => return transCreateNodeIdentifierUnchecked(c, "c_short"),
6156 .Keyword_int => return transCreateNodeIdentifierUnchecked(c, "c_int"),
4546 .Keyword_char => return Tag.type.create(c.arena, "i8"),
4547 .Keyword_short => return Tag.type.create(c.arena, "c_short"),
4548 .Keyword_int => return Tag.type.create(c.arena, "c_int"),
61574549 .Keyword_long => if (m.peek() != null and m.peek().? == .Keyword_long) {
61584550 _ = m.next();
6159 return transCreateNodeIdentifierUnchecked(c, "c_longlong");
6160 } else return transCreateNodeIdentifierUnchecked(c, "c_long"),
4551 return Tag.type.create(c.arena, "c_longlong");
4552 } else return Tag.type.create(c.arena, "c_long"),
61614553 else => {
61624554 m.i -= 1;
6163 return transCreateNodeIdentifierUnchecked(c, "c_int");
4555 return Tag.type.create(c.arena, "c_int");
61644556 },
61654557 } else {
6166 return transCreateNodeIdentifierUnchecked(c, "c_int");
4558 return Tag.type.create(c.arena, "c_int");
61674559 },
61684560 .Keyword_enum, .Keyword_struct, .Keyword_union => {
61694561 // struct Foo will be declared as struct_Foo by transRecordDecl
......@@ -6173,17 +4565,12 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*
61734565 return error.ParseError;
61744566 }
61754567
6176 const ident_token = try appendTokenFmt(c, .Identifier, "{s}_{s}", .{ slice, m.slice() });
6177 const identifier = try c.arena.create(ast.Node.OneToken);
6178 identifier.* = .{
6179 .base = .{ .tag = .Identifier },
6180 .token = ident_token,
6181 };
6182 return &identifier.base;
4568 const name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ slice, m.slice() });
4569 return Tag.identifier.create(c.arena, name);
61834570 },
61844571 .Identifier => {
61854572 const mangled_name = scope.getAlias(slice);
6186 return transCreateNodeIdentifier(c, checkForBuiltinTypedef(mangled_name) orelse mangled_name);
4573 return Tag.identifier.create(c.arena, builtin_typedef_map.get(mangled_name) orelse mangled_name);
61874574 },
61884575 .LParen => {
61894576 const inner_node = try parseCExpr(c, m, scope);
......@@ -6213,10 +4600,6 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*
62134600 },
62144601 else => return inner_node,
62154602 }
6216
6217 // hack to get zig fmt to render a comma in builtin calls
6218 _ = try appendToken(c, .Comma, ",");
6219
62204603 const node_to_cast = try parseCExpr(c, m, scope);
62214604
62224605 if (saw_l_paren and m.next().? != .RParen) {
......@@ -6224,28 +4607,7 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*
62244607 return error.ParseError;
62254608 }
62264609
6227 const lparen = try appendToken(c, .LParen, "(");
6228
6229 //(@import("std").meta.cast(dest, x))
6230 const import_fn_call = try c.createBuiltinCall("@import", 1);
6231 const std_node = try transCreateNodeStringLiteral(c, "\"std\"");
6232 import_fn_call.params()[0] = std_node;
6233 import_fn_call.rparen_token = try appendToken(c, .RParen, ")");
6234 const inner_field_access = try transCreateNodeFieldAccess(c, &import_fn_call.base, "meta");
6235 const outer_field_access = try transCreateNodeFieldAccess(c, inner_field_access, "cast");
6236
6237 const cast_fn_call = try c.createCall(outer_field_access, 2);
6238 cast_fn_call.params()[0] = inner_node;
6239 cast_fn_call.params()[1] = node_to_cast;
6240 cast_fn_call.rtoken = try appendToken(c, .RParen, ")");
6241
6242 const group_node = try c.arena.create(ast.Node.GroupedExpression);
6243 group_node.* = .{
6244 .lparen = lparen,
6245 .expr = &cast_fn_call.base,
6246 .rparen = try appendToken(c, .RParen, ")"),
6247 };
6248 return &group_node.base;
4610 return Tag.std_meta_cast.create(c.arena, .{ .lhs = inner_node, .rhs = node_to_cast });
62494611 },
62504612 else => {
62514613 try m.fail(c, "unable to translate C expr: unexpected token .{s}", .{@tagName(tok)});
......@@ -6254,447 +4616,256 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*
62544616 }
62554617}
62564618
6257fn parseCPrimaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
4619fn parseCPrimaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
62584620 var node = try parseCPrimaryExprInner(c, m, scope);
62594621 // In C the preprocessor would handle concatting strings while expanding macros.
62604622 // This should do approximately the same by concatting any strings and identifiers
62614623 // after a primary expression.
62624624 while (true) {
6263 var op_token: ast.TokenIndex = undefined;
6264 var op_id: ast.Node.Tag = undefined;
62654625 switch (m.peek().?) {
62664626 .StringLiteral, .Identifier => {},
62674627 else => break,
62684628 }
6269 const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
6270 op_node.* = .{
6271 .base = .{ .tag = .ArrayCat },
6272 .op_token = try appendToken(c, .PlusPlus, "++"),
6273 .lhs = node,
6274 .rhs = try parseCPrimaryExprInner(c, m, scope),
6275 };
6276 node = &op_node.base;
4629 node = try Tag.array_cat.create(c.arena, .{ .lhs = node, .rhs = try parseCPrimaryExprInner(c, m, scope) });
62774630 }
62784631 return node;
62794632}
62804633
6281fn nodeIsInfixOp(tag: ast.Node.Tag) bool {
6282 return switch (tag) {
6283 .Add,
6284 .AddWrap,
6285 .ArrayCat,
6286 .ArrayMult,
6287 .Assign,
6288 .AssignBitAnd,
6289 .AssignBitOr,
6290 .AssignBitShiftLeft,
6291 .AssignBitShiftRight,
6292 .AssignBitXor,
6293 .AssignDiv,
6294 .AssignSub,
6295 .AssignSubWrap,
6296 .AssignMod,
6297 .AssignAdd,
6298 .AssignAddWrap,
6299 .AssignMul,
6300 .AssignMulWrap,
6301 .BangEqual,
6302 .BitAnd,
6303 .BitOr,
6304 .BitShiftLeft,
6305 .BitShiftRight,
6306 .BitXor,
6307 .BoolAnd,
6308 .BoolOr,
6309 .Div,
6310 .EqualEqual,
6311 .ErrorUnion,
6312 .GreaterOrEqual,
6313 .GreaterThan,
6314 .LessOrEqual,
6315 .LessThan,
6316 .MergeErrorSets,
6317 .Mod,
6318 .Mul,
6319 .MulWrap,
6320 .Period,
6321 .Range,
6322 .Sub,
6323 .SubWrap,
6324 .UnwrapOptional,
6325 .Catch,
6326 => true,
6327
6328 else => false,
6329 };
6330}
6331
6332fn macroBoolToInt(c: *Context, node: *ast.Node) !*ast.Node {
4634fn macroBoolToInt(c: *Context, node: Node) !Node {
63334635 if (!isBoolRes(node)) {
6334 if (!nodeIsInfixOp(node.tag)) return node;
6335
6336 const group_node = try c.arena.create(ast.Node.GroupedExpression);
6337 group_node.* = .{
6338 .lparen = try appendToken(c, .LParen, "("),
6339 .expr = node,
6340 .rparen = try appendToken(c, .RParen, ")"),
6341 };
6342 return &group_node.base;
4636 return node;
63434637 }
63444638
6345 const builtin_node = try c.createBuiltinCall("@boolToInt", 1);
6346 builtin_node.params()[0] = node;
6347 builtin_node.rparen_token = try appendToken(c, .RParen, ")");
6348 return &builtin_node.base;
4639 return Tag.bool_to_int.create(c.arena, node);
63494640}
63504641
6351fn macroIntToBool(c: *Context, node: *ast.Node) !*ast.Node {
4642fn macroIntToBool(c: *Context, node: Node) !Node {
63524643 if (isBoolRes(node)) {
6353 if (!nodeIsInfixOp(node.tag)) return node;
6354
6355 const group_node = try c.arena.create(ast.Node.GroupedExpression);
6356 group_node.* = .{
6357 .lparen = try appendToken(c, .LParen, "("),
6358 .expr = node,
6359 .rparen = try appendToken(c, .RParen, ")"),
6360 };
6361 return &group_node.base;
4644 return node;
63624645 }
63634646
6364 const op_token = try appendToken(c, .BangEqual, "!=");
6365 const zero = try transCreateNodeInt(c, 0);
6366 const res = try c.arena.create(ast.Node.SimpleInfixOp);
6367 res.* = .{
6368 .base = .{ .tag = .BangEqual },
6369 .op_token = op_token,
6370 .lhs = node,
6371 .rhs = zero,
6372 };
6373 const group_node = try c.arena.create(ast.Node.GroupedExpression);
6374 group_node.* = .{
6375 .lparen = try appendToken(c, .LParen, "("),
6376 .expr = &res.base,
6377 .rparen = try appendToken(c, .RParen, ")"),
6378 };
6379 return &group_node.base;
4647 return Tag.not_equal.create(c.arena, .{ .lhs = node, .rhs = Tag.zero_literal.init() });
63804648}
63814649
6382fn macroGroup(c: *Context, node: *ast.Node) !*ast.Node {
6383 if (!nodeIsInfixOp(node.tag)) return node;
6384
6385 const group_node = try c.arena.create(ast.Node.GroupedExpression);
6386 group_node.* = .{
6387 .lparen = try appendToken(c, .LParen, "("),
6388 .expr = node,
6389 .rparen = try appendToken(c, .RParen, ")"),
6390 };
6391 return &group_node.base;
6392}
6393
6394fn parseCCondExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
4650fn parseCCondExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
63954651 const node = try parseCOrExpr(c, m, scope);
63964652 if (m.peek().? != .QuestionMark) {
63974653 return node;
63984654 }
63994655 _ = m.next();
64004656
6401 // must come immediately after expr
6402 _ = try appendToken(c, .RParen, ")");
6403 const if_node = try transCreateNodeIf(c);
6404 if_node.condition = node;
6405 if_node.body = try parseCOrExpr(c, m, scope);
4657 const then_body = try parseCOrExpr(c, m, scope);
64064658 if (m.next().? != .Colon) {
64074659 try m.fail(c, "unable to translate C expr: expected ':'", .{});
64084660 return error.ParseError;
64094661 }
6410 if_node.@"else" = try transCreateNodeElse(c);
6411 if_node.@"else".?.body = try parseCCondExpr(c, m, scope);
6412 return &if_node.base;
4662 const else_body = try parseCCondExpr(c, m, scope);
4663 return Tag.@"if".create(c.arena, .{ .cond = node, .then = then_body, .@"else" = else_body });
64134664}
64144665
6415fn parseCOrExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
4666fn parseCOrExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
64164667 var node = try parseCAndExpr(c, m, scope);
64174668 while (m.next().? == .PipePipe) {
6418 const lhs_node = try macroIntToBool(c, node);
6419 const op_token = try appendToken(c, .Keyword_or, "or");
6420 const rhs_node = try parseCAndExpr(c, m, scope);
6421 const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
6422 op_node.* = .{
6423 .base = .{ .tag = .BoolOr },
6424 .op_token = op_token,
6425 .lhs = lhs_node,
6426 .rhs = try macroIntToBool(c, rhs_node),
6427 };
6428 node = &op_node.base;
4669 const lhs = try macroIntToBool(c, node);
4670 const rhs = try macroIntToBool(c, try parseCAndExpr(c, m, scope));
4671 node = try Tag.@"or".create(c.arena, .{ .lhs = lhs, .rhs = rhs });
64294672 }
64304673 m.i -= 1;
64314674 return node;
64324675}
64334676
6434fn parseCAndExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
4677fn parseCAndExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
64354678 var node = try parseCBitOrExpr(c, m, scope);
64364679 while (m.next().? == .AmpersandAmpersand) {
6437 const lhs_node = try macroIntToBool(c, node);
6438 const op_token = try appendToken(c, .Keyword_and, "and");
6439 const rhs_node = try parseCBitOrExpr(c, m, scope);
6440 const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
6441 op_node.* = .{
6442 .base = .{ .tag = .BoolAnd },
6443 .op_token = op_token,
6444 .lhs = lhs_node,
6445 .rhs = try macroIntToBool(c, rhs_node),
6446 };
6447 node = &op_node.base;
4680 const lhs = try macroIntToBool(c, node);
4681 const rhs = try macroIntToBool(c, try parseCBitOrExpr(c, m, scope));
4682 node = try Tag.@"and".create(c.arena, .{ .lhs = lhs, .rhs = rhs });
64484683 }
64494684 m.i -= 1;
64504685 return node;
64514686}
64524687
6453fn parseCBitOrExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
4688fn parseCBitOrExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
64544689 var node = try parseCBitXorExpr(c, m, scope);
64554690 while (m.next().? == .Pipe) {
6456 const lhs_node = try macroBoolToInt(c, node);
6457 const op_token = try appendToken(c, .Pipe, "|");
6458 const rhs_node = try parseCBitXorExpr(c, m, scope);
6459 const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
6460 op_node.* = .{
6461 .base = .{ .tag = .BitOr },
6462 .op_token = op_token,
6463 .lhs = lhs_node,
6464 .rhs = try macroBoolToInt(c, rhs_node),
6465 };
6466 node = &op_node.base;
4691 const lhs = try macroBoolToInt(c, node);
4692 const rhs = try macroBoolToInt(c, try parseCBitXorExpr(c, m, scope));
4693 node = try Tag.bit_or.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
64674694 }
64684695 m.i -= 1;
64694696 return node;
64704697}
64714698
6472fn parseCBitXorExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
4699fn parseCBitXorExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
64734700 var node = try parseCBitAndExpr(c, m, scope);
64744701 while (m.next().? == .Caret) {
6475 const lhs_node = try macroBoolToInt(c, node);
6476 const op_token = try appendToken(c, .Caret, "^");
6477 const rhs_node = try parseCBitAndExpr(c, m, scope);
6478 const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
6479 op_node.* = .{
6480 .base = .{ .tag = .BitXor },
6481 .op_token = op_token,
6482 .lhs = lhs_node,
6483 .rhs = try macroBoolToInt(c, rhs_node),
6484 };
6485 node = &op_node.base;
4702 const lhs = try macroBoolToInt(c, node);
4703 const rhs = try macroBoolToInt(c, try parseCBitAndExpr(c, m, scope));
4704 node = try Tag.bit_xor.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
64864705 }
64874706 m.i -= 1;
64884707 return node;
64894708}
64904709
6491fn parseCBitAndExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
4710fn parseCBitAndExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
64924711 var node = try parseCEqExpr(c, m, scope);
64934712 while (m.next().? == .Ampersand) {
6494 const lhs_node = try macroBoolToInt(c, node);
6495 const op_token = try appendToken(c, .Ampersand, "&");
6496 const rhs_node = try parseCEqExpr(c, m, scope);
6497 const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
6498 op_node.* = .{
6499 .base = .{ .tag = .BitAnd },
6500 .op_token = op_token,
6501 .lhs = lhs_node,
6502 .rhs = try macroBoolToInt(c, rhs_node),
6503 };
6504 node = &op_node.base;
4713 const lhs = try macroBoolToInt(c, node);
4714 const rhs = try macroBoolToInt(c, try parseCEqExpr(c, m, scope));
4715 node = try Tag.bit_and.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
65054716 }
65064717 m.i -= 1;
65074718 return node;
65084719}
65094720
6510fn parseCEqExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
4721fn parseCEqExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
65114722 var node = try parseCRelExpr(c, m, scope);
65124723 while (true) {
6513 var op_token: ast.TokenIndex = undefined;
6514 var op_id: ast.Node.Tag = undefined;
65154724 switch (m.peek().?) {
65164725 .BangEqual => {
6517 op_token = try appendToken(c, .BangEqual, "!=");
6518 op_id = .BangEqual;
4726 _ = m.next();
4727 const lhs = try macroBoolToInt(c, node);
4728 const rhs = try macroBoolToInt(c, try parseCRelExpr(c, m, scope));
4729 node = try Tag.not_equal.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
65194730 },
65204731 .EqualEqual => {
6521 op_token = try appendToken(c, .EqualEqual, "==");
6522 op_id = .EqualEqual;
4732 _ = m.next();
4733 const lhs = try macroBoolToInt(c, node);
4734 const rhs = try macroBoolToInt(c, try parseCRelExpr(c, m, scope));
4735 node = try Tag.equal.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
65234736 },
65244737 else => return node,
65254738 }
6526 _ = m.next();
6527 const lhs_node = try macroBoolToInt(c, node);
6528 const rhs_node = try parseCRelExpr(c, m, scope);
6529 const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
6530 op_node.* = .{
6531 .base = .{ .tag = op_id },
6532 .op_token = op_token,
6533 .lhs = lhs_node,
6534 .rhs = try macroBoolToInt(c, rhs_node),
6535 };
6536 node = &op_node.base;
65374739 }
65384740}
65394741
6540fn parseCRelExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
4742fn parseCRelExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
65414743 var node = try parseCShiftExpr(c, m, scope);
65424744 while (true) {
6543 var op_token: ast.TokenIndex = undefined;
6544 var op_id: ast.Node.Tag = undefined;
65454745 switch (m.peek().?) {
65464746 .AngleBracketRight => {
6547 op_token = try appendToken(c, .AngleBracketRight, ">");
6548 op_id = .GreaterThan;
4747 _ = m.next();
4748 const lhs = try macroBoolToInt(c, node);
4749 const rhs = try macroBoolToInt(c, try parseCShiftExpr(c, m, scope));
4750 node = try Tag.greater_than.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
65494751 },
65504752 .AngleBracketRightEqual => {
6551 op_token = try appendToken(c, .AngleBracketRightEqual, ">=");
6552 op_id = .GreaterOrEqual;
4753 _ = m.next();
4754 const lhs = try macroBoolToInt(c, node);
4755 const rhs = try macroBoolToInt(c, try parseCShiftExpr(c, m, scope));
4756 node = try Tag.greater_than_equal.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
65534757 },
65544758 .AngleBracketLeft => {
6555 op_token = try appendToken(c, .AngleBracketLeft, "<");
6556 op_id = .LessThan;
4759 _ = m.next();
4760 const lhs = try macroBoolToInt(c, node);
4761 const rhs = try macroBoolToInt(c, try parseCShiftExpr(c, m, scope));
4762 node = try Tag.less_than.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
65574763 },
65584764 .AngleBracketLeftEqual => {
6559 op_token = try appendToken(c, .AngleBracketLeftEqual, "<=");
6560 op_id = .LessOrEqual;
4765 _ = m.next();
4766 const lhs = try macroBoolToInt(c, node);
4767 const rhs = try macroBoolToInt(c, try parseCShiftExpr(c, m, scope));
4768 node = try Tag.less_than_equal.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
65614769 },
65624770 else => return node,
65634771 }
6564 _ = m.next();
6565 const lhs_node = try macroBoolToInt(c, node);
6566 const rhs_node = try parseCShiftExpr(c, m, scope);
6567 const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
6568 op_node.* = .{
6569 .base = .{ .tag = op_id },
6570 .op_token = op_token,
6571 .lhs = lhs_node,
6572 .rhs = try macroBoolToInt(c, rhs_node),
6573 };
6574 node = &op_node.base;
65754772 }
65764773}
65774774
6578fn parseCShiftExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
4775fn parseCShiftExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
65794776 var node = try parseCAddSubExpr(c, m, scope);
65804777 while (true) {
6581 var op_token: ast.TokenIndex = undefined;
6582 var op_id: ast.Node.Tag = undefined;
65834778 switch (m.peek().?) {
65844779 .AngleBracketAngleBracketLeft => {
6585 op_token = try appendToken(c, .AngleBracketAngleBracketLeft, "<<");
6586 op_id = .BitShiftLeft;
4780 _ = m.next();
4781 const lhs = try macroBoolToInt(c, node);
4782 const rhs = try macroBoolToInt(c, try parseCAddSubExpr(c, m, scope));
4783 node = try Tag.shl.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
65874784 },
65884785 .AngleBracketAngleBracketRight => {
6589 op_token = try appendToken(c, .AngleBracketAngleBracketRight, ">>");
6590 op_id = .BitShiftRight;
4786 _ = m.next();
4787 const lhs = try macroBoolToInt(c, node);
4788 const rhs = try macroBoolToInt(c, try parseCAddSubExpr(c, m, scope));
4789 node = try Tag.shr.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
65914790 },
65924791 else => return node,
65934792 }
6594 _ = m.next();
6595 const lhs_node = try macroBoolToInt(c, node);
6596 const rhs_node = try parseCAddSubExpr(c, m, scope);
6597 const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
6598 op_node.* = .{
6599 .base = .{ .tag = op_id },
6600 .op_token = op_token,
6601 .lhs = lhs_node,
6602 .rhs = try macroBoolToInt(c, rhs_node),
6603 };
6604 node = &op_node.base;
66054793 }
66064794}
66074795
6608fn parseCAddSubExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
4796fn parseCAddSubExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
66094797 var node = try parseCMulExpr(c, m, scope);
66104798 while (true) {
6611 var op_token: ast.TokenIndex = undefined;
6612 var op_id: ast.Node.Tag = undefined;
66134799 switch (m.peek().?) {
66144800 .Plus => {
6615 op_token = try appendToken(c, .Plus, "+");
6616 op_id = .Add;
4801 _ = m.next();
4802 const lhs = try macroBoolToInt(c, node);
4803 const rhs = try macroBoolToInt(c, try parseCMulExpr(c, m, scope));
4804 node = try Tag.add.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
66174805 },
66184806 .Minus => {
6619 op_token = try appendToken(c, .Minus, "-");
6620 op_id = .Sub;
4807 _ = m.next();
4808 const lhs = try macroBoolToInt(c, node);
4809 const rhs = try macroBoolToInt(c, try parseCMulExpr(c, m, scope));
4810 node = try Tag.sub.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
66214811 },
66224812 else => return node,
66234813 }
6624 _ = m.next();
6625 const lhs_node = try macroBoolToInt(c, node);
6626 const rhs_node = try parseCMulExpr(c, m, scope);
6627 const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
6628 op_node.* = .{
6629 .base = .{ .tag = op_id },
6630 .op_token = op_token,
6631 .lhs = lhs_node,
6632 .rhs = try macroBoolToInt(c, rhs_node),
6633 };
6634 node = &op_node.base;
66354814 }
66364815}
66374816
6638fn parseCMulExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
4817fn parseCMulExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
66394818 var node = try parseCUnaryExpr(c, m, scope);
66404819 while (true) {
6641 var op_token: ast.TokenIndex = undefined;
6642 var op_id: ast.Node.Tag = undefined;
66434820 switch (m.next().?) {
66444821 .Asterisk => {
6645 if (m.peek().? == .RParen) {
4822 const next = m.peek().?;
4823 if (next == .RParen or next == .Nl or next == .Eof) {
66464824 // type *)
66474825
6648 // hack to get zig fmt to render a comma in builtin calls
6649 _ = try appendToken(c, .Comma, ",");
6650
66514826 // last token of `node`
66524827 const prev_id = m.list[m.i - 1].id;
66534828
66544829 if (prev_id == .Keyword_void) {
6655 const ptr = try transCreateNodePtrType(c, false, false, .Asterisk);
6656 ptr.rhs = node;
6657 const optional_node = try transCreateNodeSimplePrefixOp(c, .OptionalType, .QuestionMark, "?");
6658 optional_node.rhs = &ptr.base;
6659 return &optional_node.base;
4830 const ptr = try Tag.single_pointer.create(c.arena, .{
4831 .is_const = false,
4832 .is_volatile = false,
4833 .elem_type = node,
4834 });
4835 return Tag.optional_type.create(c.arena, ptr);
66604836 } else {
6661 const ptr = try transCreateNodePtrType(c, false, false, Token.Id.Identifier);
6662 ptr.rhs = node;
6663 return &ptr.base;
4837 return Tag.c_pointer.create(c.arena, .{
4838 .is_const = false,
4839 .is_volatile = false,
4840 .elem_type = node,
4841 });
66644842 }
66654843 } else {
66664844 // expr * expr
6667 op_token = try appendToken(c, .Asterisk, "*");
6668 op_id = .BitShiftLeft;
4845 const lhs = try macroBoolToInt(c, node);
4846 const rhs = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope));
4847 node = try Tag.mul.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
66694848 }
66704849 },
66714850 .Slash => {
6672 op_id = .Div;
6673 op_token = try appendToken(c, .Slash, "/");
4851 const lhs = try macroBoolToInt(c, node);
4852 const rhs = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope));
4853 node = try Tag.div.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
66744854 },
66754855 .Percent => {
6676 op_id = .Mod;
6677 op_token = try appendToken(c, .Percent, "%");
4856 const lhs = try macroBoolToInt(c, node);
4857 const rhs = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope));
4858 node = try Tag.mod.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
66784859 },
66794860 else => {
66804861 m.i -= 1;
66814862 return node;
66824863 },
66834864 }
6684 const lhs_node = try macroBoolToInt(c, node);
6685 const rhs_node = try parseCUnaryExpr(c, m, scope);
6686 const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
6687 op_node.* = .{
6688 .base = .{ .tag = op_id },
6689 .op_token = op_token,
6690 .lhs = lhs_node,
6691 .rhs = try macroBoolToInt(c, rhs_node),
6692 };
6693 node = &op_node.base;
66944865 }
66954866}
66964867
6697fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
4868fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
66984869 var node = try parseCPrimaryExpr(c, m, scope);
66994870 while (true) {
67004871 switch (m.next().?) {
......@@ -6704,38 +4875,33 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.N
67044875 return error.ParseError;
67054876 }
67064877
6707 node = try transCreateNodeFieldAccess(c, node, m.slice());
6708 continue;
4878 node = try Tag.field_access.create(c.arena, .{ .lhs = node, .field_name = m.slice() });
67094879 },
67104880 .Arrow => {
67114881 if (m.next().? != .Identifier) {
67124882 try m.fail(c, "unable to translate C expr: expected identifier", .{});
67134883 return error.ParseError;
67144884 }
6715 const deref = try transCreateNodePtrDeref(c, node);
6716 node = try transCreateNodeFieldAccess(c, deref, m.slice());
6717 continue;
4885
4886 const deref = try Tag.deref.create(c.arena, node);
4887 node = try Tag.field_access.create(c.arena, .{ .lhs = deref, .field_name = m.slice() });
67184888 },
67194889 .LBracket => {
6720 const arr_node = try transCreateNodeArrayAccess(c, node);
6721 arr_node.index_expr = try parseCExpr(c, m, scope);
6722 arr_node.rtoken = try appendToken(c, .RBracket, "]");
6723 node = &arr_node.base;
4890 const index = try macroBoolToInt(c, try parseCExpr(c, m, scope));
4891 node = try Tag.array_access.create(c.arena, .{ .lhs = node, .rhs = index });
67244892 if (m.next().? != .RBracket) {
67254893 try m.fail(c, "unable to translate C expr: expected ']'", .{});
67264894 return error.ParseError;
67274895 }
6728 continue;
67294896 },
67304897 .LParen => {
6731 _ = try appendToken(c, .LParen, "(");
6732 var call_params = std.ArrayList(*ast.Node).init(c.gpa);
6733 defer call_params.deinit();
4898 var args = std.ArrayList(Node).init(c.gpa);
4899 defer args.deinit();
67344900 while (true) {
67354901 const arg = try parseCCondExpr(c, m, scope);
6736 try call_params.append(arg);
4902 try args.append(arg);
67374903 switch (m.next().?) {
6738 .Comma => _ = try appendToken(c, .Comma, ","),
4904 .Comma => {},
67394905 .RParen => break,
67404906 else => {
67414907 try m.fail(c, "unable to translate C expr: expected ',' or ')'", .{});
......@@ -6743,32 +4909,17 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.N
67434909 },
67444910 }
67454911 }
6746 const call_node = try ast.Node.Call.alloc(c.arena, call_params.items.len);
6747 call_node.* = .{
6748 .lhs = node,
6749 .params_len = call_params.items.len,
6750 .async_token = null,
6751 .rtoken = try appendToken(c, .RParen, ")"),
6752 };
6753 mem.copy(*ast.Node, call_node.params(), call_params.items);
6754 node = &call_node.base;
6755 continue;
4912 node = try Tag.call.create(c.arena, .{ .lhs = node, .args = try c.arena.dupe(Node, args.items) });
67564913 },
67574914 .LBrace => {
6758 // must come immediately after `node`
6759 _ = try appendToken(c, .Comma, ",");
6760
6761 const dot = try appendToken(c, .Period, ".");
6762 _ = try appendToken(c, .LBrace, "{");
6763
6764 var init_vals = std.ArrayList(*ast.Node).init(c.gpa);
4915 var init_vals = std.ArrayList(Node).init(c.gpa);
67654916 defer init_vals.deinit();
67664917
67674918 while (true) {
67684919 const val = try parseCCondExpr(c, m, scope);
67694920 try init_vals.append(val);
67704921 switch (m.next().?) {
6771 .Comma => _ = try appendToken(c, .Comma, ","),
4922 .Comma => {},
67724923 .RBrace => break,
67734924 else => {
67744925 try m.fail(c, "unable to translate C expr: expected ',' or '}}'", .{});
......@@ -6776,29 +4927,8 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.N
67764927 },
67774928 }
67784929 }
6779 const tuple_node = try ast.Node.StructInitializerDot.alloc(c.arena, init_vals.items.len);
6780 tuple_node.* = .{
6781 .dot = dot,
6782 .list_len = init_vals.items.len,
6783 .rtoken = try appendToken(c, .RBrace, "}"),
6784 };
6785 mem.copy(*ast.Node, tuple_node.list(), init_vals.items);
6786
6787 //(@import("std").mem.zeroInit(T, .{x}))
6788 const import_fn_call = try c.createBuiltinCall("@import", 1);
6789 const std_node = try transCreateNodeStringLiteral(c, "\"std\"");
6790 import_fn_call.params()[0] = std_node;
6791 import_fn_call.rparen_token = try appendToken(c, .RParen, ")");
6792 const inner_field_access = try transCreateNodeFieldAccess(c, &import_fn_call.base, "mem");
6793 const outer_field_access = try transCreateNodeFieldAccess(c, inner_field_access, "zeroInit");
6794
6795 const zero_init_call = try c.createCall(outer_field_access, 2);
6796 zero_init_call.params()[0] = node;
6797 zero_init_call.params()[1] = &tuple_node.base;
6798 zero_init_call.rtoken = try appendToken(c, .RParen, ")");
6799
6800 node = &zero_init_call.base;
6801 continue;
4930 const tuple_node = try Tag.tuple.create(c.arena, try c.arena.dupe(Node, init_vals.items));
4931 node = try Tag.std_mem_zeroinit.create(c.arena, .{ .lhs = node, .rhs = tuple_node });
68024932 },
68034933 .PlusPlus, .MinusMinus => {
68044934 try m.fail(c, "TODO postfix inc/dec expr", .{});
......@@ -6812,35 +4942,31 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.N
68124942 }
68134943}
68144944
6815fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
4945fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
68164946 switch (m.next().?) {
68174947 .Bang => {
6818 const node = try transCreateNodeSimplePrefixOp(c, .BoolNot, .Bang, "!");
6819 node.rhs = try macroIntToBool(c, try parseCUnaryExpr(c, m, scope));
6820 return &node.base;
4948 const operand = try macroIntToBool(c, try parseCUnaryExpr(c, m, scope));
4949 return Tag.not.create(c.arena, operand);
68214950 },
68224951 .Minus => {
6823 const node = try transCreateNodeSimplePrefixOp(c, .Negation, .Minus, "-");
6824 node.rhs = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope));
6825 return &node.base;
4952 const operand = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope));
4953 return Tag.negate.create(c.arena, operand);
68264954 },
68274955 .Plus => return try parseCUnaryExpr(c, m, scope),
68284956 .Tilde => {
6829 const node = try transCreateNodeSimplePrefixOp(c, .BitNot, .Tilde, "~");
6830 node.rhs = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope));
6831 return &node.base;
4957 const operand = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope));
4958 return Tag.bit_not.create(c.arena, operand);
68324959 },
68334960 .Asterisk => {
6834 const node = try macroGroup(c, try parseCUnaryExpr(c, m, scope));
6835 return try transCreateNodePtrDeref(c, node);
4961 const operand = try parseCUnaryExpr(c, m, scope);
4962 return Tag.deref.create(c.arena, operand);
68364963 },
68374964 .Ampersand => {
6838 const node = try transCreateNodeSimplePrefixOp(c, .AddressOf, .Ampersand, "&");
6839 node.rhs = try macroGroup(c, try parseCUnaryExpr(c, m, scope));
6840 return &node.base;
4965 const operand = try parseCUnaryExpr(c, m, scope);
4966 return Tag.address_of.create(c.arena, operand);
68414967 },
68424968 .Keyword_sizeof => {
6843 const inner = if (m.peek().? == .LParen) blk: {
4969 const operand = if (m.peek().? == .LParen) blk: {
68444970 _ = m.next();
68454971 // C grammar says this should be 'type-name' but we have to
68464972 // use parseCMulExpr to correctly handle pointer types.
......@@ -6852,18 +4978,7 @@ fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Nod
68524978 break :blk inner;
68534979 } else try parseCUnaryExpr(c, m, scope);
68544980
6855 //(@import("std").meta.sizeof(dest, x))
6856 const import_fn_call = try c.createBuiltinCall("@import", 1);
6857 const std_node = try transCreateNodeStringLiteral(c, "\"std\"");
6858 import_fn_call.params()[0] = std_node;
6859 import_fn_call.rparen_token = try appendToken(c, .RParen, ")");
6860 const inner_field_access = try transCreateNodeFieldAccess(c, &import_fn_call.base, "meta");
6861 const outer_field_access = try transCreateNodeFieldAccess(c, inner_field_access, "sizeof");
6862
6863 const sizeof_call = try c.createCall(outer_field_access, 1);
6864 sizeof_call.params()[0] = inner;
6865 sizeof_call.rtoken = try appendToken(c, .RParen, ")");
6866 return &sizeof_call.base;
4981 return Tag.std_meta_sizeof.create(c.arena, operand);
68674982 },
68684983 .Keyword_alignof => {
68694984 // TODO this won't work if using <stdalign.h>'s
......@@ -6874,16 +4989,13 @@ fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Nod
68744989 }
68754990 // C grammar says this should be 'type-name' but we have to
68764991 // use parseCMulExpr to correctly handle pointer types.
6877 const inner = try parseCMulExpr(c, m, scope);
4992 const operand = try parseCMulExpr(c, m, scope);
68784993 if (m.next().? != .RParen) {
68794994 try m.fail(c, "unable to translate C expr: expected ')'", .{});
68804995 return error.ParseError;
68814996 }
68824997
6883 const builtin_call = try c.createBuiltinCall("@alignOf", 1);
6884 builtin_call.params()[0] = inner;
6885 builtin_call.rparen_token = try appendToken(c, .RParen, ")");
6886 return &builtin_call.base;
4998 return Tag.alignof.create(c.arena, operand);
68874999 },
68885000 .PlusPlus, .MinusMinus => {
68895001 try m.fail(c, "TODO unary inc/dec expr", .{});
......@@ -6896,51 +5008,40 @@ fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Nod
68965008 }
68975009}
68985010
6899fn tokenSlice(c: *Context, token: ast.TokenIndex) []u8 {
6900 const tok = c.token_locs.items[token];
6901 const slice = c.source_buffer.items[tok.start..tok.end];
6902 return if (mem.startsWith(u8, slice, "@\""))
6903 slice[2 .. slice.len - 1]
6904 else
6905 slice;
6906}
6907
6908fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node {
6909 switch (node.tag) {
6910 .ContainerDecl,
6911 .AddressOf,
6912 .Await,
6913 .BitNot,
6914 .BoolNot,
6915 .OptionalType,
6916 .Negation,
6917 .NegationWrap,
6918 .Resume,
6919 .Try,
6920 .ArrayType,
6921 .ArrayTypeSentinel,
6922 .PtrType,
6923 .SliceType,
5011fn getContainer(c: *Context, node: Node) ?Node {
5012 switch (node.tag()) {
5013 .@"union",
5014 .@"struct",
5015 .@"enum",
5016 .address_of,
5017 .bit_not,
5018 .not,
5019 .optional_type,
5020 .negate,
5021 .negate_wrap,
5022 .array_type,
5023 .c_pointer,
5024 .single_pointer,
69245025 => return node,
69255026
6926 .Identifier => {
6927 const ident = node.castTag(.Identifier).?;
6928 if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |value| {
6929 if (value.cast(ast.Node.VarDecl)) |var_decl|
6930 return getContainer(c, var_decl.getInitNode().?);
5027 .identifier => {
5028 const ident = node.castTag(.identifier).?;
5029 if (c.global_scope.sym_table.get(ident.data)) |value| {
5030 if (value.castTag(.var_decl)) |var_decl|
5031 return getContainer(c, var_decl.data.init.?);
5032 if (value.castTag(.var_simple) orelse value.castTag(.pub_var_simple)) |var_decl|
5033 return getContainer(c, var_decl.data.init);
69315034 }
69325035 },
69335036
6934 .Period => {
6935 const infix = node.castTag(.Period).?;
5037 .field_access => {
5038 const field_access = node.castTag(.field_access).?;
69365039
6937 if (getContainerTypeOf(c, infix.lhs)) |ty_node| {
6938 if (ty_node.cast(ast.Node.ContainerDecl)) |container| {
6939 for (container.fieldsAndDecls()) |field_ref| {
6940 const field = field_ref.cast(ast.Node.ContainerField).?;
6941 const ident = infix.rhs.castTag(.Identifier).?;
6942 if (mem.eql(u8, tokenSlice(c, field.name_token), tokenSlice(c, ident.token))) {
6943 return getContainer(c, field.type_expr.?);
5040 if (getContainerTypeOf(c, field_access.data.lhs)) |ty_node| {
5041 if (ty_node.castTag(.@"struct") orelse ty_node.castTag(.@"union")) |container| {
5042 for (container.data.fields) |field| {
5043 if (mem.eql(u8, field.name, field_access.data.field_name)) {
5044 return getContainer(c, field.type);
69445045 }
69455046 }
69465047 }
......@@ -6952,22 +5053,19 @@ fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node {
69525053 return null;
69535054}
69545055
6955fn getContainerTypeOf(c: *Context, ref: *ast.Node) ?*ast.Node {
6956 if (ref.castTag(.Identifier)) |ident| {
6957 if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |value| {
6958 if (value.cast(ast.Node.VarDecl)) |var_decl| {
6959 if (var_decl.getTypeNode()) |ty|
6960 return getContainer(c, ty);
5056fn getContainerTypeOf(c: *Context, ref: Node) ?Node {
5057 if (ref.castTag(.identifier)) |ident| {
5058 if (c.global_scope.sym_table.get(ident.data)) |value| {
5059 if (value.castTag(.var_decl)) |var_decl| {
5060 return getContainer(c, var_decl.data.type);
69615061 }
69625062 }
6963 } else if (ref.castTag(.Period)) |infix| {
6964 if (getContainerTypeOf(c, infix.lhs)) |ty_node| {
6965 if (ty_node.cast(ast.Node.ContainerDecl)) |container| {
6966 for (container.fieldsAndDecls()) |field_ref| {
6967 const field = field_ref.cast(ast.Node.ContainerField).?;
6968 const ident = infix.rhs.castTag(.Identifier).?;
6969 if (mem.eql(u8, tokenSlice(c, field.name_token), tokenSlice(c, ident.token))) {
6970 return getContainer(c, field.type_expr.?);
5063 } else if (ref.castTag(.field_access)) |field_access| {
5064 if (getContainerTypeOf(c, field_access.data.lhs)) |ty_node| {
5065 if (ty_node.castTag(.@"struct") orelse ty_node.castTag(.@"union")) |container| {
5066 for (container.data.fields) |field| {
5067 if (mem.eql(u8, field.name, field_access.data.field_name)) {
5068 return getContainer(c, field.type);
69715069 }
69725070 }
69735071 } else
......@@ -6977,11 +5075,16 @@ fn getContainerTypeOf(c: *Context, ref: *ast.Node) ?*ast.Node {
69775075 return null;
69785076}
69795077
6980fn getFnProto(c: *Context, ref: *ast.Node) ?*ast.Node.FnProto {
6981 const init = if (ref.cast(ast.Node.VarDecl)) |v| v.getInitNode().? else return null;
5078fn getFnProto(c: *Context, ref: Node) ?*ast.Payload.Func {
5079 const init = if (ref.castTag(.var_decl)) |v|
5080 v.data.init orelse return null
5081 else if (ref.castTag(.var_simple) orelse ref.castTag(.pub_var_simple)) |v|
5082 v.data.init
5083 else
5084 return null;
69825085 if (getContainerTypeOf(c, init)) |ty_node| {
6983 if (ty_node.castTag(.OptionalType)) |prefix| {
6984 if (prefix.rhs.cast(ast.Node.FnProto)) |fn_proto| {
5086 if (ty_node.castTag(.optional_type)) |prefix| {
5087 if (prefix.data.castTag(.func)) |fn_proto| {
69855088 return fn_proto;
69865089 }
69875090 }
src/translate_c/ast.zig created+2529
......@@ -0,0 +1,2529 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2021 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6const std = @import("std");
7const Type = @import("../type.zig").Type;
8const Allocator = std.mem.Allocator;
9
10pub const Node = extern union {
11 /// If the tag value is less than Tag.no_payload_count, then no pointer
12 /// dereference is needed.
13 tag_if_small_enough: usize,
14 ptr_otherwise: *Payload,
15
16 pub const Tag = enum {
17 /// Declarations add themselves to the correct scopes and should not be emitted as this tag.
18 declaration,
19 null_literal,
20 undefined_literal,
21 /// opaque {}
22 opaque_literal,
23 true_literal,
24 false_literal,
25 empty_block,
26 return_void,
27 zero_literal,
28 one_literal,
29 void_type,
30 noreturn_type,
31 @"anytype",
32 @"continue",
33 @"break",
34 /// pub usingnamespace @import("std").c.builtins;
35 usingnamespace_builtins,
36 // After this, the tag requires a payload.
37
38 integer_literal,
39 float_literal,
40 string_literal,
41 char_literal,
42 identifier,
43 @"if",
44 /// if (!operand) break;
45 if_not_break,
46 @"while",
47 /// while (true) operand
48 while_true,
49 @"switch",
50 /// else => operand,
51 switch_else,
52 /// items => body,
53 switch_prong,
54 break_val,
55 @"return",
56 field_access,
57 array_access,
58 call,
59 var_decl,
60 func,
61 warning,
62 /// All enums are non-exhaustive
63 @"enum",
64 @"struct",
65 @"union",
66 array_init,
67 tuple,
68 container_init,
69 std_meta_cast,
70 /// _ = operand;
71 discard,
72
73 // a + b
74 add,
75 // a = b
76 add_assign,
77 // c = (a = b)
78 add_wrap,
79 add_wrap_assign,
80 sub,
81 sub_assign,
82 sub_wrap,
83 sub_wrap_assign,
84 mul,
85 mul_assign,
86 mul_wrap,
87 mul_wrap_assign,
88 div,
89 div_assign,
90 shl,
91 shl_assign,
92 shr,
93 shr_assign,
94 mod,
95 mod_assign,
96 @"and",
97 @"or",
98 less_than,
99 less_than_equal,
100 greater_than,
101 greater_than_equal,
102 equal,
103 not_equal,
104 bit_and,
105 bit_and_assign,
106 bit_or,
107 bit_or_assign,
108 bit_xor,
109 bit_xor_assign,
110 array_cat,
111 ellipsis3,
112 assign,
113
114 log2_int_type,
115 /// @import("std").math.Log2Int(operand)
116 std_math_Log2Int,
117 /// @intCast(lhs, rhs)
118 int_cast,
119 /// @rem(lhs, rhs)
120 rem,
121 /// @divTrunc(lhs, rhs)
122 div_trunc,
123 /// @boolToInt(operand)
124 bool_to_int,
125 /// @as(lhs, rhs)
126 as,
127 /// @truncate(lhs, rhs)
128 truncate,
129 /// @bitCast(lhs, rhs)
130 bit_cast,
131 /// @floatCast(lhs, rhs)
132 float_cast,
133 /// @floatToInt(lhs, rhs)
134 float_to_int,
135 /// @intToFloat(lhs, rhs)
136 int_to_float,
137 /// @intToEnum(lhs, rhs)
138 int_to_enum,
139 /// @enumToInt(operand)
140 enum_to_int,
141 /// @intToPtr(lhs, rhs)
142 int_to_ptr,
143 /// @ptrToInt(operand)
144 ptr_to_int,
145 /// @alignCast(lhs, rhs)
146 align_cast,
147 /// @ptrCast(lhs, rhs)
148 ptr_cast,
149
150 negate,
151 negate_wrap,
152 bit_not,
153 not,
154 address_of,
155 /// .?
156 unwrap,
157 /// .*
158 deref,
159
160 block,
161 /// { operand }
162 block_single,
163
164 sizeof,
165 alignof,
166 typeof,
167 type,
168
169 optional_type,
170 c_pointer,
171 single_pointer,
172 array_type,
173
174 /// @import("std").meta.sizeof(operand)
175 std_meta_sizeof,
176 /// @import("std").mem.zeroes(operand)
177 std_mem_zeroes,
178 /// @import("std").mem.zeroInit(lhs, rhs)
179 std_mem_zeroinit,
180 // pub const name = @compileError(msg);
181 fail_decl,
182 // var actual = mangled;
183 arg_redecl,
184 /// pub const alias = actual;
185 alias,
186 /// const name = init;
187 var_simple,
188 /// pub const name = init;
189 pub_var_simple,
190 /// pub const enum_field_name = @enumToInt(enum_name.field_name);
191 pub_enum_redecl,
192 enum_redecl,
193
194 /// pub inline fn name(params) return_type body
195 pub_inline_fn,
196
197 /// [0]type{}
198 empty_array,
199 /// [1]type{val} ** count
200 array_filler,
201
202 pub const last_no_payload_tag = Tag.usingnamespace_builtins;
203 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
204
205 pub fn Type(comptime t: Tag) type {
206 return switch (t) {
207 .declaration,
208 .null_literal,
209 .undefined_literal,
210 .opaque_literal,
211 .true_literal,
212 .false_literal,
213 .empty_block,
214 .usingnamespace_builtins,
215 .return_void,
216 .zero_literal,
217 .one_literal,
218 .void_type,
219 .noreturn_type,
220 .@"anytype",
221 .@"continue",
222 .@"break",
223 => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"),
224
225 .std_mem_zeroes,
226 .@"return",
227 .discard,
228 .std_math_Log2Int,
229 .negate,
230 .negate_wrap,
231 .bit_not,
232 .not,
233 .optional_type,
234 .address_of,
235 .unwrap,
236 .deref,
237 .ptr_to_int,
238 .enum_to_int,
239 .empty_array,
240 .while_true,
241 .if_not_break,
242 .switch_else,
243 .block_single,
244 .std_meta_sizeof,
245 .bool_to_int,
246 .sizeof,
247 .alignof,
248 .typeof,
249 => Payload.UnOp,
250
251 .add,
252 .add_assign,
253 .add_wrap,
254 .add_wrap_assign,
255 .sub,
256 .sub_assign,
257 .sub_wrap,
258 .sub_wrap_assign,
259 .mul,
260 .mul_assign,
261 .mul_wrap,
262 .mul_wrap_assign,
263 .div,
264 .div_assign,
265 .shl,
266 .shl_assign,
267 .shr,
268 .shr_assign,
269 .mod,
270 .mod_assign,
271 .@"and",
272 .@"or",
273 .less_than,
274 .less_than_equal,
275 .greater_than,
276 .greater_than_equal,
277 .equal,
278 .not_equal,
279 .bit_and,
280 .bit_and_assign,
281 .bit_or,
282 .bit_or_assign,
283 .bit_xor,
284 .bit_xor_assign,
285 .div_trunc,
286 .rem,
287 .int_cast,
288 .as,
289 .truncate,
290 .bit_cast,
291 .float_cast,
292 .float_to_int,
293 .int_to_float,
294 .int_to_enum,
295 .int_to_ptr,
296 .array_cat,
297 .ellipsis3,
298 .assign,
299 .align_cast,
300 .array_access,
301 .std_mem_zeroinit,
302 .ptr_cast,
303 => Payload.BinOp,
304
305 .integer_literal,
306 .float_literal,
307 .string_literal,
308 .char_literal,
309 .identifier,
310 .warning,
311 .type,
312 => Payload.Value,
313 .@"if" => Payload.If,
314 .@"while" => Payload.While,
315 .@"switch", .array_init,.switch_prong => Payload.Switch,
316 .break_val => Payload.BreakVal,
317 .call => Payload.Call,
318 .var_decl => Payload.VarDecl,
319 .func => Payload.Func,
320 .@"enum" => Payload.Enum,
321 .@"struct", .@"union" => Payload.Record,
322 .tuple => Payload.TupleInit,
323 .container_init => Payload.ContainerInit,
324 .std_meta_cast => Payload.Infix,
325 .block => Payload.Block,
326 .c_pointer, .single_pointer => Payload.Pointer,
327 .array_type => Payload.Array,
328 .arg_redecl, .alias, .fail_decl => Payload.ArgRedecl,
329 .log2_int_type => Payload.Log2IntType,
330 .var_simple, .pub_var_simple => Payload.SimpleVarDecl,
331 .pub_enum_redecl, .enum_redecl => Payload.EnumRedecl,
332 .array_filler => Payload.ArrayFiller,
333 .pub_inline_fn => Payload.PubInlineFn,
334 .field_access => Payload.FieldAccess,
335 };
336 }
337
338 pub fn init(comptime t: Tag) Node {
339 comptime std.debug.assert(@enumToInt(t) < Tag.no_payload_count);
340 return .{ .tag_if_small_enough = @enumToInt(t) };
341 }
342
343 pub fn create(comptime t: Tag, ally: *Allocator, data: Data(t)) error{OutOfMemory}!Node {
344 const ptr = try ally.create(t.Type());
345 ptr.* = .{
346 .base = .{ .tag = t },
347 .data = data,
348 };
349 return Node{ .ptr_otherwise = &ptr.base };
350 }
351
352 pub fn Data(comptime t: Tag) type {
353 return std.meta.fieldInfo(t.Type(), .data).field_type;
354 }
355 };
356
357 pub fn tag(self: Node) Tag {
358 if (self.tag_if_small_enough < Tag.no_payload_count) {
359 return @intToEnum(Tag, @intCast(std.meta.Tag(Tag), self.tag_if_small_enough));
360 } else {
361 return self.ptr_otherwise.tag;
362 }
363 }
364
365 pub fn castTag(self: Node, comptime t: Tag) ?*t.Type() {
366 if (self.tag_if_small_enough < Tag.no_payload_count)
367 return null;
368
369 if (self.ptr_otherwise.tag == t)
370 return @fieldParentPtr(t.Type(), "base", self.ptr_otherwise);
371
372 return null;
373 }
374
375 pub fn initPayload(payload: *Payload) Node {
376 std.debug.assert(@enumToInt(payload.tag) >= Tag.no_payload_count);
377 return .{ .ptr_otherwise = payload };
378 }
379
380 pub fn isNoreturn(node: Node, break_counts: bool) bool {
381 switch (node.tag()) {
382 .block => {
383 const block_node = node.castTag(.block).?;
384 if (block_node.data.stmts.len == 0) return false;
385
386 const last = block_node.data.stmts[block_node.data.stmts.len - 1];
387 return last.isNoreturn(break_counts);
388 },
389 .@"switch" => {
390 const switch_node = node.castTag(.@"switch").?;
391
392 for (switch_node.data.cases) |case| {
393 const body = if (case.castTag(.switch_else)) |some|
394 some.data
395 else if (case.castTag(.switch_prong)) |some|
396 some.data.cond
397 else unreachable;
398
399 if (!body.isNoreturn(break_counts)) return false;
400 }
401 return true;
402 },
403 .@"return", .return_void => return true,
404 .@"break" => if (break_counts) return true,
405 else => {},
406 }
407 return false;
408 }
409
410};
411
412pub const Payload = struct {
413 tag: Node.Tag,
414
415 pub const Infix = struct {
416 base: Payload,
417 data: struct {
418 lhs: Node,
419 rhs: Node,
420 },
421 };
422
423 pub const Value = struct {
424 base: Payload,
425 data: []const u8,
426 };
427
428 pub const UnOp = struct {
429 base: Payload,
430 data: Node,
431 };
432
433 pub const BinOp = struct {
434 base: Payload,
435 data: struct {
436 lhs: Node,
437 rhs: Node,
438 },
439 };
440
441 pub const If = struct {
442 base: Payload,
443 data: struct {
444 cond: Node,
445 then: Node,
446 @"else": ?Node,
447 },
448 };
449
450 pub const While = struct {
451 base: Payload,
452 data: struct {
453 cond: Node,
454 body: Node,
455 cont_expr: ?Node,
456 },
457 };
458
459 pub const Switch = struct {
460 base: Payload,
461 data: struct {
462 cond: Node,
463 cases: []Node,
464 },
465 };
466
467 pub const BreakVal = struct {
468 base: Payload,
469 data: struct {
470 label: ?[]const u8,
471 val: Node,
472 },
473 };
474
475 pub const Call = struct {
476 base: Payload,
477 data: struct {
478 lhs: Node,
479 args: []Node,
480 },
481 };
482
483 pub const VarDecl = struct {
484 base: Payload,
485 data: struct {
486 is_pub: bool,
487 is_const: bool,
488 is_extern: bool,
489 is_export: bool,
490 is_threadlocal: bool,
491 alignment: ?c_uint,
492 linksection_string: ?[]const u8,
493 name: []const u8,
494 type: Node,
495 init: ?Node,
496 },
497 };
498
499 pub const Func = struct {
500 base: Payload,
501 data: struct {
502 is_pub: bool,
503 is_extern: bool,
504 is_export: bool,
505 is_var_args: bool,
506 name: ?[]const u8,
507 linksection_string: ?[]const u8,
508 explicit_callconv: ?std.builtin.CallingConvention,
509 params: []Param,
510 return_type: Node,
511 body: ?Node,
512 alignment: ?c_uint,
513 },
514 };
515
516 pub const Param = struct {
517 is_noalias: bool,
518 name: ?[]const u8,
519 type: Node,
520 };
521
522 pub const Enum = struct {
523 base: Payload,
524 data: struct {
525 int_type: Node,
526 fields: []Field,
527 },
528
529 pub const Field = struct {
530 name: []const u8,
531 value: ?Node,
532 };
533 };
534
535 pub const Record = struct {
536 base: Payload,
537 data: struct {
538 is_packed: bool,
539 fields: []Field,
540 },
541
542 pub const Field = struct {
543 name: []const u8,
544 type: Node,
545 alignment: ?c_uint,
546 };
547 };
548
549 pub const TupleInit = struct {
550 base: Payload,
551 data: []Node,
552 };
553
554 pub const ContainerInit = struct {
555 base: Payload,
556 data: struct {
557 lhs: Node,
558 inits: []Initializer,
559 },
560
561 pub const Initializer = struct {
562 name: []const u8,
563 value: Node,
564 };
565 };
566
567 pub const Block = struct {
568 base: Payload,
569 data: struct {
570 label: ?[]const u8,
571 stmts: []Node,
572 },
573 };
574
575 pub const Array = struct {
576 base: Payload,
577 data: struct {
578 elem_type: Node,
579 len: usize,
580 },
581 };
582
583 pub const Pointer = struct {
584 base: Payload,
585 data: struct {
586 elem_type: Node,
587 is_const: bool,
588 is_volatile: bool,
589 },
590 };
591
592 pub const ArgRedecl = struct {
593 base: Payload,
594 data: struct {
595 actual: []const u8,
596 mangled: []const u8,
597 },
598 };
599
600 pub const Log2IntType = struct {
601 base: Payload,
602 data: std.math.Log2Int(u64),
603 };
604
605 pub const SimpleVarDecl = struct {
606 base: Payload,
607 data: struct {
608 name: []const u8,
609 init: Node,
610 },
611 };
612
613 pub const EnumRedecl = struct {
614 base: Payload,
615 data: struct {
616 enum_val_name: []const u8,
617 field_name: []const u8,
618 enum_name: []const u8,
619 },
620 };
621
622 pub const ArrayFiller = struct {
623 base: Payload,
624 data: struct {
625 type: Node,
626 filler: Node,
627 count: usize,
628 },
629 };
630
631 pub const PubInlineFn = struct {
632 base: Payload,
633 data: struct {
634 name: []const u8,
635 params: []Param,
636 return_type: Node,
637 body: Node,
638 },
639 };
640
641 pub const FieldAccess = struct {
642 base: Payload,
643 data: struct {
644 lhs: Node,
645 field_name: []const u8,
646 },
647 };
648};
649
650/// Converts the nodes into a Zig ast.
651/// Caller must free the source slice.
652pub fn render(gpa: *Allocator, nodes: []const Node) !std.zig.ast.Tree {
653 var ctx = Context{
654 .gpa = gpa,
655 .buf = std.ArrayList(u8).init(gpa),
656 };
657 defer ctx.buf.deinit();
658 defer ctx.nodes.deinit(gpa);
659 defer ctx.extra_data.deinit(gpa);
660 defer ctx.tokens.deinit(gpa);
661
662 // Estimate that each top level node has 10 child nodes.
663 const estimated_node_count = nodes.len * 10;
664 try ctx.nodes.ensureCapacity(gpa, estimated_node_count);
665 // Estimate that each each node has 2 tokens.
666 const estimated_tokens_count = estimated_node_count * 2;
667 try ctx.tokens.ensureCapacity(gpa, estimated_tokens_count);
668 // Estimate that each each token is 3 bytes long.
669 const estimated_buf_len = estimated_tokens_count * 3;
670 try ctx.buf.ensureCapacity(estimated_buf_len);
671
672 ctx.nodes.appendAssumeCapacity(.{
673 .tag = .root,
674 .main_token = 0,
675 .data = .{
676 .lhs = undefined,
677 .rhs = undefined,
678 },
679 });
680
681 const root_members = blk: {
682 var result = std.ArrayList(NodeIndex).init(gpa);
683 defer result.deinit();
684
685 for (nodes) |node| {
686 const res = try renderNode(&ctx, node);
687 if (node.tag() == .warning) continue;
688 try result.append(res);
689 }
690 break :blk try ctx.listToSpan(result.items);
691 };
692
693 ctx.nodes.items(.data)[0] = .{
694 .lhs = root_members.start,
695 .rhs = root_members.end,
696 };
697
698 try ctx.tokens.append(gpa, .{
699 .tag = .eof,
700 .start = @intCast(u32, ctx.buf.items.len),
701 });
702
703 return std.zig.ast.Tree{
704 .source = ctx.buf.toOwnedSlice(),
705 .tokens = ctx.tokens.toOwnedSlice(),
706 .nodes = ctx.nodes.toOwnedSlice(),
707 .extra_data = ctx.extra_data.toOwnedSlice(gpa),
708 .errors = &.{},
709 };
710}
711
712const NodeIndex = std.zig.ast.Node.Index;
713const NodeSubRange = std.zig.ast.Node.SubRange;
714const TokenIndex = std.zig.ast.TokenIndex;
715const TokenTag = std.zig.Token.Tag;
716
717const Context = struct {
718 gpa: *Allocator,
719 buf: std.ArrayList(u8) = .{},
720 nodes: std.zig.ast.NodeList = .{},
721 extra_data: std.ArrayListUnmanaged(std.zig.ast.Node.Index) = .{},
722 tokens: std.zig.ast.TokenList = .{},
723
724 fn addTokenFmt(c: *Context, tag: TokenTag, comptime format: []const u8, args: anytype) Allocator.Error!TokenIndex {
725 const start_index = c.buf.items.len;
726 try c.buf.writer().print(format ++ " ", args);
727
728 try c.tokens.append(c.gpa, .{
729 .tag = tag,
730 .start = @intCast(u32, start_index),
731 });
732
733 return @intCast(u32, c.tokens.len - 1);
734 }
735
736 fn addToken(c: *Context, tag: TokenTag, bytes: []const u8) Allocator.Error!TokenIndex {
737 return addTokenFmt(c, tag, "{s}", .{bytes});
738 }
739
740 fn addIdentifier(c: *Context, bytes: []const u8) Allocator.Error!TokenIndex {
741 return addTokenFmt(c, .identifier, "{s}", .{std.zig.fmtId(bytes)});
742 }
743
744 fn listToSpan(c: *Context, list: []const NodeIndex) Allocator.Error!NodeSubRange {
745 try c.extra_data.appendSlice(c.gpa, list);
746 return NodeSubRange{
747 .start = @intCast(NodeIndex, c.extra_data.items.len - list.len),
748 .end = @intCast(NodeIndex, c.extra_data.items.len),
749 };
750 }
751
752 fn addNode(c: *Context, elem: std.zig.ast.NodeList.Elem) Allocator.Error!NodeIndex {
753 const result = @intCast(NodeIndex, c.nodes.len);
754 try c.nodes.append(c.gpa, elem);
755 return result;
756 }
757
758 fn addExtra(c: *Context, extra: anytype) Allocator.Error!NodeIndex {
759 const fields = std.meta.fields(@TypeOf(extra));
760 try c.extra_data.ensureCapacity(c.gpa, c.extra_data.items.len + fields.len);
761 const result = @intCast(u32, c.extra_data.items.len);
762 inline for (fields) |field| {
763 comptime std.debug.assert(field.field_type == NodeIndex);
764 c.extra_data.appendAssumeCapacity(@field(extra, field.name));
765 }
766 return result;
767 }
768};
769
770fn renderNodes(c: *Context, nodes: []const Node) Allocator.Error!NodeSubRange {
771 var result = std.ArrayList(NodeIndex).init(c.gpa);
772 defer result.deinit();
773
774 for (nodes) |node| {
775 const res = try renderNode(c, node);
776 if (node.tag() == .warning) continue;
777 try result.append(res);
778 }
779
780 return try c.listToSpan(result.items);
781}
782
783fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
784 switch (node.tag()) {
785 .declaration => unreachable,
786 .warning => {
787 const payload = node.castTag(.warning).?.data;
788 try c.buf.appendSlice(payload);
789 try c.buf.append('\n');
790 return @as(NodeIndex, 0); // error: integer value 0 cannot be coerced to type 'std.mem.Allocator.Error!u32'
791 },
792 .usingnamespace_builtins => {
793 // pub usingnamespace @import("std").c.builtins;
794 _ = try c.addToken(.keyword_pub, "pub");
795 const usingnamespace_token = try c.addToken(.keyword_usingnamespace, "usingnamespace");
796 const import_node = try renderStdImport(c, "c", "builtins");
797 _ = try c.addToken(.semicolon, ";");
798
799 return c.addNode(.{
800 .tag = .@"usingnamespace",
801 .main_token = usingnamespace_token,
802 .data = .{
803 .lhs = import_node,
804 .rhs = undefined,
805 },
806 });
807 },
808 .std_math_Log2Int => {
809 const payload = node.castTag(.std_math_Log2Int).?.data;
810 const import_node = try renderStdImport(c, "math", "Log2Int");
811 return renderCall(c, import_node, &.{payload});
812 },
813 .std_meta_cast => {
814 const payload = node.castTag(.std_meta_cast).?.data;
815 const import_node = try renderStdImport(c, "meta", "cast");
816 return renderCall(c, import_node, &.{ payload.lhs, payload.rhs });
817 },
818 .std_meta_sizeof => {
819 const payload = node.castTag(.std_meta_sizeof).?.data;
820 const import_node = try renderStdImport(c, "meta", "sizeof");
821 return renderCall(c, import_node, &.{payload});
822 },
823 .std_mem_zeroes => {
824 const payload = node.castTag(.std_mem_zeroes).?.data;
825 const import_node = try renderStdImport(c, "mem", "zeroes");
826 return renderCall(c, import_node, &.{payload});
827 },
828 .std_mem_zeroinit => {
829 const payload = node.castTag(.std_mem_zeroinit).?.data;
830 const import_node = try renderStdImport(c, "mem", "zeroInit");
831 return renderCall(c, import_node, &.{ payload.lhs, payload.rhs });
832 },
833 .call => {
834 const payload = node.castTag(.call).?.data;
835 const lhs = try renderNode(c, payload.lhs);
836 return renderCall(c, lhs, payload.args);
837 },
838 .null_literal => return c.addNode(.{
839 .tag = .null_literal,
840 .main_token = try c.addToken(.keyword_null, "null"),
841 .data = undefined,
842 }),
843 .undefined_literal => return c.addNode(.{
844 .tag = .undefined_literal,
845 .main_token = try c.addToken(.keyword_undefined, "undefined"),
846 .data = undefined,
847 }),
848 .true_literal => return c.addNode(.{
849 .tag = .true_literal,
850 .main_token = try c.addToken(.keyword_true, "true"),
851 .data = undefined,
852 }),
853 .false_literal => return c.addNode(.{
854 .tag = .false_literal,
855 .main_token = try c.addToken(.keyword_false, "false"),
856 .data = undefined,
857 }),
858 .zero_literal => return c.addNode(.{
859 .tag = .integer_literal,
860 .main_token = try c.addToken(.integer_literal, "0"),
861 .data = undefined,
862 }),
863 .one_literal => return c.addNode(.{
864 .tag = .integer_literal,
865 .main_token = try c.addToken(.integer_literal, "1"),
866 .data = undefined,
867 }),
868 .void_type => return c.addNode(.{
869 .tag = .identifier,
870 .main_token = try c.addToken(.identifier, "void"),
871 .data = undefined,
872 }),
873 .noreturn_type => return c.addNode(.{
874 .tag = .identifier,
875 .main_token = try c.addToken(.identifier, "noreturn"),
876 .data = undefined,
877 }),
878 .@"continue" => return c.addNode(.{
879 .tag = .@"continue",
880 .main_token = try c.addToken(.keyword_continue, "continue"),
881 .data = .{
882 .lhs = 0,
883 .rhs = undefined,
884 },
885 }),
886 .return_void => return c.addNode(.{
887 .tag = .@"return",
888 .main_token = try c.addToken(.keyword_return, "return"),
889 .data = .{
890 .lhs = 0,
891 .rhs = undefined,
892 },
893 }),
894 .@"break" => return c.addNode(.{
895 .tag = .@"break",
896 .main_token = try c.addToken(.keyword_break, "break"),
897 .data = .{
898 .lhs = 0,
899 .rhs = 0,
900 },
901 }),
902 .break_val => {
903 const payload = node.castTag(.break_val).?.data;
904 const tok = try c.addToken(.keyword_break, "break");
905 const break_label = if (payload.label) |some| blk: {
906 _ = try c.addToken(.colon, ":");
907 break :blk try c.addIdentifier(some);
908 } else 0;
909 return c.addNode(.{
910 .tag = .@"break",
911 .main_token = tok,
912 .data = .{
913 .lhs = break_label,
914 .rhs = try renderNode(c, payload.val),
915 },
916 });
917 },
918 .@"return" => {
919 const payload = node.castTag(.@"return").?.data;
920 return c.addNode(.{
921 .tag = .@"return",
922 .main_token = try c.addToken(.keyword_return, "return"),
923 .data = .{
924 .lhs = try renderNode(c, payload),
925 .rhs = undefined,
926 },
927 });
928 },
929 .type => {
930 const payload = node.castTag(.type).?.data;
931 return c.addNode(.{
932 .tag = .identifier,
933 .main_token = try c.addToken(.identifier, payload),
934 .data = undefined,
935 });
936 },
937 .log2_int_type => {
938 const payload = node.castTag(.log2_int_type).?.data;
939 return c.addNode(.{
940 .tag = .identifier,
941 .main_token = try c.addTokenFmt(.identifier, "u{d}", .{payload}),
942 .data = undefined,
943 });
944 },
945 .identifier => {
946 const payload = node.castTag(.identifier).?.data;
947 return c.addNode(.{
948 .tag = .identifier,
949 .main_token = try c.addIdentifier(payload),
950 .data = undefined,
951 });
952 },
953 .float_literal => {
954 const payload = node.castTag(.float_literal).?.data;
955 return c.addNode(.{
956 .tag = .float_literal,
957 .main_token = try c.addToken(.float_literal, payload),
958 .data = undefined,
959 });
960 },
961 .integer_literal => {
962 const payload = node.castTag(.integer_literal).?.data;
963 return c.addNode(.{
964 .tag = .integer_literal,
965 .main_token = try c.addToken(.integer_literal, payload),
966 .data = undefined,
967 });
968 },
969 .string_literal => {
970 const payload = node.castTag(.string_literal).?.data;
971 return c.addNode(.{
972 .tag = .identifier,
973 .main_token = try c.addToken(.string_literal, payload),
974 .data = undefined,
975 });
976 },
977 .char_literal => {
978 const payload = node.castTag(.char_literal).?.data;
979 return c.addNode(.{
980 .tag = .identifier,
981 .main_token = try c.addToken(.char_literal, payload),
982 .data = undefined,
983 });
984 },
985 .fail_decl => {
986 const payload = node.castTag(.fail_decl).?.data;
987 // pub const name = @compileError(msg);
988 _ = try c.addToken(.keyword_pub, "pub");
989 const const_tok = try c.addToken(.keyword_const, "const");
990 _ = try c.addIdentifier(payload.actual);
991 _ = try c.addToken(.equal, "=");
992
993 const compile_error_tok = try c.addToken(.builtin, "@compileError");
994 _ = try c.addToken(.l_paren, "(");
995 const err_msg_tok = try c.addTokenFmt(.string_literal, "\"{s}\"", .{std.zig.fmtEscapes(payload.mangled)});
996 const err_msg = try c.addNode(.{
997 .tag = .string_literal,
998 .main_token = err_msg_tok,
999 .data = undefined,
1000 });
1001 _ = try c.addToken(.r_paren, ")");
1002 const compile_error = try c.addNode(.{
1003 .tag = .builtin_call_two,
1004 .main_token = compile_error_tok,
1005 .data = .{
1006 .lhs = err_msg,
1007 .rhs = 0,
1008 },
1009 });
1010 _ = try c.addToken(.semicolon, ";");
1011
1012 return c.addNode(.{
1013 .tag = .simple_var_decl,
1014 .main_token = const_tok,
1015 .data = .{
1016 .lhs = 0,
1017 .rhs = compile_error,
1018 },
1019 });
1020 },
1021 .pub_var_simple, .var_simple => {
1022 const payload = @fieldParentPtr(Payload.SimpleVarDecl, "base", node.ptr_otherwise).data;
1023 if (node.tag() == .pub_var_simple) _ = try c.addToken(.keyword_pub, "pub");
1024 const const_tok = try c.addToken(.keyword_const, "const");
1025 _ = try c.addIdentifier(payload.name);
1026 _ = try c.addToken(.equal, "=");
1027
1028 const init = try renderNode(c, payload.init);
1029 _ = try c.addToken(.semicolon, ";");
1030
1031 return c.addNode(.{
1032 .tag = .simple_var_decl,
1033 .main_token = const_tok,
1034 .data = .{
1035 .lhs = 0,
1036 .rhs = init,
1037 },
1038 });
1039 },
1040 .var_decl => return renderVar(c, node),
1041 .arg_redecl, .alias => {
1042 const payload = @fieldParentPtr(Payload.ArgRedecl, "base", node.ptr_otherwise).data;
1043 if (node.tag() == .alias) _ = try c.addToken(.keyword_pub, "pub");
1044 const mut_tok = if (node.tag() == .alias)
1045 try c.addToken(.keyword_const, "const")
1046 else
1047 try c.addToken(.keyword_var, "var");
1048 _ = try c.addIdentifier(payload.actual);
1049 _ = try c.addToken(.equal, "=");
1050
1051 const init = try c.addNode(.{
1052 .tag = .identifier,
1053 .main_token = try c.addIdentifier(payload.mangled),
1054 .data = undefined,
1055 });
1056 _ = try c.addToken(.semicolon, ";");
1057
1058 return c.addNode(.{
1059 .tag = .simple_var_decl,
1060 .main_token = mut_tok,
1061 .data = .{
1062 .lhs = 0,
1063 .rhs = init,
1064 },
1065 });
1066 },
1067 .int_cast => {
1068 const payload = node.castTag(.int_cast).?.data;
1069 return renderBuiltinCall(c, "@intCast", &.{ payload.lhs, payload.rhs });
1070 },
1071 .rem => {
1072 const payload = node.castTag(.rem).?.data;
1073 return renderBuiltinCall(c, "@rem", &.{ payload.lhs, payload.rhs });
1074 },
1075 .div_trunc => {
1076 const payload = node.castTag(.div_trunc).?.data;
1077 return renderBuiltinCall(c, "@divTrunc", &.{ payload.lhs, payload.rhs });
1078 },
1079 .bool_to_int => {
1080 const payload = node.castTag(.bool_to_int).?.data;
1081 return renderBuiltinCall(c, "@boolToInt", &.{payload});
1082 },
1083 .as => {
1084 const payload = node.castTag(.as).?.data;
1085 return renderBuiltinCall(c, "@as", &.{ payload.lhs, payload.rhs });
1086 },
1087 .truncate => {
1088 const payload = node.castTag(.truncate).?.data;
1089 return renderBuiltinCall(c, "@truncate", &.{ payload.lhs, payload.rhs });
1090 },
1091 .bit_cast => {
1092 const payload = node.castTag(.bit_cast).?.data;
1093 return renderBuiltinCall(c, "@bitCast", &.{ payload.lhs, payload.rhs });
1094 },
1095 .float_cast => {
1096 const payload = node.castTag(.float_cast).?.data;
1097 return renderBuiltinCall(c, "@floatCast", &.{ payload.lhs, payload.rhs });
1098 },
1099 .float_to_int => {
1100 const payload = node.castTag(.float_to_int).?.data;
1101 return renderBuiltinCall(c, "@floatToInt", &.{ payload.lhs, payload.rhs });
1102 },
1103 .int_to_float => {
1104 const payload = node.castTag(.int_to_float).?.data;
1105 return renderBuiltinCall(c, "@intToFloat", &.{ payload.lhs, payload.rhs });
1106 },
1107 .int_to_enum => {
1108 const payload = node.castTag(.int_to_enum).?.data;
1109 return renderBuiltinCall(c, "@intToEnum", &.{ payload.lhs, payload.rhs });
1110 },
1111 .enum_to_int => {
1112 const payload = node.castTag(.enum_to_int).?.data;
1113 return renderBuiltinCall(c, "@enumToInt", &.{payload});
1114 },
1115 .int_to_ptr => {
1116 const payload = node.castTag(.int_to_ptr).?.data;
1117 return renderBuiltinCall(c, "@intToPtr", &.{ payload.lhs, payload.rhs });
1118 },
1119 .ptr_to_int => {
1120 const payload = node.castTag(.ptr_to_int).?.data;
1121 return renderBuiltinCall(c, "@ptrToInt", &.{payload});
1122 },
1123 .align_cast => {
1124 const payload = node.castTag(.align_cast).?.data;
1125 return renderBuiltinCall(c, "@alignCast", &.{ payload.lhs, payload.rhs });
1126 },
1127 .ptr_cast => {
1128 const payload = node.castTag(.ptr_cast).?.data;
1129 return renderBuiltinCall(c, "@ptrCast", &.{ payload.lhs, payload.rhs });
1130 },
1131 .sizeof => {
1132 const payload = node.castTag(.sizeof).?.data;
1133 return renderBuiltinCall(c, "@sizeOf", &.{payload});
1134 },
1135 .alignof => {
1136 const payload = node.castTag(.alignof).?.data;
1137 return renderBuiltinCall(c, "@alignOf", &.{payload});
1138 },
1139 .typeof => {
1140 const payload = node.castTag(.typeof).?.data;
1141 return renderBuiltinCall(c, "@TypeOf", &.{payload});
1142 },
1143 .negate => return renderPrefixOp(c, node, .negation, .minus, "-"),
1144 .negate_wrap => return renderPrefixOp(c, node, .negation_wrap, .minus_percent, "-%"),
1145 .bit_not => return renderPrefixOp(c, node, .bit_not, .tilde, "~"),
1146 .not => return renderPrefixOp(c, node, .bool_not, .bang, "!"),
1147 .optional_type => return renderPrefixOp(c, node, .optional_type, .question_mark, "?"),
1148 .address_of => return renderPrefixOp(c, node, .address_of, .ampersand, "&"),
1149 .deref => {
1150 const payload = node.castTag(.deref).?.data;
1151 const operand = try renderNodeGrouped(c, payload);
1152 const deref_tok = try c.addToken(.period_asterisk, ".*");
1153 return c.addNode(.{
1154 .tag = .deref,
1155 .main_token = deref_tok,
1156 .data = .{
1157 .lhs = operand,
1158 .rhs = undefined,
1159 },
1160 });
1161 },
1162 .unwrap => {
1163 const payload = node.castTag(.unwrap).?.data;
1164 const operand = try renderNodeGrouped(c, payload);
1165 const period = try c.addToken(.period, ".");
1166 const question_mark = try c.addToken(.question_mark, "?");
1167 return c.addNode(.{
1168 .tag = .unwrap_optional,
1169 .main_token = period,
1170 .data = .{
1171 .lhs = operand,
1172 .rhs = question_mark,
1173 },
1174 });
1175 },
1176 .c_pointer, .single_pointer => {
1177 const payload = @fieldParentPtr(Payload.Pointer, "base", node.ptr_otherwise).data;
1178
1179 const asterisk = if (node.tag() == .single_pointer)
1180 try c.addToken(.asterisk, "*")
1181 else blk: {
1182 _ = try c.addToken(.l_bracket, "[");
1183 const res = try c.addToken(.asterisk, "*");
1184 _ = try c.addIdentifier("c");
1185 _ = try c.addToken(.r_bracket, "]");
1186 break :blk res;
1187 };
1188 if (payload.is_const) _ = try c.addToken(.keyword_const, "const");
1189 if (payload.is_volatile) _ = try c.addToken(.keyword_volatile, "volatile");
1190 const elem_type = try renderNodeGrouped(c, payload.elem_type);
1191
1192 return c.addNode(.{
1193 .tag = .ptr_type_aligned,
1194 .main_token = asterisk,
1195 .data = .{
1196 .lhs = 0,
1197 .rhs = elem_type,
1198 },
1199 });
1200 },
1201 .add => return renderBinOpGrouped(c, node, .add, .plus, "+"),
1202 .add_assign => return renderBinOp(c, node, .assign_add, .plus_equal, "+="),
1203 .add_wrap => return renderBinOpGrouped(c, node, .add_wrap, .plus_percent, "+%"),
1204 .add_wrap_assign => return renderBinOp(c, node, .assign_add_wrap, .plus_percent_equal, "+%="),
1205 .sub => return renderBinOpGrouped(c, node, .sub, .minus, "-"),
1206 .sub_assign => return renderBinOp(c, node, .assign_sub, .minus_equal, "-="),
1207 .sub_wrap => return renderBinOpGrouped(c, node, .sub_wrap, .minus_percent, "-%"),
1208 .sub_wrap_assign => return renderBinOp(c, node, .assign_sub_wrap, .minus_percent_equal, "-%="),
1209 .mul => return renderBinOpGrouped(c, node, .mul, .asterisk, "*"),
1210 .mul_assign => return renderBinOp(c, node, .assign_mul, .asterisk_equal, "*="),
1211 .mul_wrap => return renderBinOpGrouped(c, node, .mul_wrap, .asterisk_percent, "*%"),
1212 .mul_wrap_assign => return renderBinOp(c, node, .assign_mul_wrap, .asterisk_percent_equal, "*%="),
1213 .div => return renderBinOpGrouped(c, node, .div, .slash, "/"),
1214 .div_assign => return renderBinOp(c, node, .assign_div, .slash_equal, "/="),
1215 .shl => return renderBinOpGrouped(c, node, .bit_shift_left, .angle_bracket_angle_bracket_left, "<<"),
1216 .shl_assign => return renderBinOp(c, node, .assign_bit_shift_left, .angle_bracket_angle_bracket_left_equal, "<<="),
1217 .shr => return renderBinOpGrouped(c, node, .bit_shift_right, .angle_bracket_angle_bracket_right, ">>"),
1218 .shr_assign => return renderBinOp(c, node, .assign_bit_shift_right, .angle_bracket_angle_bracket_right_equal, ">>="),
1219 .mod => return renderBinOpGrouped(c, node, .mod, .percent, "%"),
1220 .mod_assign => return renderBinOp(c, node, .assign_mod, .percent_equal, "%="),
1221 .@"and" => return renderBinOpGrouped(c, node, .bool_and, .keyword_and, "and"),
1222 .@"or" => return renderBinOpGrouped(c, node, .bool_or, .keyword_or, "or"),
1223 .less_than => return renderBinOpGrouped(c, node, .less_than, .angle_bracket_left, "<"),
1224 .less_than_equal => return renderBinOpGrouped(c, node, .less_or_equal, .angle_bracket_left_equal, "<="),
1225 .greater_than => return renderBinOpGrouped(c, node, .greater_than, .angle_bracket_right, ">="),
1226 .greater_than_equal => return renderBinOpGrouped(c, node, .greater_or_equal, .angle_bracket_right_equal, ">="),
1227 .equal => return renderBinOpGrouped(c, node, .equal_equal, .equal_equal, "=="),
1228 .not_equal => return renderBinOpGrouped(c, node, .bang_equal, .bang_equal, "!="),
1229 .bit_and => return renderBinOpGrouped(c, node, .bit_and, .ampersand, "&"),
1230 .bit_and_assign => return renderBinOp(c, node, .assign_bit_and, .ampersand_equal, "&="),
1231 .bit_or => return renderBinOpGrouped(c, node, .bit_or, .pipe, "|"),
1232 .bit_or_assign => return renderBinOp(c, node, .assign_bit_or, .pipe_equal, "|="),
1233 .bit_xor => return renderBinOpGrouped(c, node, .bit_xor, .caret, "^"),
1234 .bit_xor_assign => return renderBinOp(c, node, .assign_bit_xor, .caret_equal, "^="),
1235 .array_cat => return renderBinOp(c, node, .array_cat, .plus_plus, "++"),
1236 .ellipsis3 => return renderBinOpGrouped(c, node, .switch_range, .ellipsis3, "..."),
1237 .assign => return renderBinOp(c, node, .assign, .equal, "="),
1238 .empty_block => {
1239 const l_brace = try c.addToken(.l_brace, "{");
1240 _ = try c.addToken(.r_brace, "}");
1241 return c.addNode(.{
1242 .tag = .block_two,
1243 .main_token = l_brace,
1244 .data = .{
1245 .lhs = 0,
1246 .rhs = 0,
1247 },
1248 });
1249 },
1250 .block_single => {
1251 const payload = node.castTag(.block_single).?.data;
1252 const l_brace = try c.addToken(.l_brace, "{");
1253
1254 const stmt = try renderNode(c, payload);
1255 try addSemicolonIfNeeded(c, payload);
1256
1257 _ = try c.addToken(.r_brace, "}");
1258 return c.addNode(.{
1259 .tag = .block_two_semicolon,
1260 .main_token = l_brace,
1261 .data = .{
1262 .lhs = stmt,
1263 .rhs = 0,
1264 },
1265 });
1266 },
1267 .block => {
1268 const payload = node.castTag(.block).?.data;
1269 if (payload.label) |some| {
1270 _ = try c.addIdentifier(some);
1271 _ = try c.addToken(.colon, ":");
1272 }
1273 const l_brace = try c.addToken(.l_brace, "{");
1274
1275 var stmts = std.ArrayList(NodeIndex).init(c.gpa);
1276 defer stmts.deinit();
1277 for (payload.stmts) |stmt| {
1278 const res = try renderNode(c, stmt);
1279 if (res == 0) continue;
1280 try addSemicolonIfNeeded(c, stmt);
1281 try stmts.append(res);
1282 }
1283 const span = try c.listToSpan(stmts.items);
1284 _ = try c.addToken(.r_brace, "}");
1285
1286 const semicolon = c.tokens.items(.tag)[c.tokens.len - 2] == .semicolon;
1287 return c.addNode(.{
1288 .tag = if (semicolon) .block_semicolon else .block,
1289 .main_token = l_brace,
1290 .data = .{
1291 .lhs = span.start,
1292 .rhs = span.end,
1293 },
1294 });
1295 },
1296 .func => return renderFunc(c, node),
1297 .pub_inline_fn => return renderMacroFunc(c, node),
1298 .discard => {
1299 const payload = node.castTag(.discard).?.data;
1300 const lhs = try c.addNode(.{
1301 .tag = .identifier,
1302 .main_token = try c.addToken(.identifier, "_"),
1303 .data = undefined,
1304 });
1305 return c.addNode(.{
1306 .tag = .assign,
1307 .main_token = try c.addToken(.equal, "="),
1308 .data = .{
1309 .lhs = lhs,
1310 .rhs = try renderNode(c, payload),
1311 },
1312 });
1313 },
1314 .@"while" => {
1315 const payload = node.castTag(.@"while").?.data;
1316 const while_tok = try c.addToken(.keyword_while, "while");
1317 _ = try c.addToken(.l_paren, "(");
1318 const cond = try renderNode(c, payload.cond);
1319 _ = try c.addToken(.r_paren, ")");
1320
1321 const cont_expr = if (payload.cont_expr) |some| blk: {
1322 _ = try c.addToken(.colon, ":");
1323 _ = try c.addToken(.l_paren, "(");
1324 const res = try renderNode(c, some);
1325 _ = try c.addToken(.r_paren, ")");
1326 break :blk res;
1327 } else 0;
1328 const body = try renderNode(c, payload.body);
1329
1330 if (cont_expr == 0) {
1331 return c.addNode(.{
1332 .tag = .while_simple,
1333 .main_token = while_tok,
1334 .data = .{
1335 .lhs = cond,
1336 .rhs = body,
1337 },
1338 });
1339 } else {
1340 return c.addNode(.{
1341 .tag = .while_cont,
1342 .main_token = while_tok,
1343 .data = .{
1344 .lhs = cond,
1345 .rhs = try c.addExtra(std.zig.ast.Node.WhileCont{
1346 .cont_expr = cont_expr,
1347 .then_expr = body,
1348 }),
1349 },
1350 });
1351 }
1352 },
1353 .while_true => {
1354 const payload = node.castTag(.while_true).?.data;
1355 const while_tok = try c.addToken(.keyword_while, "while");
1356 _ = try c.addToken(.l_paren, "(");
1357 const cond = try c.addNode(.{
1358 .tag = .true_literal,
1359 .main_token = try c.addToken(.keyword_true, "true"),
1360 .data = undefined,
1361 });
1362 _ = try c.addToken(.r_paren, ")");
1363 const body = try renderNode(c, payload);
1364
1365 return c.addNode(.{
1366 .tag = .while_simple,
1367 .main_token = while_tok,
1368 .data = .{
1369 .lhs = cond,
1370 .rhs = body,
1371 },
1372 });
1373 },
1374 .@"if" => {
1375 const payload = node.castTag(.@"if").?.data;
1376 const if_tok = try c.addToken(.keyword_if, "if");
1377 _ = try c.addToken(.l_paren, "(");
1378 const cond = try renderNode(c, payload.cond);
1379 _ = try c.addToken(.r_paren, ")");
1380
1381 const then_expr = try renderNode(c, payload.then);
1382 const else_node = payload.@"else" orelse return c.addNode(.{
1383 .tag = .if_simple,
1384 .main_token = if_tok,
1385 .data = .{
1386 .lhs = cond,
1387 .rhs = then_expr,
1388 },
1389 });
1390 _ = try c.addToken(.keyword_else, "else");
1391 const else_expr = try renderNode(c, else_node);
1392
1393 return c.addNode(.{
1394 .tag = .@"if",
1395 .main_token = if_tok,
1396 .data = .{
1397 .lhs = cond,
1398 .rhs = try c.addExtra(std.zig.ast.Node.If{
1399 .then_expr = then_expr,
1400 .else_expr = else_expr,
1401 }),
1402 },
1403 });
1404 },
1405 .if_not_break => {
1406 const payload = node.castTag(.if_not_break).?.data;
1407 const if_tok = try c.addToken(.keyword_if, "if");
1408 _ = try c.addToken(.l_paren, "(");
1409 const cond = try c.addNode(.{
1410 .tag = .bool_not,
1411 .main_token = try c.addToken(.bang, "!"),
1412 .data = .{
1413 .lhs = try renderNodeGrouped(c, payload),
1414 .rhs = undefined,
1415 },
1416 });
1417 _ = try c.addToken(.r_paren, ")");
1418 const then_expr = try c.addNode(.{
1419 .tag = .@"break",
1420 .main_token = try c.addToken(.keyword_break, "break"),
1421 .data = .{
1422 .lhs = 0,
1423 .rhs = 0,
1424 },
1425 });
1426
1427 return c.addNode(.{
1428 .tag = .if_simple,
1429 .main_token = if_tok,
1430 .data = .{
1431 .lhs = cond,
1432 .rhs = then_expr,
1433 },
1434 });
1435 },
1436 .@"switch" => {
1437 const payload = node.castTag(.@"switch").?.data;
1438 const switch_tok = try c.addToken(.keyword_switch, "switch");
1439 _ = try c.addToken(.l_paren, "(");
1440 const cond = try renderNode(c, payload.cond);
1441 _ = try c.addToken(.r_paren, ")");
1442
1443 _ = try c.addToken(.l_brace, "{");
1444 var cases = try c.gpa.alloc(NodeIndex, payload.cases.len);
1445 defer c.gpa.free(cases);
1446 for (payload.cases) |case, i| {
1447 cases[i] = try renderNode(c, case);
1448 _ = try c.addToken(.comma, ",");
1449 }
1450 const span = try c.listToSpan(cases);
1451 _ = try c.addToken(.r_brace, "}");
1452 return c.addNode(.{
1453 .tag = .switch_comma,
1454 .main_token = switch_tok,
1455 .data = .{
1456 .lhs = cond,
1457 .rhs = try c.addExtra(NodeSubRange{
1458 .start = span.start,
1459 .end = span.end,
1460 }),
1461 },
1462 });
1463 },
1464 .switch_else => {
1465 const payload = node.castTag(.switch_else).?.data;
1466 _ = try c.addToken(.keyword_else, "else");
1467 return c.addNode(.{
1468 .tag = .switch_case_one,
1469 .main_token = try c.addToken(.equal_angle_bracket_right, "=>"),
1470 .data = .{
1471 .lhs = 0,
1472 .rhs = try renderNode(c, payload),
1473 },
1474 });
1475 },
1476 .switch_prong => {
1477 const payload = node.castTag(.switch_prong).?.data;
1478 var items = try c.gpa.alloc(NodeIndex, std.math.max(payload.cases.len, 1));
1479 defer c.gpa.free(items);
1480 items[0] = 0;
1481 for (payload.cases) |item, i| {
1482 if (i != 0) _ = try c.addToken(.comma, ",");
1483 items[i] = try renderNode(c, item);
1484 }
1485 _ = try c.addToken(.r_brace, "}");
1486 if (items.len < 2) {
1487 return c.addNode(.{
1488 .tag = .switch_case_one,
1489 .main_token = try c.addToken(.equal_angle_bracket_right, "=>"),
1490 .data = .{
1491 .lhs = items[0],
1492 .rhs = try renderNode(c, payload.cond),
1493 },
1494 });
1495 } else {
1496 const span = try c.listToSpan(items);
1497 return c.addNode(.{
1498 .tag = .switch_case,
1499 .main_token = try c.addToken(.equal_angle_bracket_right, "=>"),
1500 .data = .{
1501 .lhs = try c.addExtra(NodeSubRange{
1502 .start = span.start,
1503 .end = span.end,
1504 }),
1505 .rhs = try renderNode(c, payload.cond),
1506 },
1507 });
1508 }
1509 },
1510 .opaque_literal => {
1511 const opaque_tok = try c.addToken(.keyword_opaque, "opaque");
1512 _ = try c.addToken(.l_brace, "{");
1513 _ = try c.addToken(.r_brace, "}");
1514
1515 return c.addNode(.{
1516 .tag = .container_decl_two,
1517 .main_token = opaque_tok,
1518 .data = .{
1519 .lhs = 0,
1520 .rhs = 0,
1521 },
1522 });
1523 },
1524 .array_access => {
1525 const payload = node.castTag(.array_access).?.data;
1526 const lhs = try renderNode(c, payload.lhs);
1527 const l_bracket = try c.addToken(.l_bracket, "[");
1528 const index_expr = try renderNode(c, payload.rhs);
1529 _ = try c.addToken(.r_bracket, "]");
1530 return c.addNode(.{
1531 .tag = .array_access,
1532 .main_token = l_bracket,
1533 .data = .{
1534 .lhs = lhs,
1535 .rhs = index_expr,
1536 },
1537 });
1538 },
1539 .array_type => {
1540 const payload = node.castTag(.array_type).?.data;
1541 return renderArrayType(c, payload.len, payload.elem_type);
1542 },
1543 .array_filler => {
1544 const payload = node.castTag(.array_filler).?.data;
1545
1546 const type_expr = try renderArrayType(c, 1, payload.type);
1547 const l_brace = try c.addToken(.l_brace, "{");
1548 const val = try renderNode(c, payload.filler);
1549 _ = try c.addToken(.r_brace, "}");
1550
1551 const init = try c.addNode(.{
1552 .tag = .array_init_one,
1553 .main_token = l_brace,
1554 .data = .{
1555 .lhs = type_expr,
1556 .rhs = val,
1557 },
1558 });
1559 return c.addNode(.{
1560 .tag = .array_cat,
1561 .main_token = try c.addToken(.asterisk_asterisk, "**"),
1562 .data = .{
1563 .lhs = init,
1564 .rhs = try c.addNode(.{
1565 .tag = .integer_literal,
1566 .main_token = try c.addTokenFmt(.integer_literal, "{d}", .{payload.count}),
1567 .data = undefined,
1568 }),
1569 },
1570 });
1571 },
1572 .empty_array => {
1573 const payload = node.castTag(.empty_array).?.data;
1574
1575 const type_expr = try renderArrayType(c, 0, payload);
1576 return renderArrayInit(c, type_expr, &.{});
1577 },
1578 .array_init => {
1579 const payload = node.castTag(.array_init).?.data;
1580 const type_expr = try renderNode(c, payload.cond);
1581 return renderArrayInit(c, type_expr, payload.cases);
1582 },
1583 .field_access => {
1584 const payload = node.castTag(.field_access).?.data;
1585 const lhs = try renderNode(c, payload.lhs);
1586 return renderFieldAccess(c, lhs, payload.field_name);
1587 },
1588 .@"struct", .@"union" => return renderRecord(c, node),
1589 .@"enum" => {
1590 const payload = node.castTag(.@"enum").?.data;
1591 _ = try c.addToken(.keyword_extern, "extern");
1592 const enum_tok = try c.addToken(.keyword_enum, "enum");
1593 _ = try c.addToken(.l_paren, "(");
1594 const arg_expr = try renderNode(c, payload.int_type);
1595 _ = try c.addToken(.r_paren, ")");
1596 _ = try c.addToken(.l_brace, "{");
1597 const members = try c.gpa.alloc(NodeIndex, std.math.max(payload.fields.len + 1, 1));
1598 defer c.gpa.free(members);
1599 members[0] = 0;
1600
1601 for (payload.fields) |field, i| {
1602 const name_tok = try c.addIdentifier(field.name);
1603 const value_expr = if (field.value) |some| blk: {
1604 _ = try c.addToken(.equal, "=");
1605 break :blk try renderNode(c, some);
1606 } else 0;
1607
1608 members[i] = try c.addNode(.{
1609 .tag = .container_field_init,
1610 .main_token = name_tok,
1611 .data = .{
1612 .lhs = 0,
1613 .rhs = value_expr,
1614 },
1615 });
1616 _ = try c.addToken(.comma, ",");
1617 }
1618 // make non-exhaustive
1619 members[payload.fields.len] = try c.addNode(.{
1620 .tag = .container_field_init,
1621 .main_token = try c.addIdentifier("_"),
1622 .data = .{
1623 .lhs = 0,
1624 .rhs = 0,
1625 },
1626 });
1627 _ = try c.addToken(.comma, ",");
1628 _ = try c.addToken(.r_brace, "}");
1629
1630 const span = try c.listToSpan(members);
1631 return c.addNode(.{
1632 .tag = .container_decl_arg_trailing,
1633 .main_token = enum_tok,
1634 .data = .{
1635 .lhs = arg_expr,
1636 .rhs = try c.addExtra(NodeSubRange{
1637 .start = span.start,
1638 .end = span.end,
1639 }),
1640 },
1641 });
1642 },
1643 .pub_enum_redecl, .enum_redecl => {
1644 const payload = @fieldParentPtr(Payload.EnumRedecl, "base", node.ptr_otherwise).data;
1645 if (node.tag() == .pub_enum_redecl) _ = try c.addToken(.keyword_pub, "pub");
1646 const const_tok = try c.addToken(.keyword_const, "const");
1647 _ = try c.addIdentifier(payload.enum_val_name);
1648 _ = try c.addToken(.equal, "=");
1649
1650 const enum_to_int_tok = try c.addToken(.builtin, "@enumToInt");
1651 _ = try c.addToken(.l_paren, "(");
1652 const enum_name = try c.addNode(.{
1653 .tag = .identifier,
1654 .main_token = try c.addIdentifier(payload.enum_name),
1655 .data = undefined,
1656 });
1657 const field_access = try renderFieldAccess(c, enum_name, payload.field_name);
1658 const init_node = try c.addNode(.{
1659 .tag = .builtin_call_two,
1660 .main_token = enum_to_int_tok,
1661 .data = .{
1662 .lhs = field_access,
1663 .rhs = 0,
1664 },
1665 });
1666 _ = try c.addToken(.r_paren, ")");
1667 _ = try c.addToken(.semicolon, ";");
1668
1669 return c.addNode(.{
1670 .tag = .simple_var_decl,
1671 .main_token = const_tok,
1672 .data = .{
1673 .lhs = 0,
1674 .rhs = init_node,
1675 },
1676 });
1677 },
1678 .tuple => {
1679 const payload = node.castTag(.tuple).?.data;
1680 _ = try c.addToken(.period, ".");
1681 const l_brace = try c.addToken(.l_brace, "{");
1682 var inits = try c.gpa.alloc(NodeIndex, std.math.max(payload.len, 2));
1683 defer c.gpa.free(inits);
1684 inits[0] = 0;
1685 inits[1] = 0;
1686 for (payload) |init, i| {
1687 if (i != 0) _ = try c.addToken(.comma, ",");
1688 inits[i] = try renderNode(c, init);
1689 }
1690 _ = try c.addToken(.r_brace, "}");
1691 if (payload.len < 3) {
1692 return c.addNode(.{
1693 .tag = .array_init_dot_two,
1694 .main_token = l_brace,
1695 .data = .{
1696 .lhs = inits[0],
1697 .rhs = inits[1],
1698 },
1699 });
1700 } else {
1701 const span = try c.listToSpan(inits);
1702 return c.addNode(.{
1703 .tag = .array_init_dot,
1704 .main_token = l_brace,
1705 .data = .{
1706 .lhs = span.start,
1707 .rhs = span.end,
1708 },
1709 });
1710 }
1711 },
1712 .container_init => {
1713 const payload = node.castTag(.container_init).?.data;
1714 const lhs = try renderNode(c, payload.lhs);
1715
1716 const l_brace = try c.addToken(.l_brace, "{");
1717 var inits = try c.gpa.alloc(NodeIndex, std.math.max(payload.inits.len, 1));
1718 defer c.gpa.free(inits);
1719 inits[0] = 0;
1720 for (payload.inits) |init, i| {
1721 _ = try c.addToken(.period, ".");
1722 _ = try c.addIdentifier(init.name);
1723 _ = try c.addToken(.equal, "=");
1724 inits[i] = try renderNode(c, init.value);
1725 _ = try c.addToken(.comma, ",");
1726 }
1727 _ = try c.addToken(.r_brace, "}");
1728
1729 if (payload.inits.len < 2) {
1730 return c.addNode(.{
1731 .tag = .struct_init_one_comma,
1732 .main_token = l_brace,
1733 .data = .{
1734 .lhs = lhs,
1735 .rhs = inits[0],
1736 },
1737 });
1738 } else {
1739 const span = try c.listToSpan(inits);
1740 return c.addNode(.{
1741 .tag = .struct_init_comma,
1742 .main_token = l_brace,
1743 .data = .{
1744 .lhs = lhs,
1745 .rhs = try c.addExtra(NodeSubRange{
1746 .start = span.start,
1747 .end = span.end,
1748 }),
1749 },
1750 });
1751 }
1752 },
1753 .@"anytype" => unreachable, // Handled in renderParams
1754 }
1755}
1756
1757fn renderRecord(c: *Context, node: Node) !NodeIndex {
1758 const payload = @fieldParentPtr(Payload.Record, "base", node.ptr_otherwise).data;
1759 if (payload.is_packed)
1760 _ = try c.addToken(.keyword_packed, "packed")
1761 else
1762 _ = try c.addToken(.keyword_extern, "extern");
1763 const kind_tok = if (node.tag() == .@"struct")
1764 try c.addToken(.keyword_struct, "struct")
1765 else
1766 try c.addToken(.keyword_union, "union");
1767
1768 _ = try c.addToken(.l_brace, "{");
1769 const members = try c.gpa.alloc(NodeIndex, std.math.max(payload.fields.len, 2));
1770 defer c.gpa.free(members);
1771 members[0] = 0;
1772 members[1] = 0;
1773
1774 for (payload.fields) |field, i| {
1775 const name_tok = try c.addIdentifier(field.name);
1776 _ = try c.addToken(.colon, ":");
1777 const type_expr = try renderNode(c, field.type);
1778
1779 const alignment = field.alignment orelse {
1780 members[i] = try c.addNode(.{
1781 .tag = .container_field_init,
1782 .main_token = name_tok,
1783 .data = .{
1784 .lhs = type_expr,
1785 .rhs = 0,
1786 },
1787 });
1788 _ = try c.addToken(.comma, ",");
1789 continue;
1790 };
1791 _ = try c.addToken(.keyword_align, "align");
1792 _ = try c.addToken(.l_paren, "(");
1793 const align_expr = try c.addNode(.{
1794 .tag = .integer_literal,
1795 .main_token = try c.addTokenFmt(.integer_literal, "{d}", .{alignment}),
1796 .data = undefined,
1797 });
1798 _ = try c.addToken(.r_paren, ")");
1799
1800 members[i] = try c.addNode(.{
1801 .tag = .container_field_align,
1802 .main_token = name_tok,
1803 .data = .{
1804 .lhs = type_expr,
1805 .rhs = align_expr,
1806 },
1807 });
1808 _ = try c.addToken(.comma, ",");
1809 }
1810 _ = try c.addToken(.r_brace, "}");
1811
1812 if (payload.fields.len == 0) {
1813 return c.addNode(.{
1814 .tag = .container_decl_two,
1815 .main_token = kind_tok,
1816 .data = .{
1817 .lhs = 0,
1818 .rhs = 0,
1819 },
1820 });
1821 } else if (payload.fields.len <= 2) {
1822 return c.addNode(.{
1823 .tag = .container_decl_two_trailing,
1824 .main_token = kind_tok,
1825 .data = .{
1826 .lhs = members[0],
1827 .rhs = members[1],
1828 },
1829 });
1830 } else {
1831 const span = try c.listToSpan(members);
1832 return c.addNode(.{
1833 .tag = .container_decl_trailing,
1834 .main_token = kind_tok,
1835 .data = .{
1836 .lhs = span.start,
1837 .rhs = span.end,
1838 },
1839 });
1840 }
1841}
1842
1843fn renderFieldAccess(c: *Context, lhs: NodeIndex, field_name: []const u8) !NodeIndex {
1844 return c.addNode(.{
1845 .tag = .field_access,
1846 .main_token = try c.addToken(.period, "."),
1847 .data = .{
1848 .lhs = lhs,
1849 .rhs = try c.addIdentifier(field_name),
1850 },
1851 });
1852}
1853
1854fn renderArrayInit(c: *Context, lhs: NodeIndex, inits: []const Node) !NodeIndex {
1855 const l_brace = try c.addToken(.l_brace, "{");
1856 var rendered = try c.gpa.alloc(NodeIndex, std.math.max(inits.len, 1));
1857 defer c.gpa.free(rendered);
1858 rendered[0] = 0;
1859 for (inits) |init, i| {
1860 rendered[i] = try renderNode(c, init);
1861 _ = try c.addToken(.comma, ",");
1862 }
1863 _ = try c.addToken(.r_brace, "}");
1864 if (inits.len < 2) {
1865 return c.addNode(.{
1866 .tag = .array_init_one_comma,
1867 .main_token = l_brace,
1868 .data = .{
1869 .lhs = lhs,
1870 .rhs = rendered[0],
1871 },
1872 });
1873 } else {
1874 const span = try c.listToSpan(rendered);
1875 return c.addNode(.{
1876 .tag = .array_init_comma,
1877 .main_token = l_brace,
1878 .data = .{
1879 .lhs = lhs,
1880 .rhs = try c.addExtra(NodeSubRange{
1881 .start = span.start,
1882 .end = span.end,
1883 }),
1884 },
1885 });
1886 }
1887}
1888
1889fn renderArrayType(c: *Context, len: usize, elem_type: Node) !NodeIndex {
1890 const l_bracket = try c.addToken(.l_bracket, "[");
1891 const len_expr = try c.addNode(.{
1892 .tag = .integer_literal,
1893 .main_token = try c.addTokenFmt(.integer_literal, "{d}", .{len}),
1894 .data = undefined,
1895 });
1896 _ = try c.addToken(.r_bracket, "]");
1897 const elem_type_expr = try renderNode(c, elem_type);
1898 return c.addNode(.{
1899 .tag = .array_type,
1900 .main_token = l_bracket,
1901 .data = .{
1902 .lhs = len_expr,
1903 .rhs = elem_type_expr,
1904 },
1905 });
1906}
1907
1908fn addSemicolonIfNeeded(c: *Context, node: Node) !void {
1909 switch (node.tag()) {
1910 .warning => unreachable,
1911 .var_decl, .var_simple, .arg_redecl, .alias, .enum_redecl, .block, .empty_block, .block_single, .@"switch" => {},
1912 .while_true => {
1913 const payload = node.castTag(.while_true).?.data;
1914 return addSemicolonIfNotBlock(c, payload);
1915 },
1916 .@"while" => {
1917 const payload = node.castTag(.@"while").?.data;
1918 return addSemicolonIfNotBlock(c, payload.body);
1919 },
1920 .@"if" => {
1921 const payload = node.castTag(.@"if").?.data;
1922 if (payload.@"else") |some|
1923 return addSemicolonIfNeeded(c, some);
1924 return addSemicolonIfNotBlock(c, payload.then);
1925 },
1926 else => _ = try c.addToken(.semicolon, ";"),
1927 }
1928}
1929
1930fn addSemicolonIfNotBlock(c: *Context, node: Node) !void {
1931 switch (node.tag()) {
1932 .block, .empty_block, .block_single => {},
1933 else => _ = try c.addToken(.semicolon, ";"),
1934 }
1935}
1936
1937fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
1938 switch (node.tag()) {
1939 .declaration => unreachable,
1940 .null_literal,
1941 .undefined_literal,
1942 .true_literal,
1943 .false_literal,
1944 .return_void,
1945 .zero_literal,
1946 .one_literal,
1947 .void_type,
1948 .noreturn_type,
1949 .@"anytype",
1950 .div_trunc,
1951 .rem,
1952 .int_cast,
1953 .as,
1954 .truncate,
1955 .bit_cast,
1956 .float_cast,
1957 .float_to_int,
1958 .int_to_float,
1959 .int_to_enum,
1960 .int_to_ptr,
1961 .std_mem_zeroes,
1962 .std_math_Log2Int,
1963 .log2_int_type,
1964 .ptr_to_int,
1965 .enum_to_int,
1966 .sizeof,
1967 .alignof,
1968 .typeof,
1969 .std_meta_sizeof,
1970 .std_meta_cast,
1971 .std_mem_zeroinit,
1972 .integer_literal,
1973 .float_literal,
1974 .string_literal,
1975 .char_literal,
1976 .identifier,
1977 .field_access,
1978 .ptr_cast,
1979 .type,
1980 .array_access,
1981 .align_cast,
1982 .optional_type,
1983 .c_pointer,
1984 .single_pointer,
1985 .unwrap,
1986 .deref,
1987 .address_of,
1988 .not,
1989 .negate,
1990 .negate_wrap,
1991 .bit_not,
1992 .func,
1993 .call,
1994 .array_type,
1995 .bool_to_int,
1996 => {
1997 // no grouping needed
1998 return renderNode(c, node);
1999 },
2000
2001 .opaque_literal,
2002 .empty_array,
2003 .block_single,
2004 .add,
2005 .add_wrap,
2006 .sub,
2007 .sub_wrap,
2008 .mul,
2009 .mul_wrap,
2010 .div,
2011 .shl,
2012 .shr,
2013 .mod,
2014 .@"and",
2015 .@"or",
2016 .less_than,
2017 .less_than_equal,
2018 .greater_than,
2019 .greater_than_equal,
2020 .equal,
2021 .not_equal,
2022 .bit_and,
2023 .bit_or,
2024 .bit_xor,
2025 .empty_block,
2026 .array_cat,
2027 .array_filler,
2028 .@"if",
2029 .@"enum",
2030 .@"struct",
2031 .@"union",
2032 .array_init,
2033 .tuple,
2034 .container_init,
2035 .block,
2036 => return c.addNode(.{
2037 .tag = .grouped_expression,
2038 .main_token = try c.addToken(.l_paren, "("),
2039 .data = .{
2040 .lhs = try renderNode(c, node),
2041 .rhs = try c.addToken(.r_paren, ")"),
2042 },
2043 }),
2044 .ellipsis3,
2045 .switch_prong,
2046 .warning,
2047 .var_decl,
2048 .fail_decl,
2049 .arg_redecl,
2050 .alias,
2051 .var_simple,
2052 .pub_var_simple,
2053 .pub_enum_redecl,
2054 .enum_redecl,
2055 .@"while",
2056 .@"switch",
2057 .@"break",
2058 .break_val,
2059 .pub_inline_fn,
2060 .discard,
2061 .@"continue",
2062 .@"return",
2063 .usingnamespace_builtins,
2064 .while_true,
2065 .if_not_break,
2066 .switch_else,
2067 .add_assign,
2068 .add_wrap_assign,
2069 .sub_assign,
2070 .sub_wrap_assign,
2071 .mul_assign,
2072 .mul_wrap_assign,
2073 .div_assign,
2074 .shl_assign,
2075 .shr_assign,
2076 .mod_assign,
2077 .bit_and_assign,
2078 .bit_or_assign,
2079 .bit_xor_assign,
2080 .assign,
2081 => {
2082 // these should never appear in places where grouping might be needed.
2083 unreachable;
2084 },
2085 }
2086}
2087
2088fn renderPrefixOp(c: *Context, node: Node, tag: std.zig.ast.Node.Tag, tok_tag: TokenTag, bytes: []const u8) !NodeIndex {
2089 const payload = @fieldParentPtr(Payload.UnOp, "base", node.ptr_otherwise).data;
2090 return c.addNode(.{
2091 .tag = tag,
2092 .main_token = try c.addToken(tok_tag, bytes),
2093 .data = .{
2094 .lhs = try renderNodeGrouped(c, payload),
2095 .rhs = undefined,
2096 },
2097 });
2098}
2099
2100fn renderBinOpGrouped(c: *Context, node: Node, tag: std.zig.ast.Node.Tag, tok_tag: TokenTag, bytes: []const u8) !NodeIndex {
2101 const payload = @fieldParentPtr(Payload.BinOp, "base", node.ptr_otherwise).data;
2102 const lhs = try renderNodeGrouped(c, payload.lhs);
2103 return c.addNode(.{
2104 .tag = tag,
2105 .main_token = try c.addToken(tok_tag, bytes),
2106 .data = .{
2107 .lhs = lhs,
2108 .rhs = try renderNodeGrouped(c, payload.rhs),
2109 },
2110 });
2111}
2112
2113fn renderBinOp(c: *Context, node: Node, tag: std.zig.ast.Node.Tag, tok_tag: TokenTag, bytes: []const u8) !NodeIndex {
2114 const payload = @fieldParentPtr(Payload.BinOp, "base", node.ptr_otherwise).data;
2115 const lhs = try renderNode(c, payload.lhs);
2116 return c.addNode(.{
2117 .tag = tag,
2118 .main_token = try c.addToken(tok_tag, bytes),
2119 .data = .{
2120 .lhs = lhs,
2121 .rhs = try renderNode(c, payload.rhs),
2122 },
2123 });
2124}
2125
2126fn renderStdImport(c: *Context, first: []const u8, second: []const u8) !NodeIndex {
2127 const import_tok = try c.addToken(.builtin, "@import");
2128 _ = try c.addToken(.l_paren, "(");
2129 const std_tok = try c.addToken(.string_literal, "\"std\"");
2130 const std_node = try c.addNode(.{
2131 .tag = .string_literal,
2132 .main_token = std_tok,
2133 .data = undefined,
2134 });
2135 _ = try c.addToken(.r_paren, ")");
2136
2137 const import_node = try c.addNode(.{
2138 .tag = .builtin_call_two,
2139 .main_token = import_tok,
2140 .data = .{
2141 .lhs = std_node,
2142 .rhs = 0,
2143 },
2144 });
2145
2146 var access_chain = import_node;
2147 access_chain = try renderFieldAccess(c, access_chain, first);
2148 access_chain = try renderFieldAccess(c, access_chain, second);
2149 return access_chain;
2150}
2151
2152fn renderCall(c: *Context, lhs: NodeIndex, args: []const Node) !NodeIndex {
2153 const lparen = try c.addToken(.l_paren, "(");
2154 const res = switch (args.len) {
2155 0 => try c.addNode(.{
2156 .tag = .call_one,
2157 .main_token = lparen,
2158 .data = .{
2159 .lhs = lhs,
2160 .rhs = 0,
2161 },
2162 }),
2163 1 => blk: {
2164 const arg = try renderNode(c, args[0]);
2165 break :blk try c.addNode(.{
2166 .tag = .call_one,
2167 .main_token = lparen,
2168 .data = .{
2169 .lhs = lhs,
2170 .rhs = arg,
2171 },
2172 });
2173 },
2174 else => blk: {
2175 var rendered = try c.gpa.alloc(NodeIndex, args.len);
2176 defer c.gpa.free(rendered);
2177
2178 for (args) |arg, i| {
2179 if (i != 0) _ = try c.addToken(.comma, ",");
2180 rendered[i] = try renderNode(c, arg);
2181 }
2182 const span = try c.listToSpan(rendered);
2183 break :blk try c.addNode(.{
2184 .tag = .call,
2185 .main_token = lparen,
2186 .data = .{
2187 .lhs = lhs,
2188 .rhs = try c.addExtra(NodeSubRange{
2189 .start = span.start,
2190 .end = span.end,
2191 }),
2192 },
2193 });
2194 },
2195 };
2196 _ = try c.addToken(.r_paren, ")");
2197 return res;
2198}
2199
2200fn renderBuiltinCall(c: *Context, builtin: []const u8, args: []const Node) !NodeIndex {
2201 const builtin_tok = try c.addToken(.builtin, builtin);
2202 _ = try c.addToken(.l_paren, "(");
2203 var arg_1: NodeIndex = 0;
2204 var arg_2: NodeIndex = 0;
2205 switch (args.len) {
2206 0 => {},
2207 1 => {
2208 arg_1 = try renderNode(c, args[0]);
2209 },
2210 2 => {
2211 arg_1 = try renderNode(c, args[0]);
2212 _ = try c.addToken(.comma, ",");
2213 arg_2 = try renderNode(c, args[1]);
2214 },
2215 else => unreachable, // expand this function as needed.
2216 }
2217
2218 _ = try c.addToken(.r_paren, ")");
2219 return c.addNode(.{
2220 .tag = .builtin_call_two,
2221 .main_token = builtin_tok,
2222 .data = .{
2223 .lhs = arg_1,
2224 .rhs = arg_2,
2225 },
2226 });
2227}
2228
2229fn renderVar(c: *Context, node: Node) !NodeIndex {
2230 const payload = node.castTag(.var_decl).?.data;
2231 if (payload.is_pub) _ = try c.addToken(.keyword_pub, "pub");
2232 if (payload.is_extern) _ = try c.addToken(.keyword_extern, "extern");
2233 if (payload.is_export) _ = try c.addToken(.keyword_export, "export");
2234 if (payload.is_threadlocal) _ = try c.addToken(.keyword_threadlocal, "threadlocal");
2235 const mut_tok = if (payload.is_const)
2236 try c.addToken(.keyword_const, "const")
2237 else
2238 try c.addToken(.keyword_var, "var");
2239 _ = try c.addIdentifier(payload.name);
2240 _ = try c.addToken(.colon, ":");
2241 const type_node = try renderNode(c, payload.type);
2242
2243 const align_node = if (payload.alignment) |some| blk: {
2244 _ = try c.addToken(.keyword_align, "align");
2245 _ = try c.addToken(.l_paren, "(");
2246 const res = try c.addNode(.{
2247 .tag = .integer_literal,
2248 .main_token = try c.addTokenFmt(.integer_literal, "{d}", .{some}),
2249 .data = undefined,
2250 });
2251 _ = try c.addToken(.r_paren, ")");
2252 break :blk res;
2253 } else 0;
2254
2255 const section_node = if (payload.linksection_string) |some| blk: {
2256 _ = try c.addToken(.keyword_linksection, "linksection");
2257 _ = try c.addToken(.l_paren, "(");
2258 const res = try c.addNode(.{
2259 .tag = .string_literal,
2260 .main_token = try c.addTokenFmt(.string_literal, "\"{s}\"", .{std.zig.fmtEscapes(some)}),
2261 .data = undefined,
2262 });
2263 _ = try c.addToken(.r_paren, ")");
2264 break :blk res;
2265 } else 0;
2266
2267 const init_node = if (payload.init) |some| blk: {
2268 _ = try c.addToken(.equal, "=");
2269 break :blk try renderNode(c, some);
2270 } else 0;
2271 _ = try c.addToken(.semicolon, ";");
2272
2273 if (section_node == 0) {
2274 if (align_node == 0) {
2275 return c.addNode(.{
2276 .tag = .simple_var_decl,
2277 .main_token = mut_tok,
2278 .data = .{
2279 .lhs = type_node,
2280 .rhs = init_node,
2281 },
2282 });
2283 } else {
2284 return c.addNode(.{
2285 .tag = .local_var_decl,
2286 .main_token = mut_tok,
2287 .data = .{
2288 .lhs = try c.addExtra(std.zig.ast.Node.LocalVarDecl{
2289 .type_node = type_node,
2290 .align_node = align_node,
2291 }),
2292 .rhs = init_node,
2293 },
2294 });
2295 }
2296 } else {
2297 return c.addNode(.{
2298 .tag = .global_var_decl,
2299 .main_token = mut_tok,
2300 .data = .{
2301 .lhs = try c.addExtra(std.zig.ast.Node.GlobalVarDecl{
2302 .type_node = type_node,
2303 .align_node = align_node,
2304 .section_node = section_node,
2305 }),
2306 .rhs = init_node,
2307 },
2308 });
2309 }
2310}
2311
2312fn renderFunc(c: *Context, node: Node) !NodeIndex {
2313 const payload = node.castTag(.func).?.data;
2314 if (payload.is_pub) _ = try c.addToken(.keyword_pub, "pub");
2315 if (payload.is_extern) _ = try c.addToken(.keyword_extern, "extern");
2316 if (payload.is_export) _ = try c.addToken(.keyword_export, "export");
2317 const fn_token = try c.addToken(.keyword_fn, "fn");
2318 if (payload.name) |some| _ = try c.addIdentifier(some);
2319
2320 const params = try renderParams(c, payload.params, payload.is_var_args);
2321 defer params.deinit();
2322 var span: NodeSubRange = undefined;
2323 if (params.items.len > 1) span = try c.listToSpan(params.items);
2324
2325 const align_expr = if (payload.alignment) |some| blk: {
2326 _ = try c.addToken(.keyword_align, "align");
2327 _ = try c.addToken(.l_paren, "(");
2328 const res = try c.addNode(.{
2329 .tag = .integer_literal,
2330 .main_token = try c.addTokenFmt(.integer_literal, "{d}", .{some}),
2331 .data = undefined,
2332 });
2333 _ = try c.addToken(.r_paren, ")");
2334 break :blk res;
2335 } else 0;
2336
2337 const section_expr = if (payload.linksection_string) |some| blk: {
2338 _ = try c.addToken(.keyword_linksection, "linksection");
2339 _ = try c.addToken(.l_paren, "(");
2340 const res = try c.addNode(.{
2341 .tag = .string_literal,
2342 .main_token = try c.addTokenFmt(.string_literal, "\"{s}\"", .{std.zig.fmtEscapes(some)}),
2343 .data = undefined,
2344 });
2345 _ = try c.addToken(.r_paren, ")");
2346 break :blk res;
2347 } else 0;
2348
2349 const callconv_expr = if (payload.explicit_callconv) |some| blk: {
2350 _ = try c.addToken(.keyword_callconv, "callconv");
2351 _ = try c.addToken(.l_paren, "(");
2352 _ = try c.addToken(.period, ".");
2353 const res = try c.addNode(.{
2354 .tag = .enum_literal,
2355 .main_token = try c.addTokenFmt(.identifier, "{s}", .{@tagName(some)}),
2356 .data = undefined,
2357 });
2358 _ = try c.addToken(.r_paren, ")");
2359 break :blk res;
2360 } else 0;
2361
2362 const return_type_expr = try renderNode(c, payload.return_type);
2363
2364 const fn_proto = try blk: {
2365 if (align_expr == 0 and section_expr == 0 and callconv_expr == 0) {
2366 if (params.items.len < 2)
2367 break :blk c.addNode(.{
2368 .tag = .fn_proto_simple,
2369 .main_token = fn_token,
2370 .data = .{
2371 .lhs = params.items[0],
2372 .rhs = return_type_expr,
2373 },
2374 })
2375 else
2376 break :blk c.addNode(.{
2377 .tag = .fn_proto_multi,
2378 .main_token = fn_token,
2379 .data = .{
2380 .lhs = try c.addExtra(NodeSubRange{
2381 .start = span.start,
2382 .end = span.end,
2383 }),
2384 .rhs = return_type_expr,
2385 },
2386 });
2387 }
2388 if (params.items.len < 2)
2389 break :blk c.addNode(.{
2390 .tag = .fn_proto_one,
2391 .main_token = fn_token,
2392 .data = .{
2393 .lhs = try c.addExtra(std.zig.ast.Node.FnProtoOne{
2394 .param = params.items[0],
2395 .align_expr = align_expr,
2396 .section_expr = section_expr,
2397 .callconv_expr = callconv_expr,
2398 }),
2399 .rhs = return_type_expr,
2400 },
2401 })
2402 else
2403 break :blk c.addNode(.{
2404 .tag = .fn_proto,
2405 .main_token = fn_token,
2406 .data = .{
2407 .lhs = try c.addExtra(std.zig.ast.Node.FnProto{
2408 .params_start = span.start,
2409 .params_end = span.end,
2410 .align_expr = align_expr,
2411 .section_expr = section_expr,
2412 .callconv_expr = callconv_expr,
2413 }),
2414 .rhs = return_type_expr,
2415 },
2416 });
2417 };
2418
2419 const payload_body = payload.body orelse {
2420 if (payload.is_extern) {
2421 _ = try c.addToken(.semicolon, ";");
2422 }
2423 return fn_proto;
2424 };
2425 const body = try renderNode(c, payload_body);
2426 return c.addNode(.{
2427 .tag = .fn_decl,
2428 .main_token = fn_token,
2429 .data = .{
2430 .lhs = fn_proto,
2431 .rhs = body,
2432 },
2433 });
2434}
2435
2436fn renderMacroFunc(c: *Context, node: Node) !NodeIndex {
2437 const payload = node.castTag(.pub_inline_fn).?.data;
2438 _ = try c.addToken(.keyword_pub, "pub");
2439 const fn_token = try c.addToken(.keyword_fn, "fn");
2440 _ = try c.addIdentifier(payload.name);
2441
2442 const params = try renderParams(c, payload.params, false);
2443 defer params.deinit();
2444 var span: NodeSubRange = undefined;
2445 if (params.items.len > 1) span = try c.listToSpan(params.items);
2446
2447 const callconv_expr = blk: {
2448 _ = try c.addToken(.keyword_callconv, "callconv");
2449 _ = try c.addToken(.l_paren, "(");
2450 _ = try c.addToken(.period, ".");
2451 const res = try c.addNode(.{
2452 .tag = .enum_literal,
2453 .main_token = try c.addToken(.identifier, "Inline"),
2454 .data = undefined,
2455 });
2456 _ = try c.addToken(.r_paren, ")");
2457 break :blk res;
2458 };
2459 const return_type_expr = try renderNodeGrouped(c, payload.return_type);
2460
2461 const fn_proto = try blk: {
2462 if (params.items.len < 2)
2463 break :blk c.addNode(.{
2464 .tag = .fn_proto_one,
2465 .main_token = fn_token,
2466 .data = .{
2467 .lhs = try c.addExtra(std.zig.ast.Node.FnProtoOne{
2468 .param = params.items[0],
2469 .align_expr = 0,
2470 .section_expr = 0,
2471 .callconv_expr = callconv_expr,
2472 }),
2473 .rhs = return_type_expr,
2474 },
2475 })
2476 else
2477 break :blk c.addNode(.{
2478 .tag = .fn_proto,
2479 .main_token = fn_token,
2480 .data = .{
2481 .lhs = try c.addExtra(std.zig.ast.Node.FnProto{
2482 .params_start = span.start,
2483 .params_end = span.end,
2484 .align_expr = 0,
2485 .section_expr = 0,
2486 .callconv_expr = callconv_expr,
2487 }),
2488 .rhs = return_type_expr,
2489 },
2490 });
2491 };
2492 return c.addNode(.{
2493 .tag = .fn_decl,
2494 .main_token = fn_token,
2495 .data = .{
2496 .lhs = fn_proto,
2497 .rhs = try renderNode(c, payload.body),
2498 },
2499 });
2500}
2501
2502fn renderParams(c: *Context, params: []Payload.Param, is_var_args: bool) !std.ArrayList(NodeIndex) {
2503 _ = try c.addToken(.l_paren, "(");
2504 var rendered = std.ArrayList(NodeIndex).init(c.gpa);
2505 errdefer rendered.deinit();
2506 try rendered.ensureCapacity(std.math.max(params.len, 1));
2507
2508 for (params) |param, i| {
2509 if (i != 0) _ = try c.addToken(.comma, ",");
2510 if (param.is_noalias) _ = try c.addToken(.keyword_noalias, "noalias");
2511 if (param.name) |some| {
2512 _ = try c.addIdentifier(some);
2513 _ = try c.addToken(.colon, ":");
2514 }
2515 if (param.type.tag() == .@"anytype") {
2516 _ = try c.addToken(.keyword_anytype, "anytype");
2517 continue;
2518 }
2519 rendered.appendAssumeCapacity(try renderNode(c, param.type));
2520 }
2521 if (is_var_args) {
2522 if (params.len != 0) _ = try c.addToken(.comma, ",");
2523 _ = try c.addToken(.ellipsis3, "...");
2524 }
2525 _ = try c.addToken(.r_paren, ")");
2526
2527 if (rendered.items.len == 0) rendered.appendAssumeCapacity(0);
2528 return rendered;
2529}
src/type.zig+65
......@@ -28,6 +28,8 @@ pub const Type = extern union {
2828 .i32,
2929 .u64,
3030 .i64,
31 .u128,
32 .i128,
3133 .usize,
3234 .isize,
3335 .c_short,
......@@ -357,6 +359,8 @@ pub const Type = extern union {
357359 .i32,
358360 .u64,
359361 .i64,
362 .u128,
363 .i128,
360364 .usize,
361365 .isize,
362366 .c_short,
......@@ -506,6 +510,8 @@ pub const Type = extern union {
506510 .i32,
507511 .u64,
508512 .i64,
513 .u128,
514 .i128,
509515 .usize,
510516 .isize,
511517 .c_short,
......@@ -772,6 +778,8 @@ pub const Type = extern union {
772778 .i32,
773779 .u64,
774780 .i64,
781 .u128,
782 .i128,
775783 .usize,
776784 .isize,
777785 .c_short,
......@@ -868,6 +876,7 @@ pub const Type = extern union {
868876 .i16, .u16 => return 2,
869877 .i32, .u32 => return 4,
870878 .i64, .u64 => return 8,
879 .u128, .i128 => return 16,
871880
872881 .isize,
873882 .usize,
......@@ -1010,6 +1019,7 @@ pub const Type = extern union {
10101019 .i16, .u16 => return 2,
10111020 .i32, .u32 => return 4,
10121021 .i64, .u64 => return 8,
1022 .u128, .i128 => return 16,
10131023
10141024 .@"anyframe", .anyframe_T, .isize, .usize => return @divExact(target.cpu.arch.ptrBitWidth(), 8),
10151025
......@@ -1109,6 +1119,8 @@ pub const Type = extern union {
11091119 .i32,
11101120 .u64,
11111121 .i64,
1122 .u128,
1123 .i128,
11121124 .usize,
11131125 .isize,
11141126 .c_short,
......@@ -1191,6 +1203,8 @@ pub const Type = extern union {
11911203 .i32,
11921204 .u64,
11931205 .i64,
1206 .u128,
1207 .i128,
11941208 .usize,
11951209 .isize,
11961210 .c_short,
......@@ -1278,6 +1292,8 @@ pub const Type = extern union {
12781292 .i32,
12791293 .u64,
12801294 .i64,
1295 .u128,
1296 .i128,
12811297 .usize,
12821298 .isize,
12831299 .c_short,
......@@ -1359,6 +1375,8 @@ pub const Type = extern union {
13591375 .i32,
13601376 .u64,
13611377 .i64,
1378 .u128,
1379 .i128,
13621380 .usize,
13631381 .isize,
13641382 .c_short,
......@@ -1440,6 +1458,8 @@ pub const Type = extern union {
14401458 .i32,
14411459 .u64,
14421460 .i64,
1461 .u128,
1462 .i128,
14431463 .usize,
14441464 .isize,
14451465 .c_short,
......@@ -1522,6 +1542,8 @@ pub const Type = extern union {
15221542 .i32,
15231543 .u64,
15241544 .i64,
1545 .u128,
1546 .i128,
15251547 .usize,
15261548 .isize,
15271549 .c_short,
......@@ -1660,6 +1682,8 @@ pub const Type = extern union {
16601682 .i32 => unreachable,
16611683 .u64 => unreachable,
16621684 .i64 => unreachable,
1685 .u128 => unreachable,
1686 .i128 => unreachable,
16631687 .usize => unreachable,
16641688 .isize => unreachable,
16651689 .c_short => unreachable,
......@@ -1776,6 +1800,8 @@ pub const Type = extern union {
17761800 .i32,
17771801 .u64,
17781802 .i64,
1803 .u128,
1804 .i128,
17791805 .usize,
17801806 .isize,
17811807 .c_short,
......@@ -1856,6 +1882,8 @@ pub const Type = extern union {
18561882 .i32,
18571883 .u64,
18581884 .i64,
1885 .u128,
1886 .i128,
18591887 .usize,
18601888 .isize,
18611889 .c_short,
......@@ -2009,6 +2037,8 @@ pub const Type = extern union {
20092037 .i16,
20102038 .i32,
20112039 .i64,
2040 .u128,
2041 .i128,
20122042 => true,
20132043 };
20142044 }
......@@ -2061,6 +2091,8 @@ pub const Type = extern union {
20612091 .i16,
20622092 .i32,
20632093 .i64,
2094 .u128,
2095 .i128,
20642096 .optional,
20652097 .optional_single_mut_pointer,
20662098 .optional_single_const_pointer,
......@@ -2167,6 +2199,8 @@ pub const Type = extern union {
21672199 .i32 => .{ .signedness = .signed, .bits = 32 },
21682200 .u64 => .{ .signedness = .unsigned, .bits = 64 },
21692201 .i64 => .{ .signedness = .signed, .bits = 64 },
2202 .u128 => .{ .signedness = .unsigned, .bits = 128 },
2203 .i128 => .{ .signedness = .signed, .bits = 128 },
21702204 .usize => .{ .signedness = .unsigned, .bits = target.cpu.arch.ptrBitWidth() },
21712205 .isize => .{ .signedness = .signed, .bits = target.cpu.arch.ptrBitWidth() },
21722206 .c_short => .{ .signedness = .signed, .bits = CType.short.sizeInBits(target) },
......@@ -2227,6 +2261,8 @@ pub const Type = extern union {
22272261 .i32,
22282262 .u64,
22292263 .i64,
2264 .u128,
2265 .i128,
22302266 .optional,
22312267 .optional_single_mut_pointer,
22322268 .optional_single_const_pointer,
......@@ -2333,6 +2369,8 @@ pub const Type = extern union {
23332369 .i32,
23342370 .u64,
23352371 .i64,
2372 .u128,
2373 .i128,
23362374 .usize,
23372375 .isize,
23382376 .c_short,
......@@ -2417,6 +2455,8 @@ pub const Type = extern union {
24172455 .i32,
24182456 .u64,
24192457 .i64,
2458 .u128,
2459 .i128,
24202460 .usize,
24212461 .isize,
24222462 .c_short,
......@@ -2500,6 +2540,8 @@ pub const Type = extern union {
25002540 .i32,
25012541 .u64,
25022542 .i64,
2543 .u128,
2544 .i128,
25032545 .usize,
25042546 .isize,
25052547 .c_short,
......@@ -2583,6 +2625,8 @@ pub const Type = extern union {
25832625 .i32,
25842626 .u64,
25852627 .i64,
2628 .u128,
2629 .i128,
25862630 .usize,
25872631 .isize,
25882632 .c_short,
......@@ -2663,6 +2707,8 @@ pub const Type = extern union {
26632707 .i32,
26642708 .u64,
26652709 .i64,
2710 .u128,
2711 .i128,
26662712 .usize,
26672713 .isize,
26682714 .c_short,
......@@ -2743,6 +2789,8 @@ pub const Type = extern union {
27432789 .i32,
27442790 .u64,
27452791 .i64,
2792 .u128,
2793 .i128,
27462794 .usize,
27472795 .isize,
27482796 .c_short,
......@@ -2793,6 +2841,8 @@ pub const Type = extern union {
27932841 .i32,
27942842 .u64,
27952843 .i64,
2844 .u128,
2845 .i128,
27962846 .usize,
27972847 .isize,
27982848 .c_short,
......@@ -2874,6 +2924,8 @@ pub const Type = extern union {
28742924 .i32,
28752925 .u64,
28762926 .i64,
2927 .u128,
2928 .i128,
28772929 .usize,
28782930 .isize,
28792931 .c_short,
......@@ -2971,6 +3023,8 @@ pub const Type = extern union {
29713023 .i32,
29723024 .u64,
29733025 .i64,
3026 .u128,
3027 .i128,
29743028 .usize,
29753029 .isize,
29763030 .c_short,
......@@ -3060,6 +3114,8 @@ pub const Type = extern union {
30603114 .i32,
30613115 .u64,
30623116 .i64,
3117 .u128,
3118 .i128,
30633119 .usize,
30643120 .isize,
30653121 .c_short,
......@@ -3193,6 +3249,8 @@ pub const Type = extern union {
31933249 i32,
31943250 u64,
31953251 i64,
3252 u128,
3253 i128,
31963254 usize,
31973255 isize,
31983256 c_short,
......@@ -3277,6 +3335,8 @@ pub const Type = extern union {
32773335 .i32,
32783336 .u64,
32793337 .i64,
3338 .u128,
3339 .i128,
32803340 .usize,
32813341 .isize,
32823342 .c_short,
......@@ -3352,6 +3412,11 @@ pub const Type = extern union {
33523412 };
33533413 }
33543414
3415 pub fn init(comptime t: Tag) Type {
3416 comptime std.debug.assert(@enumToInt(t) < Tag.no_payload_count);
3417 return .{ .tag_if_small_enough = @enumToInt(t) };
3418 }
3419
33553420 pub fn create(comptime t: Tag, ally: *Allocator, data: Data(t)) error{OutOfMemory}!Type {
33563421 const ptr = try ally.create(t.Type());
33573422 ptr.* = .{
src/zig_clang.cpp+5
......@@ -2244,6 +2244,11 @@ unsigned ZigClangAPSInt_getNumWords(const ZigClangAPSInt *self) {
22442244 return casted->getNumWords();
22452245}
22462246
2247bool ZigClangAPSInt_lessThanEqual(const ZigClangAPSInt *self, uint64_t rhs) {
2248 auto casted = reinterpret_cast<const llvm::APSInt *>(self);
2249 return casted->ule(rhs);
2250}
2251
22472252uint64_t ZigClangAPInt_getLimitedValue(const ZigClangAPInt *self, uint64_t limit) {
22482253 auto casted = reinterpret_cast<const llvm::APInt *>(self);
22492254 return casted->getLimitedValue(limit);
src/zig_clang.h+1
......@@ -1097,6 +1097,7 @@ ZIG_EXTERN_C const struct ZigClangAPSInt *ZigClangAPSInt_negate(const struct Zig
10971097ZIG_EXTERN_C void ZigClangAPSInt_free(const struct ZigClangAPSInt *self);
10981098ZIG_EXTERN_C const uint64_t *ZigClangAPSInt_getRawData(const struct ZigClangAPSInt *self);
10991099ZIG_EXTERN_C unsigned ZigClangAPSInt_getNumWords(const struct ZigClangAPSInt *self);
1100ZIG_EXTERN_C bool ZigClangAPSInt_lessThanEqual(const struct ZigClangAPSInt *self, uint64_t rhs);
11001101
11011102ZIG_EXTERN_C uint64_t ZigClangAPInt_getLimitedValue(const struct ZigClangAPInt *self, uint64_t limit);
11021103
src/zir.zig+41-26
......@@ -53,6 +53,9 @@ pub const Inst = struct {
5353 indexable_ptr_len,
5454 /// Function parameter value. These must be first in a function's main block,
5555 /// in respective order with the parameters.
56 /// TODO make this instruction implicit; after we transition to having ZIR
57 /// instructions be same sized and referenced by index, the first N indexes
58 /// will implicitly be references to the parameters of the function.
5659 arg,
5760 /// Type coercion.
5861 as,
......@@ -169,8 +172,10 @@ pub const Inst = struct {
169172 floatcast,
170173 /// Declare a function body.
171174 @"fn",
172 /// Returns a function type.
173 fntype,
175 /// Returns a function type, assuming unspecified calling convention.
176 fn_type,
177 /// Returns a function type, with a calling convention instruction operand.
178 fn_type_cc,
174179 /// @import(operand)
175180 import,
176181 /// Integer literal.
......@@ -340,6 +345,8 @@ pub const Inst = struct {
340345 void_value,
341346 /// A switch expression.
342347 switchbr,
348 /// Same as `switchbr` but the target is a pointer to the value being switched on.
349 switchbr_ref,
343350 /// A range in a switch case, `lhs...rhs`.
344351 /// Only checks that `lhs >= rhs` if they are ints, everything else is
345352 /// validated by the .switch instruction.
......@@ -450,6 +457,8 @@ pub const Inst = struct {
450457 .block_comptime_flat,
451458 => Block,
452459
460 .switchbr, .switchbr_ref => SwitchBr,
461
453462 .arg => Arg,
454463 .array_type_sentinel => ArrayTypeSentinel,
455464 .@"break" => Break,
......@@ -471,7 +480,8 @@ pub const Inst = struct {
471480 .@"export" => Export,
472481 .param_type => ParamType,
473482 .primitive => Primitive,
474 .fntype => FnType,
483 .fn_type => FnType,
484 .fn_type_cc => FnTypeCc,
475485 .elem_ptr, .elem_val => Elem,
476486 .condbr => CondBr,
477487 .ptr_type => PtrType,
......@@ -485,7 +495,6 @@ pub const Inst = struct {
485495 .enum_type => EnumType,
486496 .union_type => UnionType,
487497 .struct_type => StructType,
488 .switchbr => SwitchBr,
489498 };
490499 }
491500
......@@ -546,7 +555,8 @@ pub const Inst = struct {
546555 .field_ptr_named,
547556 .field_val_named,
548557 .@"fn",
549 .fntype,
558 .fn_type,
559 .fn_type_cc,
550560 .int,
551561 .intcast,
552562 .int_type,
......@@ -614,7 +624,6 @@ pub const Inst = struct {
614624 .struct_type,
615625 .void_value,
616626 .switch_range,
617 .switchbr,
618627 => false,
619628
620629 .@"break",
......@@ -629,6 +638,8 @@ pub const Inst = struct {
629638 .container_field_named,
630639 .container_field_typed,
631640 .container_field,
641 .switchbr,
642 .switchbr_ref,
632643 => true,
633644 };
634645 }
......@@ -689,6 +700,8 @@ pub const Inst = struct {
689700 base: Inst,
690701
691702 positionals: struct {
703 /// This exists to be passed to the arg TZIR instruction, which
704 /// needs it for debug info.
692705 name: []const u8,
693706 },
694707 kw_args: struct {},
......@@ -725,6 +738,8 @@ pub const Inst = struct {
725738 kw_args: struct {},
726739 };
727740
741 // TODO break this into multiple call instructions to avoid paying the cost
742 // of the calling convention field most of the time.
728743 pub const Call = struct {
729744 pub const base_tag = Tag.call;
730745 base: Inst,
......@@ -732,10 +747,9 @@ pub const Inst = struct {
732747 positionals: struct {
733748 func: *Inst,
734749 args: []*Inst,
735 },
736 kw_args: struct {
737750 modifier: std.builtin.CallOptions.Modifier = .auto,
738751 },
752 kw_args: struct {},
739753 };
740754
741755 pub const DeclRef = struct {
......@@ -849,8 +863,8 @@ pub const Inst = struct {
849863 kw_args: struct {
850864 @"volatile": bool = false,
851865 output: ?*Inst = null,
852 inputs: []*Inst = &[0]*Inst{},
853 clobbers: []*Inst = &[0]*Inst{},
866 inputs: []const []const u8 = &.{},
867 clobbers: []const []const u8 = &.{},
854868 args: []*Inst = &[0]*Inst{},
855869 },
856870 };
......@@ -867,7 +881,18 @@ pub const Inst = struct {
867881 };
868882
869883 pub const FnType = struct {
870 pub const base_tag = Tag.fntype;
884 pub const base_tag = Tag.fn_type;
885 base: Inst,
886
887 positionals: struct {
888 param_types: []*Inst,
889 return_type: *Inst,
890 },
891 kw_args: struct {},
892 };
893
894 pub const FnTypeCc = struct {
895 pub const base_tag = Tag.fn_type_cc;
871896 base: Inst,
872897
873898 positionals: struct {
......@@ -1167,20 +1192,12 @@ pub const Inst = struct {
11671192 },
11681193 kw_args: struct {
11691194 init_inst: ?*Inst = null,
1170 init_kind: InitKind = .none,
1195 has_enum_token: bool,
11711196 layout: std.builtin.TypeInfo.ContainerLayout = .Auto,
11721197 },
1173
1174 // TODO error: values of type '(enum literal)' must be comptime known
1175 pub const InitKind = enum {
1176 enum_type,
1177 tag_type,
1178 none,
1179 };
11801198 };
11811199
11821200 pub const SwitchBr = struct {
1183 pub const base_tag = Tag.switchbr;
11841201 base: Inst,
11851202
11861203 positionals: struct {
......@@ -1189,14 +1206,12 @@ pub const Inst = struct {
11891206 items: []*Inst,
11901207 cases: []Case,
11911208 else_body: Body,
1192 },
1193 kw_args: struct {
11941209 /// Pointer to first range if such exists.
11951210 range: ?*Inst = null,
11961211 special_prong: SpecialProng = .none,
11971212 },
1213 kw_args: struct {},
11981214
1199 // Not anonymous due to stage1 limitations
12001215 pub const SpecialProng = enum {
12011216 none,
12021217 @"else",
......@@ -1391,6 +1406,7 @@ const Writer = struct {
13911406 }
13921407 switch (@TypeOf(param)) {
13931408 *Inst => return self.writeInstParamToStream(stream, param),
1409 ?*Inst => return self.writeInstParamToStream(stream, param.?),
13941410 []*Inst => {
13951411 try stream.writeByte('[');
13961412 for (param) |inst, i| {
......@@ -1458,7 +1474,7 @@ const Writer = struct {
14581474 const name = self.loop_table.get(param).?;
14591475 return stream.print("\"{}\"", .{std.zig.fmtEscapes(name)});
14601476 },
1461 [][]const u8 => {
1477 [][]const u8, []const []const u8 => {
14621478 try stream.writeByte('[');
14631479 for (param) |str, i| {
14641480 if (i != 0) {
......@@ -1586,6 +1602,7 @@ const DumpTzir = struct {
15861602 .unreach,
15871603 .breakpoint,
15881604 .dbg_stmt,
1605 .arg,
15891606 => {},
15901607
15911608 .ref,
......@@ -1630,8 +1647,6 @@ const DumpTzir = struct {
16301647 try dtz.findConst(bin_op.rhs);
16311648 },
16321649
1633 .arg => {},
1634
16351650 .br => {
16361651 const br = inst.castTag(.br).?;
16371652 try dtz.findConst(&br.block.base);
src/zir_sema.zig+74-30
......@@ -91,7 +91,8 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
9191 .@"fn" => return zirFn(mod, scope, old_inst.castTag(.@"fn").?),
9292 .@"export" => return zirExport(mod, scope, old_inst.castTag(.@"export").?),
9393 .primitive => return zirPrimitive(mod, scope, old_inst.castTag(.primitive).?),
94 .fntype => return zirFnType(mod, scope, old_inst.castTag(.fntype).?),
94 .fn_type => return zirFnType(mod, scope, old_inst.castTag(.fn_type).?),
95 .fn_type_cc => return zirFnTypeCc(mod, scope, old_inst.castTag(.fn_type_cc).?),
9596 .intcast => return zirIntcast(mod, scope, old_inst.castTag(.intcast).?),
9697 .bitcast => return zirBitcast(mod, scope, old_inst.castTag(.bitcast).?),
9798 .floatcast => return zirFloatcast(mod, scope, old_inst.castTag(.floatcast).?),
......@@ -154,7 +155,8 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
154155 .bool_and => return zirBoolOp(mod, scope, old_inst.castTag(.bool_and).?),
155156 .bool_or => return zirBoolOp(mod, scope, old_inst.castTag(.bool_or).?),
156157 .void_value => return mod.constVoid(scope, old_inst.src),
157 .switchbr => return zirSwitchBr(mod, scope, old_inst.castTag(.switchbr).?),
158 .switchbr => return zirSwitchBr(mod, scope, old_inst.castTag(.switchbr).?, false),
159 .switchbr_ref => return zirSwitchBr(mod, scope, old_inst.castTag(.switchbr_ref).?, true),
158160 .switch_range => return zirSwitchRange(mod, scope, old_inst.castTag(.switch_range).?),
159161
160162 .container_field_named,
......@@ -957,11 +959,11 @@ fn zirCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst {
957959 );
958960 }
959961
960 if (inst.kw_args.modifier == .compile_time) {
962 if (inst.positionals.modifier == .compile_time) {
961963 return mod.fail(scope, inst.base.src, "TODO implement comptime function calls", .{});
962964 }
963 if (inst.kw_args.modifier != .auto) {
964 return mod.fail(scope, inst.base.src, "TODO implement call with modifier {}", .{inst.kw_args.modifier});
965 if (inst.positionals.modifier != .auto) {
966 return mod.fail(scope, inst.base.src, "TODO implement call with modifier {}", .{inst.positionals.modifier});
965967 }
966968
967969 // TODO handle function calls of generic functions
......@@ -979,8 +981,8 @@ fn zirCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst {
979981 const ret_type = func.ty.fnReturnType();
980982
981983 const b = try mod.requireFunctionBlock(scope, inst.base.src);
982 const is_comptime_call = b.is_comptime or inst.kw_args.modifier == .compile_time;
983 const is_inline_call = is_comptime_call or inst.kw_args.modifier == .always_inline or
984 const is_comptime_call = b.is_comptime or inst.positionals.modifier == .compile_time;
985 const is_inline_call = is_comptime_call or inst.positionals.modifier == .always_inline or
984986 func.ty.fnCallingConvention() == .Inline;
985987 if (is_inline_call) {
986988 const func_val = try mod.resolveConstValue(scope, func);
......@@ -1294,34 +1296,69 @@ fn zirEnsureErrPayloadVoid(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp)
12941296fn zirFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst {
12951297 const tracy = trace(@src());
12961298 defer tracy.end();
1297 const return_type = try resolveType(mod, scope, fntype.positionals.return_type);
1299
1300 return fnTypeCommon(
1301 mod,
1302 scope,
1303 &fntype.base,
1304 fntype.positionals.param_types,
1305 fntype.positionals.return_type,
1306 .Unspecified,
1307 );
1308}
1309
1310fn zirFnTypeCc(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnTypeCc) InnerError!*Inst {
1311 const tracy = trace(@src());
1312 defer tracy.end();
1313
12981314 const cc_tv = try resolveInstConst(mod, scope, fntype.positionals.cc);
1315 // TODO once we're capable of importing and analyzing decls from
1316 // std.builtin, this needs to change
12991317 const cc_str = cc_tv.val.castTag(.enum_literal).?.data;
13001318 const cc = std.meta.stringToEnum(std.builtin.CallingConvention, cc_str) orelse
13011319 return mod.fail(scope, fntype.positionals.cc.src, "Unknown calling convention {s}", .{cc_str});
1320 return fnTypeCommon(
1321 mod,
1322 scope,
1323 &fntype.base,
1324 fntype.positionals.param_types,
1325 fntype.positionals.return_type,
1326 cc,
1327 );
1328}
1329
1330fn fnTypeCommon(
1331 mod: *Module,
1332 scope: *Scope,
1333 zir_inst: *zir.Inst,
1334 zir_param_types: []*zir.Inst,
1335 zir_return_type: *zir.Inst,
1336 cc: std.builtin.CallingConvention,
1337) InnerError!*Inst {
1338 const return_type = try resolveType(mod, scope, zir_return_type);
13021339
13031340 // Hot path for some common function types.
1304 if (fntype.positionals.param_types.len == 0) {
1341 if (zir_param_types.len == 0) {
13051342 if (return_type.zigTypeTag() == .NoReturn and cc == .Unspecified) {
1306 return mod.constType(scope, fntype.base.src, Type.initTag(.fn_noreturn_no_args));
1343 return mod.constType(scope, zir_inst.src, Type.initTag(.fn_noreturn_no_args));
13071344 }
13081345
13091346 if (return_type.zigTypeTag() == .Void and cc == .Unspecified) {
1310 return mod.constType(scope, fntype.base.src, Type.initTag(.fn_void_no_args));
1347 return mod.constType(scope, zir_inst.src, Type.initTag(.fn_void_no_args));
13111348 }
13121349
13131350 if (return_type.zigTypeTag() == .NoReturn and cc == .Naked) {
1314 return mod.constType(scope, fntype.base.src, Type.initTag(.fn_naked_noreturn_no_args));
1351 return mod.constType(scope, zir_inst.src, Type.initTag(.fn_naked_noreturn_no_args));
13151352 }
13161353
13171354 if (return_type.zigTypeTag() == .Void and cc == .C) {
1318 return mod.constType(scope, fntype.base.src, Type.initTag(.fn_ccc_void_no_args));
1355 return mod.constType(scope, zir_inst.src, Type.initTag(.fn_ccc_void_no_args));
13191356 }
13201357 }
13211358
13221359 const arena = scope.arena();
1323 const param_types = try arena.alloc(Type, fntype.positionals.param_types.len);
1324 for (fntype.positionals.param_types) |param_type, i| {
1360 const param_types = try arena.alloc(Type, zir_param_types.len);
1361 for (zir_param_types) |param_type, i| {
13251362 const resolved = try resolveType(mod, scope, param_type);
13261363 // TODO skip for comptime params
13271364 if (!resolved.isValidVarType(false)) {
......@@ -1335,7 +1372,7 @@ fn zirFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*
13351372 .return_type = return_type,
13361373 .cc = cc,
13371374 });
1338 return mod.constType(scope, fntype.base.src, fn_ty);
1375 return mod.constType(scope, zir_inst.src, fn_ty);
13391376}
13401377
13411378fn zirPrimitive(mod: *Module, scope: *Scope, primitive: *zir.Inst.Primitive) InnerError!*Inst {
......@@ -1554,10 +1591,15 @@ fn zirSwitchRange(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError
15541591 return mod.constVoid(scope, inst.base.src);
15551592}
15561593
1557fn zirSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) InnerError!*Inst {
1594fn zirSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr, ref: bool) InnerError!*Inst {
15581595 const tracy = trace(@src());
15591596 defer tracy.end();
1560 const target = try resolveInst(mod, scope, inst.positionals.target);
1597
1598 const target_ptr = try resolveInst(mod, scope, inst.positionals.target);
1599 const target = if (ref)
1600 try mod.analyzeDeref(scope, inst.base.src, target_ptr, inst.positionals.target.src)
1601 else
1602 target_ptr;
15611603 try validateSwitch(mod, scope, target, inst);
15621604
15631605 if (try mod.resolveDefinedValue(scope, target)) |target_val| {
......@@ -1626,13 +1668,13 @@ fn zirSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) InnerError
16261668
16271669fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.SwitchBr) InnerError!void {
16281670 // validate usage of '_' prongs
1629 if (inst.kw_args.special_prong == .underscore and target.ty.zigTypeTag() != .Enum) {
1671 if (inst.positionals.special_prong == .underscore and target.ty.zigTypeTag() != .Enum) {
16301672 return mod.fail(scope, inst.base.src, "'_' prong only allowed when switching on non-exhaustive enums", .{});
16311673 // TODO notes "'_' prong here" inst.positionals.cases[last].src
16321674 }
16331675
16341676 // check that target type supports ranges
1635 if (inst.kw_args.range) |range_inst| {
1677 if (inst.positionals.range) |range_inst| {
16361678 switch (target.ty.zigTypeTag()) {
16371679 .Int, .ComptimeInt => {},
16381680 else => {
......@@ -1683,14 +1725,14 @@ fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.Sw
16831725 const start = try target.ty.minInt(&arena, mod.getTarget());
16841726 const end = try target.ty.maxInt(&arena, mod.getTarget());
16851727 if (try range_set.spans(start, end)) {
1686 if (inst.kw_args.special_prong == .@"else") {
1728 if (inst.positionals.special_prong == .@"else") {
16871729 return mod.fail(scope, inst.base.src, "unreachable else prong, all cases already handled", .{});
16881730 }
16891731 return;
16901732 }
16911733 }
16921734
1693 if (inst.kw_args.special_prong != .@"else") {
1735 if (inst.positionals.special_prong != .@"else") {
16941736 return mod.fail(scope, inst.base.src, "switch must handle all possibilities", .{});
16951737 }
16961738 },
......@@ -1710,15 +1752,15 @@ fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.Sw
17101752 return mod.fail(scope, item.src, "duplicate switch value", .{});
17111753 }
17121754 }
1713 if ((true_count + false_count < 2) and inst.kw_args.special_prong != .@"else") {
1755 if ((true_count + false_count < 2) and inst.positionals.special_prong != .@"else") {
17141756 return mod.fail(scope, inst.base.src, "switch must handle all possibilities", .{});
17151757 }
1716 if ((true_count + false_count == 2) and inst.kw_args.special_prong == .@"else") {
1758 if ((true_count + false_count == 2) and inst.positionals.special_prong == .@"else") {
17171759 return mod.fail(scope, inst.base.src, "unreachable else prong, all cases already handled", .{});
17181760 }
17191761 },
17201762 .EnumLiteral, .Void, .Fn, .Pointer, .Type => {
1721 if (inst.kw_args.special_prong != .@"else") {
1763 if (inst.positionals.special_prong != .@"else") {
17221764 return mod.fail(scope, inst.base.src, "else prong required when switching on type '{}'", .{target.ty});
17231765 }
17241766
......@@ -1981,19 +2023,21 @@ fn zirDeref(mod: *Module, scope: *Scope, deref: *zir.Inst.UnOp) InnerError!*Inst
19812023fn zirAsm(mod: *Module, scope: *Scope, assembly: *zir.Inst.Asm) InnerError!*Inst {
19822024 const tracy = trace(@src());
19832025 defer tracy.end();
2026
19842027 const return_type = try resolveType(mod, scope, assembly.positionals.return_type);
19852028 const asm_source = try resolveConstString(mod, scope, assembly.positionals.asm_source);
19862029 const output = if (assembly.kw_args.output) |o| try resolveConstString(mod, scope, o) else null;
19872030
1988 const inputs = try scope.arena().alloc([]const u8, assembly.kw_args.inputs.len);
1989 const clobbers = try scope.arena().alloc([]const u8, assembly.kw_args.clobbers.len);
1990 const args = try scope.arena().alloc(*Inst, assembly.kw_args.args.len);
2031 const arena = scope.arena();
2032 const inputs = try arena.alloc([]const u8, assembly.kw_args.inputs.len);
2033 const clobbers = try arena.alloc([]const u8, assembly.kw_args.clobbers.len);
2034 const args = try arena.alloc(*Inst, assembly.kw_args.args.len);
19912035
19922036 for (inputs) |*elem, i| {
1993 elem.* = try resolveConstString(mod, scope, assembly.kw_args.inputs[i]);
2037 elem.* = try arena.dupe(u8, assembly.kw_args.inputs[i]);
19942038 }
19952039 for (clobbers) |*elem, i| {
1996 elem.* = try resolveConstString(mod, scope, assembly.kw_args.clobbers[i]);
2040 elem.* = try arena.dupe(u8, assembly.kw_args.clobbers[i]);
19972041 }
19982042 for (args) |*elem, i| {
19992043 const arg = try resolveInst(mod, scope, assembly.kw_args.args[i]);
test/run_translated_c.zig+36
......@@ -3,6 +3,33 @@ const tests = @import("tests.zig");
33const nl = std.cstr.line_sep;
44
55pub fn addCases(cases: *tests.RunTranslatedCContext) void {
6 cases.add("use global scope for record/enum/typedef type transalation if needed",
7 \\void bar(void);
8 \\void baz(void);
9 \\struct foo { int x; };
10 \\void bar() {
11 \\ struct foo tmp;
12 \\}
13 \\
14 \\void baz() {
15 \\ struct foo tmp;
16 \\}
17 \\
18 \\int main(void) {
19 \\ bar();
20 \\ baz();
21 \\ return 0;
22 \\}
23 , "");
24
25 cases.add("failed macros are only declared once",
26 \\#define FOO =
27 \\#define FOO =
28 \\#define PtrToPtr64(p) ((void *POINTER_64) p)
29 \\#define STRUC_ALIGNED_STACK_COPY(t,s) ((CONST t *)(s))
30 \\int main(void) {}
31 , "");
32
633 cases.add("parenthesized string literal",
734 \\void foo(const char *s) {}
835 \\int main(void) {
......@@ -922,4 +949,13 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
922949 \\ return 0;
923950 \\}
924951 , "");
952
953 cases.add("Use correct break label for statement expression in nested scope",
954 \\#include <stdlib.h>
955 \\int main(void) {
956 \\ int x = ({1, ({2; 3;});});
957 \\ if (x != 3) abort();
958 \\ return 0;
959 \\}
960 , "");
925961}
test/stage2/test.zig+8-4
......@@ -1088,7 +1088,7 @@ pub fn addCases(ctx: *TestContext) !void {
10881088 \\ _ = foo;
10891089 \\}
10901090 \\extern var foo;
1091 , &[_][]const u8{":4:1: error: unable to infer variable type"});
1091 , &[_][]const u8{":4:8: error: unable to infer variable type"});
10921092 }
10931093
10941094 {
......@@ -1194,12 +1194,12 @@ pub fn addCases(ctx: *TestContext) !void {
11941194 \\comptime {
11951195 \\ foo: while (true) {}
11961196 \\}
1197 , &[_][]const u8{":2:5: error: unused while label"});
1197 , &[_][]const u8{":2:5: error: unused while loop label"});
11981198 case.addError(
11991199 \\comptime {
12001200 \\ foo: for ("foo") |_| {}
12011201 \\}
1202 , &[_][]const u8{":2:5: error: unused for label"});
1202 , &[_][]const u8{":2:5: error: unused for loop label"});
12031203 case.addError(
12041204 \\comptime {
12051205 \\ blk: {blk: {}}
......@@ -1294,6 +1294,10 @@ pub fn addCases(ctx: *TestContext) !void {
12941294 ,
12951295 "",
12961296 );
1297 // TODO this should be :8:21 not :8:19. we need to improve source locations
1298 // to be relative to the containing Decl so that they can survive when the byte
1299 // offset of a previous Decl changes. Here the change from 7 to 999 introduces
1300 // +2 to the byte offset and makes the error location wrong by 2 bytes.
12971301 case.addError(
12981302 \\export fn _start() noreturn {
12991303 \\ const y = fibonacci(999);
......@@ -1314,7 +1318,7 @@ pub fn addCases(ctx: *TestContext) !void {
13141318 \\ );
13151319 \\ unreachable;
13161320 \\}
1317 , &[_][]const u8{":8:10: error: evaluation exceeded 1000 backwards branches"});
1321 , &[_][]const u8{":8:19: error: evaluation exceeded 1000 backwards branches"});
13181322 }
13191323 {
13201324 var case = ctx.exe("orelse at comptime", linux_x64);
test/translate_c.zig+478-268
......@@ -3,12 +3,208 @@ const std = @import("std");
33const CrossTarget = std.zig.CrossTarget;
44
55pub fn addCases(cases: *tests.TranslateCContext) void {
6 cases.add("variadic function demoted to prototype",
6 cases.add("if as while stmt has semicolon",
7 \\void foo() {
8 \\ while (1) if (1) {
9 \\ int a = 1;
10 \\ } else {
11 \\ int b = 2;
12 \\ }
13 \\ if (1) if (1) {}
14 \\}
15 , &[_][]const u8{
16 \\pub export fn foo() void {
17 \\ while (true) if (true) {
18 \\ var a: c_int = 1;
19 \\ } else {
20 \\ var b: c_int = 2;
21 \\ };
22 \\ if (true) if (true) {};
23 \\}
24 });
25
26 cases.add("conditional operator cast to void",
27 \\int bar();
28 \\void foo() {
29 \\ int a;
30 \\ a ? a = 2 : bar();
31 \\}
32 , &[_][]const u8{
33 \\pub extern fn bar(...) c_int;
34 \\pub export fn foo() void {
35 \\ var a: c_int = undefined;
36 \\ if (a != 0) a = 2 else _ = bar();
37 \\}
38 });
39
40 cases.add("struct in struct init to zero",
41 \\struct Foo {
42 \\ int a;
43 \\ struct Bar {
44 \\ int a;
45 \\ } b;
46 \\} a = {};
47 \\#define PTR void *
48 , &[_][]const u8{
49 \\pub const struct_Bar = extern struct {
50 \\ a: c_int,
51 \\};
52 \\pub const struct_Foo = extern struct {
53 \\ a: c_int,
54 \\ b: struct_Bar,
55 \\};
56 \\pub export var a: struct_Foo = struct_Foo{
57 \\ .a = 0,
58 \\ .b = @import("std").mem.zeroes(struct_Bar),
59 \\};
60 ,
61 \\pub const PTR = ?*c_void;
62 });
63
64 cases.add("scoped enum",
65 \\void foo() {
66 \\ enum Foo {
67 \\ A,
68 \\ B,
69 \\ C,
70 \\ };
71 \\ enum Foo a = B;
72 \\ {
73 \\ enum Foo {
74 \\ A,
75 \\ B,
76 \\ C,
77 \\ };
78 \\ enum Foo a = B;
79 \\ }
80 \\}
81 , &[_][]const u8{
82 \\pub export fn foo() void {
83 \\ const enum_Foo = extern enum(c_int) {
84 \\ A,
85 \\ B,
86 \\ C,
87 \\ _,
88 \\ };
89 \\ const A = @enumToInt(enum_Foo.A);
90 \\ const B = @enumToInt(enum_Foo.B);
91 \\ const C = @enumToInt(enum_Foo.C);
92 \\ var a: enum_Foo = @intToEnum(enum_Foo, B);
93 \\ {
94 \\ const enum_Foo = extern enum(c_int) {
95 \\ A,
96 \\ B,
97 \\ C,
98 \\ _,
99 \\ };
100 \\ const A_2 = @enumToInt(enum_Foo.A);
101 \\ const B_3 = @enumToInt(enum_Foo.B);
102 \\ const C_4 = @enumToInt(enum_Foo.C);
103 \\ var a_5: enum_Foo = @intToEnum(enum_Foo, B_3);
104 \\ }
105 \\}
106 });
107
108 cases.add("scoped record",
109 \\void foo() {
110 \\ struct Foo {
111 \\ int A;
112 \\ int B;
113 \\ int C;
114 \\ };
115 \\ struct Foo a = {0};
116 \\ {
117 \\ struct Foo {
118 \\ int A;
119 \\ int B;
120 \\ int C;
121 \\ };
122 \\ struct Foo a = {0};
123 \\ }
124 \\}
125 , &[_][]const u8{
126 \\pub export fn foo() void {
127 \\ const struct_Foo = extern struct {
128 \\ A: c_int,
129 \\ B: c_int,
130 \\ C: c_int,
131 \\ };
132 \\ var a: struct_Foo = struct_Foo{
133 \\ .A = @as(c_int, 0),
134 \\ .B = 0,
135 \\ .C = 0,
136 \\ };
137 \\ {
138 \\ const struct_Foo_1 = extern struct {
139 \\ A: c_int,
140 \\ B: c_int,
141 \\ C: c_int,
142 \\ };
143 \\ var a_2: struct_Foo_1 = struct_Foo_1{
144 \\ .A = @as(c_int, 0),
145 \\ .B = 0,
146 \\ .C = 0,
147 \\ };
148 \\ }
149 \\}
150 });
151
152 cases.add("scoped typedef",
153 \\void foo() {
154 \\ typedef union {
155 \\ int A;
156 \\ int B;
157 \\ int C;
158 \\ } Foo;
159 \\ Foo a = {0};
160 \\ {
161 \\ typedef union {
162 \\ int A;
163 \\ int B;
164 \\ int C;
165 \\ } Foo;
166 \\ Foo a = {0};
167 \\ }
168 \\}
169 , &[_][]const u8{
170 \\pub export fn foo() void {
171 \\ const union_unnamed_1 = extern union {
172 \\ A: c_int,
173 \\ B: c_int,
174 \\ C: c_int,
175 \\ };
176 \\ const Foo = union_unnamed_1;
177 \\ var a: Foo = Foo{
178 \\ .A = @as(c_int, 0),
179 \\ };
180 \\ {
181 \\ const union_unnamed_2 = extern union {
182 \\ A: c_int,
183 \\ B: c_int,
184 \\ C: c_int,
185 \\ };
186 \\ const Foo_1 = union_unnamed_2;
187 \\ var a_2: Foo_1 = Foo_1{
188 \\ .A = @as(c_int, 0),
189 \\ };
190 \\ }
191 \\}
192 });
193
194 cases.add("use cast param as macro fn return type",
195 \\#define MEM_PHYSICAL_TO_K0(x) (void*)((u32)(x) + SYS_BASE_CACHED)
196 , &[_][]const u8{
197 \\pub fn MEM_PHYSICAL_TO_K0(x: anytype) callconv(.Inline) ?*c_void {
198 \\ return @import("std").meta.cast(?*c_void, @import("std").meta.cast(u32, x) + SYS_BASE_CACHED);
199 \\}
200 });
201
202 cases.add("variadic function demoted to extern",
7203 \\int foo(int bar, ...) {
8204 \\ return 1;
9205 \\}
10206 , &[_][]const u8{
11 \\warning: TODO unable to translate variadic function, demoted to declaration
207 \\warning: TODO unable to translate variadic function, demoted to extern
12208 \\pub extern fn foo(bar: c_int, ...) c_int;
13209 });
14210
......@@ -21,11 +217,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
21217 \\ Foo *bar;
22218 \\} Bar;
23219 , &[_][]const u8{
24 \\const struct_unnamed_1 = //
25 ,
26 \\warning: unsupported type: 'Atomic'
27 \\ opaque {}; //
28 ,
220 \\source.h:1:9: warning: struct demoted to opaque type - unable to translate type of field foo
221 \\const struct_unnamed_1 = opaque {};
29222 \\pub const Foo = struct_unnamed_1;
30223 \\const struct_unnamed_2 = extern struct {
31224 \\ bar: ?*Foo,
......@@ -43,8 +236,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
43236 ,
44237 \\pub const VALUE = ((((1 + (2 * 3)) + (4 * 5)) + 6) << 7) | @boolToInt(8 == 9);
45238 ,
46 \\pub fn _AL_READ3BYTES(p: anytype) callconv(.Inline) @TypeOf(((@import("std").meta.cast([*c]u8, p)).* | (((@import("std").meta.cast([*c]u8, p)) + 1).* << 8)) | (((@import("std").meta.cast([*c]u8, p)) + 2).* << 16)) {
47 \\ return ((@import("std").meta.cast([*c]u8, p)).* | (((@import("std").meta.cast([*c]u8, p)) + 1).* << 8)) | (((@import("std").meta.cast([*c]u8, p)) + 2).* << 16);
239 \\pub fn _AL_READ3BYTES(p: anytype) callconv(.Inline) @TypeOf((@import("std").meta.cast([*c]u8, p).* | ((@import("std").meta.cast([*c]u8, p) + 1).* << 8)) | ((@import("std").meta.cast([*c]u8, p) + 2).* << 16)) {
240 \\ return (@import("std").meta.cast([*c]u8, p).* | ((@import("std").meta.cast([*c]u8, p) + 1).* << 8)) | ((@import("std").meta.cast([*c]u8, p) + 2).* << 16);
48241 \\}
49242 });
50243
......@@ -57,7 +250,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
57250 \\pub export var bar: f32 = @import("std").mem.zeroes(f32);
58251 \\threadlocal var bar_1: c_int = 2;
59252 \\pub export fn foo() c_int {
60 \\ _ = bar_1;
61253 \\ return 0;
62254 \\}
63255 });
......@@ -107,7 +299,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
107299 \\ int i1;
108300 \\} boom_t;
109301 \\#define FOO ((boom_t){1})
110 , &[_][]const u8{ // TODO properly translate this
302 , &[_][]const u8{
111303 \\pub const struct_Color = extern struct {
112304 \\ r: u8,
113305 \\ g: u8,
......@@ -127,7 +319,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
127319 \\};
128320 \\pub const boom_t = struct_boom_t;
129321 ,
130 \\pub const FOO = @import("std").mem.zeroInit(boom_t, .{ 1 });
322 \\pub const FOO = @import("std").mem.zeroInit(boom_t, .{1});
131323 });
132324
133325 cases.add("complex switch",
......@@ -142,14 +334,17 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
142334 \\ }
143335 \\}
144336 , &[_][]const u8{ // TODO properly translate this
145 \\pub const main = @compileError("unable to translate function");
337 \\source.h:5:13: warning: TODO complex switch
338 ,
339 \\source.h:1:5: warning: unable to translate function, demoted to extern
340 \\pub extern fn main() c_int;
146341 });
147342
148343 cases.add("correct semicolon after infixop",
149344 \\#define __ferror_unlocked_body(_fp) (((_fp)->_flags & _IO_ERR_SEEN) != 0)
150345 , &[_][]const u8{
151 \\pub fn __ferror_unlocked_body(_fp: anytype) callconv(.Inline) @TypeOf(((_fp.*._flags) & _IO_ERR_SEEN) != 0) {
152 \\ return ((_fp.*._flags) & _IO_ERR_SEEN) != 0;
346 \\pub fn __ferror_unlocked_body(_fp: anytype) callconv(.Inline) @TypeOf((_fp.*._flags & _IO_ERR_SEEN) != 0) {
347 \\ return (_fp.*._flags & _IO_ERR_SEEN) != 0;
153348 \\}
154349 });
155350
......@@ -193,9 +388,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
193388 \\pub export fn foo() void {
194389 \\ while (false) while (false) {};
195390 \\ while (true) while (false) {};
196 \\ while (true) while (true) {
197 \\ if (!false) break;
198 \\ };
391 \\ while (true) {}
199392 \\}
200393 });
201394
......@@ -245,15 +438,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
245438 \\ volatile _Atomic int abufused[12];
246439 \\};
247440 , &[_][]const u8{
248 \\pub const struct_arcan_shmif_page = //
249 ,
250 \\warning: unsupported type: 'Atomic'
251 \\ opaque {}; //
252 ,
253 \\ warning: struct demoted to opaque type - unable to translate type of field abufused
254 , // TODO should be `addr: *struct_arcan_shmif_page`
441 \\source.h:4:8: warning: struct demoted to opaque type - unable to translate type of field abufused
442 \\pub const struct_arcan_shmif_page = opaque {};
255443 \\pub const struct_arcan_shmif_cont = extern struct {
256 \\ addr: [*c]struct_arcan_shmif_page,
444 \\ addr: ?*struct_arcan_shmif_page,
257445 \\};
258446 });
259447
......@@ -293,22 +481,22 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
293481 , &[_][]const u8{
294482 \\pub const uuid_t = [16]u8;
295483 \\pub const UUID_NULL: uuid_t = [16]u8{
296 \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))),
297 \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))),
298 \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))),
299 \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))),
300 \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))),
301 \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))),
302 \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))),
303 \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))),
304 \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))),
305 \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))),
306 \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))),
307 \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))),
308 \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))),
309 \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))),
310 \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))),
311 \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))),
484 \\ 0,
485 \\ 0,
486 \\ 0,
487 \\ 0,
488 \\ 0,
489 \\ 0,
490 \\ 0,
491 \\ 0,
492 \\ 0,
493 \\ 0,
494 \\ 0,
495 \\ 0,
496 \\ 0,
497 \\ 0,
498 \\ 0,
499 \\ 0,
312500 \\};
313501 });
314502
......@@ -362,10 +550,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
362550 \\};
363551 \\pub export var ub: union_unnamed_1 = union_unnamed_1{
364552 \\ .c = [4]u8{
365 \\ @bitCast(u8, @truncate(i8, @as(c_int, 'a'))),
366 \\ @bitCast(u8, @truncate(i8, @as(c_int, 'b'))),
367 \\ @bitCast(u8, @truncate(i8, @as(c_int, 'b'))),
368 \\ @bitCast(u8, @truncate(i8, @as(c_int, 'a'))),
553 \\ 'a',
554 \\ 'b',
555 \\ 'b',
556 \\ 'a',
369557 \\ },
370558 \\};
371559 });
......@@ -492,7 +680,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
492680 , &[_][]const u8{
493681 \\pub export fn foo() void {
494682 \\ var a: c_int = undefined;
495 \\ var b: u8 = @bitCast(u8, @truncate(i8, @as(c_int, 123)));
683 \\ var b: u8 = 123;
496684 \\ const c: c_int = undefined;
497685 \\ const d: c_uint = @bitCast(c_uint, @as(c_int, 440));
498686 \\ var e: c_int = 10;
......@@ -514,8 +702,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
514702 \\ var a: c_int = undefined;
515703 \\ _ = @as(c_int, 1);
516704 \\ _ = "hey";
517 \\ _ = (@as(c_int, 1) + @as(c_int, 1));
518 \\ _ = (@as(c_int, 1) - @as(c_int, 1));
705 \\ _ = @as(c_int, 1) + @as(c_int, 1);
706 \\ _ = @as(c_int, 1) - @as(c_int, 1);
519707 \\ a = 1;
520708 \\}
521709 });
......@@ -559,9 +747,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
559747 \\ '2',
560748 \\ 0,
561749 \\};
562 \\pub export fn foo() void {
563 \\ _ = v2;
564 \\}
750 \\pub export fn foo() void {}
565751 });
566752
567753 cases.add("simple function definition",
......@@ -634,9 +820,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
634820 \\ var a: c_int = undefined;
635821 \\ var b: c_int = undefined;
636822 \\ var c: c_int = undefined;
637 \\ c = (a + b);
638 \\ c = (a - b);
639 \\ c = (a * b);
823 \\ c = a + b;
824 \\ c = a - b;
825 \\ c = a * b;
640826 \\ c = @divTrunc(a, b);
641827 \\ c = @rem(a, b);
642828 \\ return 0;
......@@ -645,11 +831,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
645831 \\ var a: c_uint = undefined;
646832 \\ var b: c_uint = undefined;
647833 \\ var c: c_uint = undefined;
648 \\ c = (a +% b);
649 \\ c = (a -% b);
650 \\ c = (a *% b);
651 \\ c = (a / b);
652 \\ c = (a % b);
834 \\ c = a +% b;
835 \\ c = a -% b;
836 \\ c = a *% b;
837 \\ c = a / b;
838 \\ c = a % b;
653839 \\ return 0;
654840 \\}
655841 });
......@@ -914,13 +1100,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
9141100 \\ ;;;;;
9151101 \\}
9161102 , &[_][]const u8{
917 \\pub export fn foo() void {
918 \\ {}
919 \\ {}
920 \\ {}
921 \\ {}
922 \\ {}
923 \\}
1103 \\pub export fn foo() void {}
9241104 });
9251105
9261106 if (std.Target.current.os.tag != .windows) {
......@@ -1335,11 +1515,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
13351515 \\extern enum enum_ty my_enum;
13361516 \\enum enum_ty { FOO };
13371517 , &[_][]const u8{
1338 \\pub const FOO = @enumToInt(enum_enum_ty.FOO);
13391518 \\pub const enum_enum_ty = extern enum(c_int) {
13401519 \\ FOO,
13411520 \\ _,
13421521 \\};
1522 \\pub const FOO = @enumToInt(enum_enum_ty.FOO);
13431523 \\pub extern var my_enum: enum_enum_ty;
13441524 });
13451525
......@@ -1448,7 +1628,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
14481628 , &[_][]const u8{
14491629 \\pub fn foo() callconv(.C) void {
14501630 \\ var arr: [10]u8 = [1]u8{
1451 \\ @bitCast(u8, @truncate(i8, @as(c_int, 1))),
1631 \\ 1,
14521632 \\ } ++ [1]u8{0} ** 9;
14531633 \\ var arr1: [10][*c]u8 = [1][*c]u8{
14541634 \\ null,
......@@ -1481,48 +1661,48 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
14811661 \\ p,
14821662 \\};
14831663 , &[_][]const u8{
1484 \\pub const a = @enumToInt(enum_unnamed_1.a);
1485 \\pub const b = @enumToInt(enum_unnamed_1.b);
1486 \\pub const c = @enumToInt(enum_unnamed_1.c);
14871664 \\const enum_unnamed_1 = extern enum(c_int) {
14881665 \\ a,
14891666 \\ b,
14901667 \\ c,
14911668 \\ _,
14921669 \\};
1670 \\pub const a = @enumToInt(enum_unnamed_1.a);
1671 \\pub const b = @enumToInt(enum_unnamed_1.b);
1672 \\pub const c = @enumToInt(enum_unnamed_1.c);
14931673 \\pub const d = enum_unnamed_1;
1494 \\pub const e = @enumToInt(enum_unnamed_2.e);
1495 \\pub const f = @enumToInt(enum_unnamed_2.f);
1496 \\pub const g = @enumToInt(enum_unnamed_2.g);
14971674 \\const enum_unnamed_2 = extern enum(c_int) {
14981675 \\ e = 0,
14991676 \\ f = 4,
15001677 \\ g = 5,
15011678 \\ _,
15021679 \\};
1680 \\pub const e = @enumToInt(enum_unnamed_2.e);
1681 \\pub const f = @enumToInt(enum_unnamed_2.f);
1682 \\pub const g = @enumToInt(enum_unnamed_2.g);
15031683 \\pub export var h: enum_unnamed_2 = @intToEnum(enum_unnamed_2, e);
1504 \\pub const i = @enumToInt(enum_unnamed_3.i);
1505 \\pub const j = @enumToInt(enum_unnamed_3.j);
1506 \\pub const k = @enumToInt(enum_unnamed_3.k);
15071684 \\const enum_unnamed_3 = extern enum(c_int) {
15081685 \\ i,
15091686 \\ j,
15101687 \\ k,
15111688 \\ _,
15121689 \\};
1690 \\pub const i = @enumToInt(enum_unnamed_3.i);
1691 \\pub const j = @enumToInt(enum_unnamed_3.j);
1692 \\pub const k = @enumToInt(enum_unnamed_3.k);
15131693 \\pub const struct_Baz = extern struct {
15141694 \\ l: enum_unnamed_3,
15151695 \\ m: d,
15161696 \\};
1517 \\pub const n = @enumToInt(enum_i.n);
1518 \\pub const o = @enumToInt(enum_i.o);
1519 \\pub const p = @enumToInt(enum_i.p);
15201697 \\pub const enum_i = extern enum(c_int) {
15211698 \\ n,
15221699 \\ o,
15231700 \\ p,
15241701 \\ _,
15251702 \\};
1703 \\pub const n = @enumToInt(enum_i.n);
1704 \\pub const o = @enumToInt(enum_i.o);
1705 \\pub const p = @enumToInt(enum_i.p);
15261706 ,
15271707 \\pub const Baz = struct_Baz;
15281708 });
......@@ -1639,7 +1819,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
16391819 cases.add("macro pointer cast",
16401820 \\#define NRF_GPIO ((NRF_GPIO_Type *) NRF_GPIO_BASE)
16411821 , &[_][]const u8{
1642 \\pub const NRF_GPIO = (@import("std").meta.cast([*c]NRF_GPIO_Type, NRF_GPIO_BASE));
1822 \\pub const NRF_GPIO = @import("std").meta.cast([*c]NRF_GPIO_Type, NRF_GPIO_BASE);
16431823 });
16441824
16451825 cases.add("basic macro function",
......@@ -1701,13 +1881,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
17011881 \\ unsigned d = 440;
17021882 \\}
17031883 , &[_][]const u8{
1704 \\pub var a: c_long = @bitCast(c_long, @as(c_long, @as(c_int, 2)));
1705 \\pub var b: c_long = @bitCast(c_long, @as(c_long, @as(c_int, 2)));
1884 \\pub var a: c_long = 2;
1885 \\pub var b: c_long = 2;
17061886 \\pub var c: c_int = 4;
17071887 \\pub export fn foo(arg_c_1: u8) void {
17081888 \\ var c_1 = arg_c_1;
17091889 \\ var a_2: c_int = undefined;
1710 \\ var b_3: u8 = @bitCast(u8, @truncate(i8, @as(c_int, 123)));
1890 \\ var b_3: u8 = 123;
17111891 \\ b_3 = @bitCast(u8, @truncate(i8, a_2));
17121892 \\ {
17131893 \\ var d: c_int = 5;
......@@ -1723,17 +1903,17 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
17231903 \\}
17241904 , &[_][]const u8{
17251905 \\pub export fn foo() c_int {
1726 \\ _ = (blk: {
1906 \\ _ = blk: {
17271907 \\ _ = @as(c_int, 2);
17281908 \\ break :blk @as(c_int, 4);
1729 \\ });
1730 \\ return (blk: {
1731 \\ _ = (blk_1: {
1909 \\ };
1910 \\ return blk: {
1911 \\ _ = blk_1: {
17321912 \\ _ = @as(c_int, 2);
17331913 \\ break :blk_1 @as(c_int, 4);
1734 \\ });
1914 \\ };
17351915 \\ break :blk @as(c_int, 6);
1736 \\ });
1916 \\ };
17371917 \\}
17381918 });
17391919
......@@ -1780,20 +1960,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
17801960 \\ while (true) {
17811961 \\ var a_1: c_int = 4;
17821962 \\ a_1 = 9;
1783 \\ return (blk: {
1963 \\ return blk: {
17841964 \\ _ = @as(c_int, 6);
17851965 \\ break :blk a_1;
1786 \\ });
1966 \\ };
17871967 \\ }
17881968 \\ while (true) {
17891969 \\ var a_1: c_int = 2;
17901970 \\ a_1 = 12;
1791 \\ if (!true) break;
1792 \\ }
1793 \\ while (true) {
1794 \\ a = 7;
1795 \\ if (!true) break;
17961971 \\ }
1972 \\ while (true) a = 7;
17971973 \\ return 0;
17981974 \\}
17991975 });
......@@ -1813,16 +1989,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
18131989 \\ var b: c_int = 4;
18141990 \\ while ((i + @as(c_int, 2)) != 0) : (i = 2) {
18151991 \\ var a: c_int = 2;
1816 \\ _ = (blk: {
1817 \\ _ = (blk_1: {
1992 \\ _ = blk: {
1993 \\ _ = blk_1: {
18181994 \\ a = 6;
18191995 \\ break :blk_1 @as(c_int, 5);
1820 \\ });
1996 \\ };
18211997 \\ break :blk @as(c_int, 7);
1822 \\ });
1998 \\ };
18231999 \\ }
18242000 \\ }
1825 \\ var i: u8 = @bitCast(u8, @truncate(i8, @as(c_int, 2)));
2001 \\ var i: u8 = 2;
18262002 \\}
18272003 });
18282004
......@@ -1830,7 +2006,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
18302006 \\unsigned anyerror = 2;
18312007 \\#define noreturn _Noreturn
18322008 , &[_][]const u8{
1833 \\pub export var anyerror_1: c_uint = @bitCast(c_uint, @as(c_int, 2));
2009 \\pub export var anyerror_1: c_uint = 2;
18342010 ,
18352011 \\pub const noreturn_2 = @compileError("unable to translate C expr: unexpected token .Keyword_noreturn");
18362012 });
......@@ -1844,7 +2020,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
18442020 \\pub export var a: f32 = @floatCast(f32, 3.1415);
18452021 \\pub export var b: f64 = 3.1415;
18462022 \\pub export var c: c_int = @floatToInt(c_int, 3.1415);
1847 \\pub export var d: f64 = @intToFloat(f64, @as(c_int, 3));
2023 \\pub export var d: f64 = 3;
18482024 });
18492025
18502026 cases.add("conditional operator",
......@@ -1854,7 +2030,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
18542030 \\}
18552031 , &[_][]const u8{
18562032 \\pub export fn bar() c_int {
1857 \\ if ((if (true) @as(c_int, 5) else (if (true) @as(c_int, 4) else @as(c_int, 6))) != 0) _ = @as(c_int, 2);
2033 \\ if ((if (true) @as(c_int, 5) else if (true) @as(c_int, 4) else @as(c_int, 6)) != 0) _ = @as(c_int, 2);
18582034 \\ return if (true) @as(c_int, 5) else if (true) @as(c_int, 4) else @as(c_int, 6);
18592035 \\}
18602036 });
......@@ -1870,34 +2046,64 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
18702046 \\ default:
18712047 \\ res = 3 * i;
18722048 \\ break;
2049 \\ break;
2050 \\ case 7: {
2051 \\ res = 7;
2052 \\ break;
2053 \\ }
18732054 \\ case 4:
2055 \\ case 5:
2056 \\ res = 69;
2057 \\ {
18742058 \\ res = 5;
2059 \\ return;
2060 \\ }
2061 \\ case 6:
2062 \\ switch (res) {
2063 \\ case 9: break;
2064 \\ }
2065 \\ res = 1;
2066 \\ return;
18752067 \\ }
18762068 \\}
18772069 , &[_][]const u8{
18782070 \\pub export fn switch_fn(arg_i: c_int) void {
18792071 \\ var i = arg_i;
18802072 \\ var res: c_int = 0;
1881 \\ @"switch": {
1882 \\ case_2: {
1883 \\ default: {
1884 \\ case_1: {
1885 \\ case: {
1886 \\ switch (i) {
1887 \\ @as(c_int, 0) => break :case,
1888 \\ @as(c_int, 1)...@as(c_int, 3) => break :case_1,
1889 \\ else => break :default,
1890 \\ @as(c_int, 4) => break :case_2,
1891 \\ }
1892 \\ }
1893 \\ res = 1;
1894 \\ }
1895 \\ res = 2;
2073 \\ switch (i) {
2074 \\ @as(c_int, 0) => {
2075 \\ res = 1;
2076 \\ res = 2;
2077 \\ res = @as(c_int, 3) * i;
2078 \\ },
2079 \\ @as(c_int, 1)...@as(c_int, 3) => {
2080 \\ res = 2;
2081 \\ res = @as(c_int, 3) * i;
2082 \\ },
2083 \\ else => {
2084 \\ res = @as(c_int, 3) * i;
2085 \\ },
2086 \\ @as(c_int, 7) => {
2087 \\ {
2088 \\ res = 7;
2089 \\ break;
18962090 \\ }
1897 \\ res = (@as(c_int, 3) * i);
1898 \\ break :@"switch";
1899 \\ }
1900 \\ res = 5;
2091 \\ },
2092 \\ @as(c_int, 4), @as(c_int, 5) => {
2093 \\ res = 69;
2094 \\ {
2095 \\ res = 5;
2096 \\ return;
2097 \\ }
2098 \\ },
2099 \\ @as(c_int, 6) => {
2100 \\ switch (res) {
2101 \\ @as(c_int, 9) => {},
2102 \\ else => {},
2103 \\ }
2104 \\ res = 1;
2105 \\ return;
2106 \\ },
19012107 \\ }
19022108 \\}
19032109 });
......@@ -1973,13 +2179,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
19732179 \\ Two,
19742180 \\};
19752181 , &[_][]const u8{
1976 \\pub const One = @enumToInt(enum_unnamed_1.One);
1977 \\pub const Two = @enumToInt(enum_unnamed_1.Two);
19782182 \\const enum_unnamed_1 = extern enum(c_int) {
19792183 \\ One,
19802184 \\ Two,
19812185 \\ _,
19822186 \\};
2187 \\pub const One = @enumToInt(enum_unnamed_1.One);
2188 \\pub const Two = @enumToInt(enum_unnamed_1.Two);
19832189 });
19842190
19852191 cases.add("c style cast",
......@@ -1993,7 +2199,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
19932199 \\}
19942200 });
19952201
1996 // TODO translate-c should in theory be able to figure out to drop all these casts
19972202 cases.add("escape sequences",
19982203 \\const char *escapes() {
19992204 \\char a = '\'',
......@@ -2012,17 +2217,17 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
20122217 \\
20132218 , &[_][]const u8{
20142219 \\pub export fn escapes() [*c]const u8 {
2015 \\ var a: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\'')));
2016 \\ var b: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\\')));
2017 \\ var c: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\x07')));
2018 \\ var d: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\x08')));
2019 \\ var e: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\x0c')));
2020 \\ var f: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\n')));
2021 \\ var g: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\r')));
2022 \\ var h: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\t')));
2023 \\ var i: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\x0b')));
2024 \\ var j: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\x00')));
2025 \\ var k: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\"')));
2220 \\ var a: u8 = '\'';
2221 \\ var b: u8 = '\\';
2222 \\ var c: u8 = '\x07';
2223 \\ var d: u8 = '\x08';
2224 \\ var e: u8 = '\x0c';
2225 \\ var f: u8 = '\n';
2226 \\ var g: u8 = '\r';
2227 \\ var h: u8 = '\t';
2228 \\ var i: u8 = '\x0b';
2229 \\ var j: u8 = '\x00';
2230 \\ var k: u8 = '\"';
20262231 \\ return "\'\\\x07\x08\x0c\n\r\t\x0b\x00\"";
20272232 \\}
20282233 });
......@@ -2043,12 +2248,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
20432248 \\pub export fn foo() void {
20442249 \\ var a: c_int = 2;
20452250 \\ while (true) {
2046 \\ a = (a - @as(c_int, 1));
2251 \\ a = a - @as(c_int, 1);
20472252 \\ if (!(a != 0)) break;
20482253 \\ }
20492254 \\ var b: c_int = 2;
20502255 \\ while (true) {
2051 \\ b = (b - @as(c_int, 1));
2256 \\ b = b - @as(c_int, 1);
20522257 \\ if (!(b != 0)) break;
20532258 \\ }
20542259 \\}
......@@ -2084,25 +2289,28 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
20842289 \\ C,
20852290 \\ _,
20862291 \\};
2292 \\pub const FooA = @enumToInt(enum_Foo.A);
2293 \\pub const FooB = @enumToInt(enum_Foo.B);
2294 \\pub const FooC = @enumToInt(enum_Foo.C);
20872295 \\pub const SomeTypedef = c_int;
20882296 \\pub export fn and_or_non_bool(arg_a: c_int, arg_b: f32, arg_c: ?*c_void) c_int {
20892297 \\ var a = arg_a;
20902298 \\ var b = arg_b;
20912299 \\ var c = arg_c;
20922300 \\ var d: enum_Foo = @intToEnum(enum_Foo, FooA);
2093 \\ var e: c_int = @boolToInt(((a != 0) and (b != 0)));
2094 \\ var f: c_int = @boolToInt(((b != 0) and (c != null)));
2095 \\ var g: c_int = @boolToInt(((a != 0) and (c != null)));
2096 \\ var h: c_int = @boolToInt(((a != 0) or (b != 0)));
2097 \\ var i: c_int = @boolToInt(((b != 0) or (c != null)));
2098 \\ var j: c_int = @boolToInt(((a != 0) or (c != null)));
2099 \\ var k: c_int = @boolToInt(((a != 0) or (@bitCast(c_int, @enumToInt(d)) != 0)));
2100 \\ var l: c_int = @boolToInt(((@bitCast(c_int, @enumToInt(d)) != 0) and (b != 0)));
2101 \\ var m: c_int = @boolToInt(((c != null) or (@bitCast(c_uint, @enumToInt(d)) != 0)));
2301 \\ var e: c_int = @boolToInt((a != 0) and (b != 0));
2302 \\ var f: c_int = @boolToInt((b != 0) and (c != null));
2303 \\ var g: c_int = @boolToInt((a != 0) and (c != null));
2304 \\ var h: c_int = @boolToInt((a != 0) or (b != 0));
2305 \\ var i: c_int = @boolToInt((b != 0) or (c != null));
2306 \\ var j: c_int = @boolToInt((a != 0) or (c != null));
2307 \\ var k: c_int = @boolToInt((a != 0) or (@bitCast(c_int, @enumToInt(d)) != 0));
2308 \\ var l: c_int = @boolToInt((@bitCast(c_int, @enumToInt(d)) != 0) and (b != 0));
2309 \\ var m: c_int = @boolToInt((c != null) or (@bitCast(c_uint, @enumToInt(d)) != 0));
21022310 \\ var td: SomeTypedef = 44;
2103 \\ var o: c_int = @boolToInt(((td != 0) or (b != 0)));
2104 \\ var p: c_int = @boolToInt(((c != null) and (td != 0)));
2105 \\ return ((((((((((e + f) + g) + h) + i) + j) + k) + l) + m) + o) + p);
2311 \\ var o: c_int = @boolToInt((td != 0) or (b != 0));
2312 \\ var p: c_int = @boolToInt((c != null) and (td != 0));
2313 \\ return (((((((((e + f) + g) + h) + i) + j) + k) + l) + m) + o) + p;
21062314 \\}
21072315 ,
21082316 \\pub const Foo = enum_Foo;
......@@ -2129,6 +2337,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
21292337 \\ B,
21302338 \\ _,
21312339 \\};
2340 \\pub const BarA = @enumToInt(enum_Bar.A);
2341 \\pub const BarB = @enumToInt(enum_Bar.B);
21322342 \\pub extern fn func(a: [*c]struct_Foo, b: [*c][*c]enum_Bar) void;
21332343 ,
21342344 \\pub const Foo = struct_Foo;
......@@ -2143,7 +2353,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
21432353 \\pub export fn max(arg_a: c_int, arg_b: c_int) c_int {
21442354 \\ var a = arg_a;
21452355 \\ var b = arg_b;
2146 \\ return ((a & b) ^ (a | b));
2356 \\ return (a & b) ^ (a | b);
21472357 \\}
21482358 });
21492359
......@@ -2162,13 +2372,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
21622372 \\pub export fn test_comparisons(arg_a: c_int, arg_b: c_int) c_int {
21632373 \\ var a = arg_a;
21642374 \\ var b = arg_b;
2165 \\ var c: c_int = @boolToInt((a < b));
2166 \\ var d: c_int = @boolToInt((a > b));
2167 \\ var e: c_int = @boolToInt((a <= b));
2168 \\ var f: c_int = @boolToInt((a >= b));
2169 \\ var g: c_int = @boolToInt((c < d));
2170 \\ var h: c_int = @boolToInt((e < f));
2171 \\ var i: c_int = @boolToInt((g < h));
2375 \\ var c: c_int = @boolToInt(a < b);
2376 \\ var d: c_int = @boolToInt(a > b);
2377 \\ var e: c_int = @boolToInt(a <= b);
2378 \\ var f: c_int = @boolToInt(a >= b);
2379 \\ var g: c_int = @boolToInt(c < d);
2380 \\ var h: c_int = @boolToInt(e < f);
2381 \\ var i: c_int = @boolToInt(g < h);
21722382 \\ return i;
21732383 \\}
21742384 });
......@@ -2215,11 +2425,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
22152425 \\}
22162426 , &[_][]const u8{
22172427 \\pub export fn foo() c_int {
2218 \\ return (blk: {
2428 \\ return blk: {
22192429 \\ var a: c_int = 1;
22202430 \\ _ = a;
22212431 \\ break :blk a;
2222 \\ });
2432 \\ };
22232433 \\}
22242434 });
22252435
......@@ -2289,8 +2499,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
22892499 , &[_][]const u8{
22902500 \\pub export fn foo() void {
22912501 \\ var a: [10]c_longlong = undefined;
2292 \\ var i: c_longlong = @bitCast(c_longlong, @as(c_longlong, @as(c_int, 0)));
2293 \\ a[@intCast(usize, i)] = @bitCast(c_longlong, @as(c_longlong, @as(c_int, 0)));
2502 \\ var i: c_longlong = 0;
2503 \\ a[@intCast(usize, i)] = 0;
22942504 \\}
22952505 });
22962506
......@@ -2302,8 +2512,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
23022512 , &[_][]const u8{
23032513 \\pub export fn foo() void {
23042514 \\ var a: [10]c_uint = undefined;
2305 \\ var i: c_uint = @bitCast(c_uint, @as(c_int, 0));
2306 \\ a[i] = @bitCast(c_uint, @as(c_int, 0));
2515 \\ var i: c_uint = 0;
2516 \\ a[i] = 0;
23072517 \\}
23082518 });
23092519
......@@ -2395,6 +2605,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
23952605 \\ C,
23962606 \\ _,
23972607 \\};
2608 \\pub const A = @enumToInt(enum_SomeEnum.A);
2609 \\pub const B = @enumToInt(enum_SomeEnum.B);
2610 \\pub const C = @enumToInt(enum_SomeEnum.C);
23982611 \\pub export fn if_none_bool(arg_a: c_int, arg_b: f32, arg_c: ?*c_void, arg_d: enum_SomeEnum) c_int {
23992612 \\ var a = arg_a;
24002613 \\ var b = arg_b;
......@@ -2484,10 +2697,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
24842697 \\ var f: ?fn () callconv(.C) void = foo;
24852698 \\ var b: ?fn () callconv(.C) c_int = baz;
24862699 \\ f.?();
2487 \\ (f).?();
2700 \\ f.?();
24882701 \\ foo();
24892702 \\ _ = b.?();
2490 \\ _ = (b).?();
2703 \\ _ = b.?();
24912704 \\ _ = baz();
24922705 \\}
24932706 });
......@@ -2508,31 +2721,31 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
25082721 , &[_][]const u8{
25092722 \\pub export fn foo() void {
25102723 \\ var i: c_int = 0;
2511 \\ var u: c_uint = @bitCast(c_uint, @as(c_int, 0));
2724 \\ var u: c_uint = 0;
25122725 \\ i += 1;
25132726 \\ i -= 1;
25142727 \\ u +%= 1;
25152728 \\ u -%= 1;
2516 \\ i = (blk: {
2729 \\ i = blk: {
25172730 \\ const ref = &i;
25182731 \\ ref.* += 1;
25192732 \\ break :blk ref.*;
2520 \\ });
2521 \\ i = (blk: {
2733 \\ };
2734 \\ i = blk: {
25222735 \\ const ref = &i;
25232736 \\ ref.* -= 1;
25242737 \\ break :blk ref.*;
2525 \\ });
2526 \\ u = (blk: {
2738 \\ };
2739 \\ u = blk: {
25272740 \\ const ref = &u;
25282741 \\ ref.* +%= 1;
25292742 \\ break :blk ref.*;
2530 \\ });
2531 \\ u = (blk: {
2743 \\ };
2744 \\ u = blk: {
25322745 \\ const ref = &u;
25332746 \\ ref.* -%= 1;
25342747 \\ break :blk ref.*;
2535 \\ });
2748 \\ };
25362749 \\}
25372750 });
25382751
......@@ -2595,67 +2808,67 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
25952808 , &[_][]const u8{
25962809 \\pub export fn foo() void {
25972810 \\ var a: c_int = 0;
2598 \\ var b: c_uint = @bitCast(c_uint, @as(c_int, 0));
2599 \\ a += (blk: {
2811 \\ var b: c_uint = 0;
2812 \\ a += blk: {
26002813 \\ const ref = &a;
2601 \\ ref.* = ref.* + @as(c_int, 1);
2814 \\ ref.* += @as(c_int, 1);
26022815 \\ break :blk ref.*;
2603 \\ });
2604 \\ a -= (blk: {
2816 \\ };
2817 \\ a -= blk: {
26052818 \\ const ref = &a;
2606 \\ ref.* = ref.* - @as(c_int, 1);
2819 \\ ref.* -= @as(c_int, 1);
26072820 \\ break :blk ref.*;
2608 \\ });
2609 \\ a *= (blk: {
2821 \\ };
2822 \\ a *= blk: {
26102823 \\ const ref = &a;
2611 \\ ref.* = ref.* * @as(c_int, 1);
2824 \\ ref.* *= @as(c_int, 1);
26122825 \\ break :blk ref.*;
2613 \\ });
2614 \\ a &= (blk: {
2826 \\ };
2827 \\ a &= blk: {
26152828 \\ const ref = &a;
2616 \\ ref.* = ref.* & @as(c_int, 1);
2829 \\ ref.* &= @as(c_int, 1);
26172830 \\ break :blk ref.*;
2618 \\ });
2619 \\ a |= (blk: {
2831 \\ };
2832 \\ a |= blk: {
26202833 \\ const ref = &a;
2621 \\ ref.* = ref.* | @as(c_int, 1);
2834 \\ ref.* |= @as(c_int, 1);
26222835 \\ break :blk ref.*;
2623 \\ });
2624 \\ a ^= (blk: {
2836 \\ };
2837 \\ a ^= blk: {
26252838 \\ const ref = &a;
2626 \\ ref.* = ref.* ^ @as(c_int, 1);
2839 \\ ref.* ^= @as(c_int, 1);
26272840 \\ break :blk ref.*;
2628 \\ });
2629 \\ a >>= @intCast(@import("std").math.Log2Int(c_int), (blk: {
2841 \\ };
2842 \\ a >>= @intCast(@import("std").math.Log2Int(c_int), blk: {
26302843 \\ const ref = &a;
2631 \\ ref.* = ref.* >> @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1));
2844 \\ ref.* >>= @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1));
26322845 \\ break :blk ref.*;
2633 \\ }));
2634 \\ a <<= @intCast(@import("std").math.Log2Int(c_int), (blk: {
2846 \\ });
2847 \\ a <<= @intCast(@import("std").math.Log2Int(c_int), blk: {
26352848 \\ const ref = &a;
2636 \\ ref.* = ref.* << @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1));
2849 \\ ref.* <<= @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1));
26372850 \\ break :blk ref.*;
2638 \\ }));
2639 \\ a = @divTrunc(a, (blk: {
2851 \\ });
2852 \\ a = @divTrunc(a, blk: {
26402853 \\ const ref = &a;
26412854 \\ ref.* = @divTrunc(ref.*, @as(c_int, 1));
26422855 \\ break :blk ref.*;
2643 \\ }));
2644 \\ a = @rem(a, (blk: {
2856 \\ });
2857 \\ a = @rem(a, blk: {
26452858 \\ const ref = &a;
26462859 \\ ref.* = @rem(ref.*, @as(c_int, 1));
26472860 \\ break :blk ref.*;
2648 \\ }));
2649 \\ b /= (blk: {
2861 \\ });
2862 \\ b /= blk: {
26502863 \\ const ref = &b;
2651 \\ ref.* = ref.* / @bitCast(c_uint, @as(c_int, 1));
2864 \\ ref.* /= @bitCast(c_uint, @as(c_int, 1));
26522865 \\ break :blk ref.*;
2653 \\ });
2654 \\ b %= (blk: {
2866 \\ };
2867 \\ b %= blk: {
26552868 \\ const ref = &b;
2656 \\ ref.* = ref.* % @bitCast(c_uint, @as(c_int, 1));
2869 \\ ref.* %= @bitCast(c_uint, @as(c_int, 1));
26572870 \\ break :blk ref.*;
2658 \\ });
2871 \\ };
26592872 \\}
26602873 });
26612874
......@@ -2673,47 +2886,47 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
26732886 \\}
26742887 , &[_][]const u8{
26752888 \\pub export fn foo() void {
2676 \\ var a: c_uint = @bitCast(c_uint, @as(c_int, 0));
2677 \\ a +%= (blk: {
2889 \\ var a: c_uint = 0;
2890 \\ a +%= blk: {
26782891 \\ const ref = &a;
2679 \\ ref.* = ref.* +% @bitCast(c_uint, @as(c_int, 1));
2892 \\ ref.* +%= @bitCast(c_uint, @as(c_int, 1));
26802893 \\ break :blk ref.*;
2681 \\ });
2682 \\ a -%= (blk: {
2894 \\ };
2895 \\ a -%= blk: {
26832896 \\ const ref = &a;
2684 \\ ref.* = ref.* -% @bitCast(c_uint, @as(c_int, 1));
2897 \\ ref.* -%= @bitCast(c_uint, @as(c_int, 1));
26852898 \\ break :blk ref.*;
2686 \\ });
2687 \\ a *%= (blk: {
2899 \\ };
2900 \\ a *%= blk: {
26882901 \\ const ref = &a;
2689 \\ ref.* = ref.* *% @bitCast(c_uint, @as(c_int, 1));
2902 \\ ref.* *%= @bitCast(c_uint, @as(c_int, 1));
26902903 \\ break :blk ref.*;
2691 \\ });
2692 \\ a &= (blk: {
2904 \\ };
2905 \\ a &= blk: {
26932906 \\ const ref = &a;
2694 \\ ref.* = ref.* & @bitCast(c_uint, @as(c_int, 1));
2907 \\ ref.* &= @bitCast(c_uint, @as(c_int, 1));
26952908 \\ break :blk ref.*;
2696 \\ });
2697 \\ a |= (blk: {
2909 \\ };
2910 \\ a |= blk: {
26982911 \\ const ref = &a;
2699 \\ ref.* = ref.* | @bitCast(c_uint, @as(c_int, 1));
2912 \\ ref.* |= @bitCast(c_uint, @as(c_int, 1));
27002913 \\ break :blk ref.*;
2701 \\ });
2702 \\ a ^= (blk: {
2914 \\ };
2915 \\ a ^= blk: {
27032916 \\ const ref = &a;
2704 \\ ref.* = ref.* ^ @bitCast(c_uint, @as(c_int, 1));
2917 \\ ref.* ^= @bitCast(c_uint, @as(c_int, 1));
27052918 \\ break :blk ref.*;
2706 \\ });
2707 \\ a >>= @intCast(@import("std").math.Log2Int(c_uint), (blk: {
2919 \\ };
2920 \\ a >>= @intCast(@import("std").math.Log2Int(c_uint), blk: {
27082921 \\ const ref = &a;
2709 \\ ref.* = ref.* >> @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1));
2922 \\ ref.* >>= @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1));
27102923 \\ break :blk ref.*;
2711 \\ }));
2712 \\ a <<= @intCast(@import("std").math.Log2Int(c_uint), (blk: {
2924 \\ });
2925 \\ a <<= @intCast(@import("std").math.Log2Int(c_uint), blk: {
27132926 \\ const ref = &a;
2714 \\ ref.* = ref.* << @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1));
2927 \\ ref.* <<= @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1));
27152928 \\ break :blk ref.*;
2716 \\ }));
2929 \\ });
27172930 \\}
27182931 });
27192932
......@@ -2733,35 +2946,35 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
27332946 , &[_][]const u8{
27342947 \\pub export fn foo() void {
27352948 \\ var i: c_int = 0;
2736 \\ var u: c_uint = @bitCast(c_uint, @as(c_int, 0));
2949 \\ var u: c_uint = 0;
27372950 \\ i += 1;
27382951 \\ i -= 1;
27392952 \\ u +%= 1;
27402953 \\ u -%= 1;
2741 \\ i = (blk: {
2954 \\ i = blk: {
27422955 \\ const ref = &i;
27432956 \\ const tmp = ref.*;
27442957 \\ ref.* += 1;
27452958 \\ break :blk tmp;
2746 \\ });
2747 \\ i = (blk: {
2959 \\ };
2960 \\ i = blk: {
27482961 \\ const ref = &i;
27492962 \\ const tmp = ref.*;
27502963 \\ ref.* -= 1;
27512964 \\ break :blk tmp;
2752 \\ });
2753 \\ u = (blk: {
2965 \\ };
2966 \\ u = blk: {
27542967 \\ const ref = &u;
27552968 \\ const tmp = ref.*;
27562969 \\ ref.* +%= 1;
27572970 \\ break :blk tmp;
2758 \\ });
2759 \\ u = (blk: {
2971 \\ };
2972 \\ u = blk: {
27602973 \\ const ref = &u;
27612974 \\ const tmp = ref.*;
27622975 \\ ref.* -%= 1;
27632976 \\ break :blk tmp;
2764 \\ });
2977 \\ };
27652978 \\}
27662979 });
27672980
......@@ -2854,15 +3067,15 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
28543067 \\ Foo1,
28553068 \\};
28563069 , &[_][]const u8{
2857 \\pub const FooA = @enumToInt(enum_Foo.A);
2858 \\pub const FooB = @enumToInt(enum_Foo.B);
2859 \\pub const Foo1 = @enumToInt(enum_Foo.@"1");
28603070 \\pub const enum_Foo = extern enum(c_int) {
28613071 \\ A = 2,
28623072 \\ B = 5,
28633073 \\ @"1" = 6,
28643074 \\ _,
28653075 \\};
3076 \\pub const FooA = @enumToInt(enum_Foo.A);
3077 \\pub const FooB = @enumToInt(enum_Foo.B);
3078 \\pub const Foo1 = @enumToInt(enum_Foo.@"1");
28663079 ,
28673080 \\pub const Foo = enum_Foo;
28683081 });
......@@ -2872,13 +3085,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
28723085 \\#define BAR (void*) a
28733086 \\#define BAZ (uint32_t)(2)
28743087 , &[_][]const u8{
2875 \\pub fn FOO(bar: anytype) callconv(.Inline) @TypeOf(baz((@import("std").meta.cast(?*c_void, baz)))) {
2876 \\ return baz((@import("std").meta.cast(?*c_void, baz)));
3088 \\pub fn FOO(bar: anytype) callconv(.Inline) @TypeOf(baz(@import("std").meta.cast(?*c_void, baz))) {
3089 \\ return baz(@import("std").meta.cast(?*c_void, baz));
28773090 \\}
28783091 ,
2879 \\pub const BAR = (@import("std").meta.cast(?*c_void, a));
3092 \\pub const BAR = @import("std").meta.cast(?*c_void, a);
28803093 ,
2881 \\pub const BAZ = (@import("std").meta.cast(u32, 2));
3094 \\pub const BAZ = @import("std").meta.cast(u32, 2);
28823095 });
28833096
28843097 cases.add("macro with cast to unsigned short, long, and long long",
......@@ -2886,9 +3099,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
28863099 \\#define CURLAUTH_BASIC ((unsigned long) 1)
28873100 \\#define CURLAUTH_BASIC_BUT_ULONGLONG ((unsigned long long) 1)
28883101 , &[_][]const u8{
2889 \\pub const CURLAUTH_BASIC_BUT_USHORT = (@import("std").meta.cast(c_ushort, 1));
2890 \\pub const CURLAUTH_BASIC = (@import("std").meta.cast(c_ulong, 1));
2891 \\pub const CURLAUTH_BASIC_BUT_ULONGLONG = (@import("std").meta.cast(c_ulonglong, 1));
3102 \\pub const CURLAUTH_BASIC_BUT_USHORT = @import("std").meta.cast(c_ushort, 1);
3103 \\pub const CURLAUTH_BASIC = @import("std").meta.cast(c_ulong, 1);
3104 \\pub const CURLAUTH_BASIC_BUT_ULONGLONG = @import("std").meta.cast(c_ulonglong, 1);
28923105 });
28933106
28943107 cases.add("macro conditional operator",
......@@ -2904,9 +3117,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
29043117 \\}
29053118 , &[_][]const u8{
29063119 \\pub fn foo() callconv(.C) void {
2907 \\ if (true) while (true) {
2908 \\ if (!false) break;
2909 \\ };
3120 \\ if (true) {}
29103121 \\}
29113122 });
29123123
......@@ -2923,7 +3134,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
29233134 \\}
29243135 });
29253136
2926 // TODO: detect to use different block labels here
29273137 cases.add("nested assignment",
29283138 \\int foo(int *p, int x) {
29293139 \\ return *p++ = x;
......@@ -3033,10 +3243,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
30333243 , &[_][]const u8{
30343244 \\pub export fn foo(arg_x: bool) bool {
30353245 \\ var x = arg_x;
3036 \\ var a: bool = (@as(c_int, @boolToInt(x)) != @as(c_int, 1));
3037 \\ var b: bool = (@as(c_int, @boolToInt(a)) != @as(c_int, 0));
3246 \\ var a: bool = @as(c_int, @boolToInt(x)) != @as(c_int, 1);
3247 \\ var b: bool = @as(c_int, @boolToInt(a)) != @as(c_int, 0);
30383248 \\ var c: bool = @ptrToInt(foo) != 0;
3039 \\ return foo((@as(c_int, @boolToInt(c)) != @as(c_int, @boolToInt(b))));
3249 \\ return foo(@as(c_int, @boolToInt(c)) != @as(c_int, @boolToInt(b)));
30403250 \\}
30413251 });
30423252
......@@ -3106,8 +3316,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
31063316 \\#define DefaultScreen(dpy) (((_XPrivDisplay)(dpy))->default_screen)
31073317 \\
31083318 , &[_][]const u8{
3109 \\pub fn DefaultScreen(dpy: anytype) callconv(.Inline) @TypeOf((@import("std").meta.cast(_XPrivDisplay, dpy)).*.default_screen) {
3110 \\ return (@import("std").meta.cast(_XPrivDisplay, dpy)).*.default_screen;
3319 \\pub fn DefaultScreen(dpy: anytype) callconv(.Inline) @TypeOf(@import("std").meta.cast(_XPrivDisplay, dpy).*.default_screen) {
3320 \\ return @import("std").meta.cast(_XPrivDisplay, dpy).*.default_screen;
31113321 \\}
31123322 });
31133323
......@@ -3115,9 +3325,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
31153325 \\#define NULL ((void*)0)
31163326 \\#define FOO ((int)0x8000)
31173327 , &[_][]const u8{
3118 \\pub const NULL = (@import("std").meta.cast(?*c_void, 0));
3328 \\pub const NULL = @import("std").meta.cast(?*c_void, 0);
31193329 ,
3120 \\pub const FOO = (@import("std").meta.cast(c_int, 0x8000));
3330 \\pub const FOO = @import("std").meta.cast(c_int, 0x8000);
31213331 });
31223332
31233333 if (std.Target.current.abi == .msvc) {