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...@@ -370,7 +370,6 @@ set(ZIG_STAGE2_SOURCES
370 "${CMAKE_SOURCE_DIR}/lib/std/heap.zig"370 "${CMAKE_SOURCE_DIR}/lib/std/heap.zig"
371 "${CMAKE_SOURCE_DIR}/lib/std/heap/arena_allocator.zig"371 "${CMAKE_SOURCE_DIR}/lib/std/heap/arena_allocator.zig"
372 "${CMAKE_SOURCE_DIR}/lib/std/io.zig"372 "${CMAKE_SOURCE_DIR}/lib/std/io.zig"
373 "${CMAKE_SOURCE_DIR}/lib/std/io/auto_indenting_stream.zig"
374 "${CMAKE_SOURCE_DIR}/lib/std/io/buffered_atomic_file.zig"373 "${CMAKE_SOURCE_DIR}/lib/std/io/buffered_atomic_file.zig"
375 "${CMAKE_SOURCE_DIR}/lib/std/io/buffered_writer.zig"374 "${CMAKE_SOURCE_DIR}/lib/std/io/buffered_writer.zig"
376 "${CMAKE_SOURCE_DIR}/lib/std/io/change_detection_stream.zig"375 "${CMAKE_SOURCE_DIR}/lib/std/io/change_detection_stream.zig"
...@@ -408,6 +407,7 @@ set(ZIG_STAGE2_SOURCES...@@ -408,6 +407,7 @@ set(ZIG_STAGE2_SOURCES
408 "${CMAKE_SOURCE_DIR}/lib/std/meta.zig"407 "${CMAKE_SOURCE_DIR}/lib/std/meta.zig"
409 "${CMAKE_SOURCE_DIR}/lib/std/meta/trailer_flags.zig"408 "${CMAKE_SOURCE_DIR}/lib/std/meta/trailer_flags.zig"
410 "${CMAKE_SOURCE_DIR}/lib/std/meta/trait.zig"409 "${CMAKE_SOURCE_DIR}/lib/std/meta/trait.zig"
410 "${CMAKE_SOURCE_DIR}/lib/std/multi_array_list.zig"
411 "${CMAKE_SOURCE_DIR}/lib/std/os.zig"411 "${CMAKE_SOURCE_DIR}/lib/std/os.zig"
412 "${CMAKE_SOURCE_DIR}/lib/std/os/bits.zig"412 "${CMAKE_SOURCE_DIR}/lib/std/os/bits.zig"
413 "${CMAKE_SOURCE_DIR}/lib/std/os/bits/linux.zig"413 "${CMAKE_SOURCE_DIR}/lib/std/os/bits/linux.zig"
...@@ -573,6 +573,7 @@ set(ZIG_STAGE2_SOURCES...@@ -573,6 +573,7 @@ set(ZIG_STAGE2_SOURCES
573 "${CMAKE_SOURCE_DIR}/src/target.zig"573 "${CMAKE_SOURCE_DIR}/src/target.zig"
574 "${CMAKE_SOURCE_DIR}/src/tracy.zig"574 "${CMAKE_SOURCE_DIR}/src/tracy.zig"
575 "${CMAKE_SOURCE_DIR}/src/translate_c.zig"575 "${CMAKE_SOURCE_DIR}/src/translate_c.zig"
576 "${CMAKE_SOURCE_DIR}/src/translate_c/ast.zig"
576 "${CMAKE_SOURCE_DIR}/src/type.zig"577 "${CMAKE_SOURCE_DIR}/src/type.zig"
577 "${CMAKE_SOURCE_DIR}/src/value.zig"578 "${CMAKE_SOURCE_DIR}/src/value.zig"
578 "${CMAKE_SOURCE_DIR}/src/windows_sdk.zig"579 "${CMAKE_SOURCE_DIR}/src/windows_sdk.zig"
README.md+1-1
...@@ -5,7 +5,7 @@ A general-purpose programming language and toolchain for maintaining...@@ -5,7 +5,7 @@ A general-purpose programming language and toolchain for maintaining
55
6## Resources6## Resources
77
8 * [Introduction](https://ziglang.org/#Introduction)8 * [Introduction](https://ziglang.org/learn/#introduction)
9 * [Download & Documentation](https://ziglang.org/download)9 * [Download & Documentation](https://ziglang.org/download)
10 * [Chapter 0 - Getting Started | ZigLearn.org](https://ziglearn.org/)10 * [Chapter 0 - Getting Started | ZigLearn.org](https://ziglearn.org/)
11 * [Community](https://github.com/ziglang/zig/wiki/Community)11 * [Community](https://github.com/ziglang/zig/wiki/Community)
build.zig+2
...@@ -77,10 +77,12 @@ pub fn build(b: *Builder) !void {...@@ -77,10 +77,12 @@ pub fn build(b: *Builder) !void {
7777
78 const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source");78 const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source");
79 const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse enable_llvm;79 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
81 const main_file = if (is_stage1) "src/stage1.zig" else "src/main.zig";82 const main_file = if (is_stage1) "src/stage1.zig" else "src/main.zig";
8283
83 var exe = b.addExecutable("zig", main_file);84 var exe = b.addExecutable("zig", main_file);
85 exe.strip = strip;
84 exe.install();86 exe.install();
85 exe.setBuildMode(mode);87 exe.setBuildMode(mode);
86 exe.setTarget(target);88 exe.setTarget(target);
doc/docgen.zig+134-121
...@@ -781,106 +781,119 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: anytype, source_token:...@@ -781,106 +781,119 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: anytype, source_token:
781 next_tok_is_fn = false;781 next_tok_is_fn = false;
782782
783 const token = tokenizer.next();783 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
784 try writeEscaped(out, src[index..token.loc.start]);799 try writeEscaped(out, src[index..token.loc.start]);
785 switch (token.id) {800 switch (token.tag) {
786 .Eof => break,801 .eof => break,
787802
788 .Keyword_align,803 .keyword_align,
789 .Keyword_and,804 .keyword_and,
790 .Keyword_asm,805 .keyword_asm,
791 .Keyword_async,806 .keyword_async,
792 .Keyword_await,807 .keyword_await,
793 .Keyword_break,808 .keyword_break,
794 .Keyword_catch,809 .keyword_catch,
795 .Keyword_comptime,810 .keyword_comptime,
796 .Keyword_const,811 .keyword_const,
797 .Keyword_continue,812 .keyword_continue,
798 .Keyword_defer,813 .keyword_defer,
799 .Keyword_else,814 .keyword_else,
800 .Keyword_enum,815 .keyword_enum,
801 .Keyword_errdefer,816 .keyword_errdefer,
802 .Keyword_error,817 .keyword_error,
803 .Keyword_export,818 .keyword_export,
804 .Keyword_extern,819 .keyword_extern,
805 .Keyword_for,820 .keyword_for,
806 .Keyword_if,821 .keyword_if,
807 .Keyword_inline,822 .keyword_inline,
808 .Keyword_noalias,823 .keyword_noalias,
809 .Keyword_noinline,824 .keyword_noinline,
810 .Keyword_nosuspend,825 .keyword_nosuspend,
811 .Keyword_opaque,826 .keyword_opaque,
812 .Keyword_or,827 .keyword_or,
813 .Keyword_orelse,828 .keyword_orelse,
814 .Keyword_packed,829 .keyword_packed,
815 .Keyword_anyframe,830 .keyword_anyframe,
816 .Keyword_pub,831 .keyword_pub,
817 .Keyword_resume,832 .keyword_resume,
818 .Keyword_return,833 .keyword_return,
819 .Keyword_linksection,834 .keyword_linksection,
820 .Keyword_callconv,835 .keyword_callconv,
821 .Keyword_struct,836 .keyword_struct,
822 .Keyword_suspend,837 .keyword_suspend,
823 .Keyword_switch,838 .keyword_switch,
824 .Keyword_test,839 .keyword_test,
825 .Keyword_threadlocal,840 .keyword_threadlocal,
826 .Keyword_try,841 .keyword_try,
827 .Keyword_union,842 .keyword_union,
828 .Keyword_unreachable,843 .keyword_unreachable,
829 .Keyword_usingnamespace,844 .keyword_usingnamespace,
830 .Keyword_var,845 .keyword_var,
831 .Keyword_volatile,846 .keyword_volatile,
832 .Keyword_allowzero,847 .keyword_allowzero,
833 .Keyword_while,848 .keyword_while,
834 .Keyword_anytype,849 .keyword_anytype,
835 => {850 => {
836 try out.writeAll("<span class=\"tok-kw\">");851 try out.writeAll("<span class=\"tok-kw\">");
837 try writeEscaped(out, src[token.loc.start..token.loc.end]);852 try writeEscaped(out, src[token.loc.start..token.loc.end]);
838 try out.writeAll("</span>");853 try out.writeAll("</span>");
839 },854 },
840855
841 .Keyword_fn => {856 .keyword_fn => {
842 try out.writeAll("<span class=\"tok-kw\">");857 try out.writeAll("<span class=\"tok-kw\">");
843 try writeEscaped(out, src[token.loc.start..token.loc.end]);858 try writeEscaped(out, src[token.loc.start..token.loc.end]);
844 try out.writeAll("</span>");859 try out.writeAll("</span>");
845 next_tok_is_fn = true;860 next_tok_is_fn = true;
846 },861 },
847862
848 .Keyword_undefined,863 .keyword_undefined,
849 .Keyword_null,864 .keyword_null,
850 .Keyword_true,865 .keyword_true,
851 .Keyword_false,866 .keyword_false,
852 => {867 => {
853 try out.writeAll("<span class=\"tok-null\">");868 try out.writeAll("<span class=\"tok-null\">");
854 try writeEscaped(out, src[token.loc.start..token.loc.end]);869 try writeEscaped(out, src[token.loc.start..token.loc.end]);
855 try out.writeAll("</span>");870 try out.writeAll("</span>");
856 },871 },
857872
858 .StringLiteral,873 .string_literal,
859 .MultilineStringLiteralLine,874 .multiline_string_literal_line,
860 .CharLiteral,875 .char_literal,
861 => {876 => {
862 try out.writeAll("<span class=\"tok-str\">");877 try out.writeAll("<span class=\"tok-str\">");
863 try writeEscaped(out, src[token.loc.start..token.loc.end]);878 try writeEscaped(out, src[token.loc.start..token.loc.end]);
864 try out.writeAll("</span>");879 try out.writeAll("</span>");
865 },880 },
866881
867 .Builtin => {882 .builtin => {
868 try out.writeAll("<span class=\"tok-builtin\">");883 try out.writeAll("<span class=\"tok-builtin\">");
869 try writeEscaped(out, src[token.loc.start..token.loc.end]);884 try writeEscaped(out, src[token.loc.start..token.loc.end]);
870 try out.writeAll("</span>");885 try out.writeAll("</span>");
871 },886 },
872887
873 .LineComment,888 .doc_comment,
874 .DocComment,889 .container_doc_comment,
875 .ContainerDocComment,
876 .ShebangLine,
877 => {890 => {
878 try out.writeAll("<span class=\"tok-comment\">");891 try out.writeAll("<span class=\"tok-comment\">");
879 try writeEscaped(out, src[token.loc.start..token.loc.end]);892 try writeEscaped(out, src[token.loc.start..token.loc.end]);
880 try out.writeAll("</span>");893 try out.writeAll("</span>");
881 },894 },
882895
883 .Identifier => {896 .identifier => {
884 if (prev_tok_was_fn) {897 if (prev_tok_was_fn) {
885 try out.writeAll("<span class=\"tok-fn\">");898 try out.writeAll("<span class=\"tok-fn\">");
886 try writeEscaped(out, src[token.loc.start..token.loc.end]);899 try writeEscaped(out, src[token.loc.start..token.loc.end]);
...@@ -908,71 +921,71 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: anytype, source_token:...@@ -908,71 +921,71 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: anytype, source_token:
908 }921 }
909 },922 },
910923
911 .IntegerLiteral,924 .integer_literal,
912 .FloatLiteral,925 .float_literal,
913 => {926 => {
914 try out.writeAll("<span class=\"tok-number\">");927 try out.writeAll("<span class=\"tok-number\">");
915 try writeEscaped(out, src[token.loc.start..token.loc.end]);928 try writeEscaped(out, src[token.loc.start..token.loc.end]);
916 try out.writeAll("</span>");929 try out.writeAll("</span>");
917 },930 },
918931
919 .Bang,932 .bang,
920 .Pipe,933 .pipe,
921 .PipePipe,934 .pipe_pipe,
922 .PipeEqual,935 .pipe_equal,
923 .Equal,936 .equal,
924 .EqualEqual,937 .equal_equal,
925 .EqualAngleBracketRight,938 .equal_angle_bracket_right,
926 .BangEqual,939 .bang_equal,
927 .LParen,940 .l_paren,
928 .RParen,941 .r_paren,
929 .Semicolon,942 .semicolon,
930 .Percent,943 .percent,
931 .PercentEqual,944 .percent_equal,
932 .LBrace,945 .l_brace,
933 .RBrace,946 .r_brace,
934 .LBracket,947 .l_bracket,
935 .RBracket,948 .r_bracket,
936 .Period,949 .period,
937 .PeriodAsterisk,950 .period_asterisk,
938 .Ellipsis2,951 .ellipsis2,
939 .Ellipsis3,952 .ellipsis3,
940 .Caret,953 .caret,
941 .CaretEqual,954 .caret_equal,
942 .Plus,955 .plus,
943 .PlusPlus,956 .plus_plus,
944 .PlusEqual,957 .plus_equal,
945 .PlusPercent,958 .plus_percent,
946 .PlusPercentEqual,959 .plus_percent_equal,
947 .Minus,960 .minus,
948 .MinusEqual,961 .minus_equal,
949 .MinusPercent,962 .minus_percent,
950 .MinusPercentEqual,963 .minus_percent_equal,
951 .Asterisk,964 .asterisk,
952 .AsteriskEqual,965 .asterisk_equal,
953 .AsteriskAsterisk,966 .asterisk_asterisk,
954 .AsteriskPercent,967 .asterisk_percent,
955 .AsteriskPercentEqual,968 .asterisk_percent_equal,
956 .Arrow,969 .arrow,
957 .Colon,970 .colon,
958 .Slash,971 .slash,
959 .SlashEqual,972 .slash_equal,
960 .Comma,973 .comma,
961 .Ampersand,974 .ampersand,
962 .AmpersandEqual,975 .ampersand_equal,
963 .QuestionMark,976 .question_mark,
964 .AngleBracketLeft,977 .angle_bracket_left,
965 .AngleBracketLeftEqual,978 .angle_bracket_left_equal,
966 .AngleBracketAngleBracketLeft,979 .angle_bracket_angle_bracket_left,
967 .AngleBracketAngleBracketLeftEqual,980 .angle_bracket_angle_bracket_left_equal,
968 .AngleBracketRight,981 .angle_bracket_right,
969 .AngleBracketRightEqual,982 .angle_bracket_right_equal,
970 .AngleBracketAngleBracketRight,983 .angle_bracket_angle_bracket_right,
971 .AngleBracketAngleBracketRightEqual,984 .angle_bracket_angle_bracket_right_equal,
972 .Tilde,985 .tilde,
973 => try writeEscaped(out, src[token.loc.start..token.loc.end]),986 => 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(
976 docgen_tokenizer,989 docgen_tokenizer,
977 source_token,990 source_token,
978 "syntax error",991 "syntax error",
lib/std/heap/general_purpose_allocator.zig+27-1
...@@ -98,7 +98,7 @@...@@ -98,7 +98,7 @@
98//! in a `std.HashMap` using the backing allocator.98//! in a `std.HashMap` using the backing allocator.
9999
100const std = @import("std");100const std = @import("std");
101const log = std.log.scoped(.std);101const log = std.log.scoped(.gpa);
102const math = std.math;102const math = std.math;
103const assert = std.debug.assert;103const assert = std.debug.assert;
104const mem = std.mem;104const mem = std.mem;
...@@ -162,6 +162,9 @@ pub const Config = struct {...@@ -162,6 +162,9 @@ pub const Config = struct {
162 /// logged error messages with stack trace details. The downside is that every allocation162 /// logged error messages with stack trace details. The downside is that every allocation
163 /// will be leaked!163 /// will be leaked!
164 never_unmap: bool = false,164 never_unmap: bool = false,
165
166 /// Enables emitting info messages with the size and address of every allocation.
167 verbose_log: bool = false,
165};168};
166169
167pub fn GeneralPurposeAllocator(comptime config: Config) type {170pub fn GeneralPurposeAllocator(comptime config: Config) type {
...@@ -454,10 +457,19 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {...@@ -454,10 +457,19 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
454 const result_len = try self.backing_allocator.resizeFn(self.backing_allocator, old_mem, old_align, new_size, len_align, ret_addr);457 const result_len = try self.backing_allocator.resizeFn(self.backing_allocator, old_mem, old_align, new_size, len_align, ret_addr);
455458
456 if (result_len == 0) {459 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
457 self.large_allocations.removeAssertDiscard(@ptrToInt(old_mem.ptr));464 self.large_allocations.removeAssertDiscard(@ptrToInt(old_mem.ptr));
458 return 0;465 return 0;
459 }466 }
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 }
461 entry.value.bytes = old_mem.ptr[0..result_len];473 entry.value.bytes = old_mem.ptr[0..result_len];
462 collectStackTrace(ret_addr, &entry.value.stack_addresses);474 collectStackTrace(ret_addr, &entry.value.stack_addresses);
463 return result_len;475 return result_len;
...@@ -568,6 +580,9 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {...@@ -568,6 +580,9 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
568 } else {580 } else {
569 @memset(old_mem.ptr, undefined, old_mem.len);581 @memset(old_mem.ptr, undefined, old_mem.len);
570 }582 }
583 if (config.verbose_log) {
584 log.info("small free {d} bytes at {*}", .{ old_mem.len, old_mem.ptr });
585 }
571 return @as(usize, 0);586 return @as(usize, 0);
572 }587 }
573 const new_aligned_size = math.max(new_size, old_align);588 const new_aligned_size = math.max(new_size, old_align);
...@@ -576,6 +591,11 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {...@@ -576,6 +591,11 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
576 if (old_mem.len > new_size) {591 if (old_mem.len > new_size) {
577 @memset(old_mem.ptr + new_size, undefined, old_mem.len - new_size);592 @memset(old_mem.ptr + new_size, undefined, old_mem.len - new_size);
578 }593 }
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 }
579 return new_size;599 return new_size;
580 }600 }
581 return error.OutOfMemory;601 return error.OutOfMemory;
...@@ -623,6 +643,9 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {...@@ -623,6 +643,9 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
623 gop.entry.value.bytes = slice;643 gop.entry.value.bytes = slice;
624 collectStackTrace(ret_addr, &gop.entry.value.stack_addresses);644 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 }
626 return slice;649 return slice;
627 }650 }
628651
...@@ -632,6 +655,9 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {...@@ -632,6 +655,9 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
632655
633 const new_size_class = math.ceilPowerOfTwoAssert(usize, new_aligned_size);656 const new_size_class = math.ceilPowerOfTwoAssert(usize, new_aligned_size);
634 const ptr = try self.allocSlot(new_size_class, ret_addr);657 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 }
635 return ptr[0..len];661 return ptr[0..len];
636 }662 }
637663
lib/std/io.zig-3
...@@ -142,9 +142,6 @@ pub const bitReader = @import("io/bit_reader.zig").bitReader;...@@ -142,9 +142,6 @@ pub const bitReader = @import("io/bit_reader.zig").bitReader;
142pub const BitWriter = @import("io/bit_writer.zig").BitWriter;142pub const BitWriter = @import("io/bit_writer.zig").BitWriter;
143pub const bitWriter = @import("io/bit_writer.zig").bitWriter;143pub 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
148pub const ChangeDetectionStream = @import("io/change_detection_stream.zig").ChangeDetectionStream;145pub const ChangeDetectionStream = @import("io/change_detection_stream.zig").ChangeDetectionStream;
149pub const changeDetectionStream = @import("io/change_detection_stream.zig").changeDetectionStream;146pub 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...@@ -20,6 +20,7 @@ pub const ComptimeStringMap = @import("comptime_string_map.zig").ComptimeStringM
20pub const DynLib = @import("dynamic_library.zig").DynLib;20pub const DynLib = @import("dynamic_library.zig").DynLib;
21pub const HashMap = hash_map.HashMap;21pub const HashMap = hash_map.HashMap;
22pub const HashMapUnmanaged = hash_map.HashMapUnmanaged;22pub const HashMapUnmanaged = hash_map.HashMapUnmanaged;
23pub const MultiArrayList = @import("multi_array_list.zig").MultiArrayList;
23pub const PackedIntArray = @import("packed_int_array.zig").PackedIntArray;24pub const PackedIntArray = @import("packed_int_array.zig").PackedIntArray;
24pub const PackedIntArrayEndian = @import("packed_int_array.zig").PackedIntArrayEndian;25pub const PackedIntArrayEndian = @import("packed_int_array.zig").PackedIntArrayEndian;
25pub const PackedIntSlice = @import("packed_int_array.zig").PackedIntSlice;26pub 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;...@@ -12,7 +12,6 @@ pub const fmtId = @import("zig/fmt.zig").fmtId;
12pub const fmtEscapes = @import("zig/fmt.zig").fmtEscapes;12pub const fmtEscapes = @import("zig/fmt.zig").fmtEscapes;
13pub const parse = @import("zig/parse.zig").parse;13pub const parse = @import("zig/parse.zig").parse;
14pub const parseStringLiteral = @import("zig/string_literal.zig").parse;14pub const parseStringLiteral = @import("zig/string_literal.zig").parse;
15pub const render = @import("zig/render.zig").render;
16pub const ast = @import("zig/ast.zig");15pub const ast = @import("zig/ast.zig");
17pub const system = @import("zig/system.zig");16pub const system = @import("zig/system.zig");
18pub const CrossTarget = @import("zig/cross_target.zig").CrossTarget;17pub const CrossTarget = @import("zig/cross_target.zig").CrossTarget;
lib/std/zig/ast.zig+2754-3074
...@@ -9,71 +9,78 @@ const testing = std.testing;...@@ -9,71 +9,78 @@ const testing = std.testing;
9const mem = std.mem;9const mem = std.mem;
10const Token = std.zig.Token;10const Token = std.zig.Token;
1111
12pub const TokenIndex = usize;12pub const TokenIndex = u32;
13pub const NodeIndex = usize;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
15pub const Tree = struct {21pub const Tree = struct {
16 /// Reference to externally-owned data.22 /// Reference to externally-owned data.
17 source: []const u8,23 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,25 tokens: TokenList.Slice,
24 gpa: *mem.Allocator,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 newlines31 errors: []const Error,
27 /// TODO get rid of this hack
28 generated: bool = false,
2932
30 pub fn deinit(self: *Tree) void {33 pub const Location = struct {
31 self.gpa.free(self.token_ids);34 line: usize,
32 self.gpa.free(self.token_locs);35 column: usize,
33 self.gpa.free(self.errors);36 line_start: usize,
34 self.arena.promote(self.gpa).deinit();37 line_end: usize,
35 }38 };
3639
37 pub fn renderError(self: *Tree, parse_error: *const Error, stream: anytype) !void {40 pub fn deinit(tree: *Tree, gpa: *mem.Allocator) void {
38 return parse_error.render(self.token_ids, stream);41 tree.tokens.deinit(gpa);
42 tree.nodes.deinit(gpa);
43 gpa.free(tree.extra_data);
44 gpa.free(tree.errors);
45 tree.* = undefined;
39 }46 }
4047
41 pub fn tokenSlice(self: *Tree, token_index: TokenIndex) []const u8 {48 pub const RenderError = error{
42 return self.tokenSliceLoc(self.token_locs[token_index]);49 /// Ran out of memory allocating call stack frames to complete rendering, or
43 }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 {54 /// `gpa` is used for allocating the resulting formatted source code, as well as
46 return self.source[token.start..token.end];55 /// for allocating extra stack memory if needed, because this function utilizes recursion.
47 }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 {62 try tree.renderToArrayList(&buffer);
50 const first_token = self.token_locs[node.firstToken()];63 return buffer.toOwnedSlice();
51 const last_token = self.token_locs[node.lastToken()];
52 return self.source[first_token.start..last_token.end];
53 }64 }
5465
55 pub const Location = struct {66 pub fn renderToArrayList(tree: Tree, buffer: *std.ArrayList(u8)) RenderError!void {
56 line: usize,67 return @import("./render.zig").renderTree(buffer, tree);
57 column: usize,68 }
58 line_start: usize,
59 line_end: usize,
60 };
6169
62 /// Return the Location of the token relative to the offset specified by `start_index`.70 pub fn tokenLocation(self: Tree, start_offset: ByteOffset, token_index: TokenIndex) Location {
63 pub fn tokenLocationLoc(self: *Tree, start_index: usize, token: Token.Loc) Location {
64 var loc = Location{71 var loc = Location{
65 .line = 0,72 .line = 0,
66 .column = 0,73 .column = 0,
67 .line_start = start_index,74 .line_start = start_offset,
68 .line_end = self.source.len,75 .line_end = self.source.len,
69 };76 };
70 if (self.generated)77 const token_start = self.tokens.items(.start)[token_index];
71 return loc;78 for (self.source[start_offset..]) |c, i| {
72 const token_start = token.start;79 if (i + start_offset == token_start) {
73 for (self.source[start_index..]) |c, i| {80 loc.line_end = i + start_offset;
74 if (i + start_index == token_start) {81 while (loc.line_end < self.source.len and self.source[loc.line_end] != '\n') {
75 loc.line_end = i + start_index;82 loc.line_end += 1;
76 while (loc.line_end < self.source.len and self.source[loc.line_end] != '\n') : (loc.line_end += 1) {}83 }
77 return loc;84 return loc;
78 }85 }
79 if (c == '\n') {86 if (c == '\n') {
...@@ -87,3205 +94,2878 @@ pub const Tree = struct {...@@ -87,3205 +94,2878 @@ pub const Tree = struct {
87 return loc;94 return loc;
88 }95 }
8996
90 pub fn tokenLocation(self: *Tree, start_index: usize, token_index: TokenIndex) Location {97 pub fn tokenSlice(tree: Tree, token_index: TokenIndex) []const u8 {
91 return self.tokenLocationLoc(start_index, self.token_locs[token_index]);98 const token_starts = tree.tokens.items(.start);
92 }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 {107 // For some tokens, re-tokenization is needed to find the end.
95 return self.tokensOnSameLineLoc(self.token_locs[token1_index], self.token_locs[token2_index]);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];
96 }116 }
97117
98 pub fn tokensOnSameLineLoc(self: *Tree, token1: Token.Loc, token2: Token.Loc) bool {118 pub fn extraData(tree: Tree, index: usize, comptime T: type) T {
99 return mem.indexOfScalar(u8, self.source[token1.end..token2.start], '\n') == null;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;
100 }126 }
101127
102 pub fn dump(self: *Tree) void {128 pub fn rootDecls(tree: Tree) []const Node.Index {
103 self.root_node.base.dump(0);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];
104 }132 }
105133
106 /// Skips over comments134 pub fn renderError(tree: Tree, parse_error: Error, stream: anytype) !void {
107 pub fn prevToken(self: *Tree, token_index: TokenIndex) TokenIndex {135 const token_tags = tree.tokens.items(.tag);
108 var index = token_index - 1;136 switch (parse_error.tag) {
109 while (self.token_ids[index] == Token.Id.LineComment) {137 .asterisk_after_ptr_deref => {
110 index -= 1;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 },
111 }311 }
112 return index;
113 }312 }
114313
115 /// Skips over comments314 pub fn firstToken(tree: Tree, node: Node.Index) TokenIndex {
116 pub fn nextToken(self: *Tree, token_index: TokenIndex) TokenIndex {315 const tags = tree.nodes.items(.tag);
117 var index = token_index + 1;316 const datas = tree.nodes.items(.data);
118 while (self.token_ids[index] == Token.Id.LineComment) {317 const main_tokens = tree.nodes.items(.main_token);
119 index += 1;318 const token_tags = tree.tokens.items(.tag);
120 }319 var end_offset: TokenIndex = 0;
121 return index;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 };
122 }609 }
123};
124610
125pub const Error = union(enum) {611 pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {
126 InvalidToken: InvalidToken,612 const tags = tree.nodes.items(.tag);
127 ExpectedContainerMembers: ExpectedContainerMembers,613 const datas = tree.nodes.items(.data);
128 ExpectedStringLiteral: ExpectedStringLiteral,614 const main_tokens = tree.nodes.items(.main_token);
129 ExpectedIntegerLiteral: ExpectedIntegerLiteral,615 const token_starts = tree.tokens.items(.start);
130 ExpectedPubItem: ExpectedPubItem,616 const token_tags = tree.tokens.items(.tag);
131 ExpectedIdentifier: ExpectedIdentifier,617 var n = node;
132 ExpectedStatement: ExpectedStatement,618 var end_offset: TokenIndex = 0;
133 ExpectedVarDeclOrFn: ExpectedVarDeclOrFn,619 while (true) switch (tags[n]) {
134 ExpectedVarDecl: ExpectedVarDecl,620 .root => return @intCast(TokenIndex, tree.tokens.len - 1),
135 ExpectedFn: ExpectedFn,621
136 ExpectedReturnType: ExpectedReturnType,622 .@"usingnamespace",
137 ExpectedAggregateKw: ExpectedAggregateKw,623 .bool_not,
138 UnattachedDocComment: UnattachedDocComment,624 .negation,
139 ExpectedEqOrSemi: ExpectedEqOrSemi,625 .bit_not,
140 ExpectedSemiOrLBrace: ExpectedSemiOrLBrace,626 .negation_wrap,
141 ExpectedSemiOrElse: ExpectedSemiOrElse,627 .address_of,
142 ExpectedLabelOrLBrace: ExpectedLabelOrLBrace,628 .@"try",
143 ExpectedLBrace: ExpectedLBrace,629 .@"await",
144 ExpectedColonOrRParen: ExpectedColonOrRParen,630 .optional_type,
145 ExpectedLabelable: ExpectedLabelable,631 .@"resume",
146 ExpectedInlinable: ExpectedInlinable,632 .@"nosuspend",
147 ExpectedAsmOutputReturnOrType: ExpectedAsmOutputReturnOrType,633 .@"comptime",
148 ExpectedCall: ExpectedCall,634 => n = datas[n].lhs,
149 ExpectedCallOrFnProto: ExpectedCallOrFnProto,635
150 ExpectedSliceOrRBracket: ExpectedSliceOrRBracket,636 .test_decl,
151 ExtraAlignQualifier: ExtraAlignQualifier,637 .@"errdefer",
152 ExtraConstQualifier: ExtraConstQualifier,638 .@"defer",
153 ExtraVolatileQualifier: ExtraVolatileQualifier,639 .@"catch",
154 ExtraAllowZeroQualifier: ExtraAllowZeroQualifier,640 .equal_equal,
155 ExpectedTypeExpr: ExpectedTypeExpr,641 .bang_equal,
156 ExpectedPrimaryTypeExpr: ExpectedPrimaryTypeExpr,642 .less_than,
157 ExpectedParamType: ExpectedParamType,643 .greater_than,
158 ExpectedExpr: ExpectedExpr,644 .less_or_equal,
159 ExpectedPrimaryExpr: ExpectedPrimaryExpr,645 .greater_or_equal,
160 ExpectedToken: ExpectedToken,646 .assign_mul,
161 ExpectedCommaOrEnd: ExpectedCommaOrEnd,647 .assign_div,
162 ExpectedParamList: ExpectedParamList,648 .assign_mod,
163 ExpectedPayload: ExpectedPayload,649 .assign_add,
164 ExpectedBlockOrAssignment: ExpectedBlockOrAssignment,650 .assign_sub,
165 ExpectedBlockOrExpression: ExpectedBlockOrExpression,651 .assign_bit_shift_left,
166 ExpectedExprOrAssignment: ExpectedExprOrAssignment,652 .assign_bit_shift_right,
167 ExpectedPrefixExpr: ExpectedPrefixExpr,653 .assign_bit_and,
168 ExpectedLoopExpr: ExpectedLoopExpr,654 .assign_bit_xor,
169 ExpectedDerefOrUnwrap: ExpectedDerefOrUnwrap,655 .assign_bit_or,
170 ExpectedSuffixOp: ExpectedSuffixOp,656 .assign_mul_wrap,
171 ExpectedBlockOrField: ExpectedBlockOrField,657 .assign_add_wrap,
172 DeclBetweenFields: DeclBetweenFields,658 .assign_sub_wrap,
173 InvalidAnd: InvalidAnd,659 .assign,
174 AsteriskAfterPointerDereference: AsteriskAfterPointerDereference,660 .merge_error_sets,
175661 .mul,
176 pub fn render(self: *const Error, tokens: []const Token.Id, stream: anytype) !void {662 .div,
177 switch (self.*) {663 .mod,
178 .InvalidToken => |*x| return x.render(tokens, stream),664 .array_mult,
179 .ExpectedContainerMembers => |*x| return x.render(tokens, stream),665 .mul_wrap,
180 .ExpectedStringLiteral => |*x| return x.render(tokens, stream),666 .add,
181 .ExpectedIntegerLiteral => |*x| return x.render(tokens, stream),667 .sub,
182 .ExpectedPubItem => |*x| return x.render(tokens, stream),668 .array_cat,
183 .ExpectedIdentifier => |*x| return x.render(tokens, stream),669 .add_wrap,
184 .ExpectedStatement => |*x| return x.render(tokens, stream),670 .sub_wrap,
185 .ExpectedVarDeclOrFn => |*x| return x.render(tokens, stream),671 .bit_shift_left,
186 .ExpectedVarDecl => |*x| return x.render(tokens, stream),672 .bit_shift_right,
187 .ExpectedFn => |*x| return x.render(tokens, stream),673 .bit_and,
188 .ExpectedReturnType => |*x| return x.render(tokens, stream),674 .bit_xor,
189 .ExpectedAggregateKw => |*x| return x.render(tokens, stream),675 .bit_or,
190 .UnattachedDocComment => |*x| return x.render(tokens, stream),676 .@"orelse",
191 .ExpectedEqOrSemi => |*x| return x.render(tokens, stream),677 .bool_and,
192 .ExpectedSemiOrLBrace => |*x| return x.render(tokens, stream),678 .bool_or,
193 .ExpectedSemiOrElse => |*x| return x.render(tokens, stream),679 .anyframe_type,
194 .ExpectedLabelOrLBrace => |*x| return x.render(tokens, stream),680 .error_union,
195 .ExpectedLBrace => |*x| return x.render(tokens, stream),681 .if_simple,
196 .ExpectedColonOrRParen => |*x| return x.render(tokens, stream),682 .while_simple,
197 .ExpectedLabelable => |*x| return x.render(tokens, stream),683 .for_simple,
198 .ExpectedInlinable => |*x| return x.render(tokens, stream),684 .fn_proto_simple,
199 .ExpectedAsmOutputReturnOrType => |*x| return x.render(tokens, stream),685 .fn_proto_multi,
200 .ExpectedCall => |*x| return x.render(tokens, stream),686 .ptr_type_aligned,
201 .ExpectedCallOrFnProto => |*x| return x.render(tokens, stream),687 .ptr_type_sentinel,
202 .ExpectedSliceOrRBracket => |*x| return x.render(tokens, stream),688 .ptr_type,
203 .ExtraAlignQualifier => |*x| return x.render(tokens, stream),689 .ptr_type_bit_range,
204 .ExtraConstQualifier => |*x| return x.render(tokens, stream),690 .array_type,
205 .ExtraVolatileQualifier => |*x| return x.render(tokens, stream),691 .switch_case_one,
206 .ExtraAllowZeroQualifier => |*x| return x.render(tokens, stream),692 .switch_case,
207 .ExpectedTypeExpr => |*x| return x.render(tokens, stream),693 .switch_range,
208 .ExpectedPrimaryTypeExpr => |*x| return x.render(tokens, stream),694 => n = datas[n].rhs,
209 .ExpectedParamType => |*x| return x.render(tokens, stream),695
210 .ExpectedExpr => |*x| return x.render(tokens, stream),696 .field_access,
211 .ExpectedPrimaryExpr => |*x| return x.render(tokens, stream),697 .unwrap_optional,
212 .ExpectedToken => |*x| return x.render(tokens, stream),698 .grouped_expression,
213 .ExpectedCommaOrEnd => |*x| return x.render(tokens, stream),699 .multiline_string_literal,
214 .ExpectedParamList => |*x| return x.render(tokens, stream),700 .error_set_decl,
215 .ExpectedPayload => |*x| return x.render(tokens, stream),701 .asm_simple,
216 .ExpectedBlockOrAssignment => |*x| return x.render(tokens, stream),702 .asm_output,
217 .ExpectedBlockOrExpression => |*x| return x.render(tokens, stream),703 .asm_input,
218 .ExpectedExprOrAssignment => |*x| return x.render(tokens, stream),704 .error_value,
219 .ExpectedPrefixExpr => |*x| return x.render(tokens, stream),705 => return datas[n].rhs + end_offset,
220 .ExpectedLoopExpr => |*x| return x.render(tokens, stream),706
221 .ExpectedDerefOrUnwrap => |*x| return x.render(tokens, stream),707 .@"anytype",
222 .ExpectedSuffixOp => |*x| return x.render(tokens, stream),708 .anyframe_literal,
223 .ExpectedBlockOrField => |*x| return x.render(tokens, stream),709 .char_literal,
224 .DeclBetweenFields => |*x| return x.render(tokens, stream),710 .integer_literal,
225 .InvalidAnd => |*x| return x.render(tokens, stream),711 .float_literal,
226 .AsteriskAfterPointerDereference => |*x| return x.render(tokens, stream),712 .false_literal,
227 }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 };
228 }1121 }
2291122
230 pub fn loc(self: *const Error) TokenIndex {1123 pub fn tokensOnSameLine(tree: Tree, token1: TokenIndex, token2: TokenIndex) bool {
231 switch (self.*) {1124 const token_starts = tree.tokens.items(.start);
232 .InvalidToken => |x| return x.token,1125 const source = tree.source[token_starts[token1]..token_starts[token2]];
233 .ExpectedContainerMembers => |x| return x.token,1126 return mem.indexOfScalar(u8, source, '\n') == null;
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 }
282 }1127 }
2831128
284 pub const InvalidToken = SingleTokenError("Invalid token '{s}'");1129 pub fn getNodeSource(tree: Tree, node: Node.Index) []const u8 {
285 pub const ExpectedContainerMembers = SingleTokenError("Expected test, comptime, var decl, or container field, found '{s}'");1130 const token_starts = tree.tokens.items(.start);
286 pub const ExpectedStringLiteral = SingleTokenError("Expected string literal, found '{s}'");1131 const first_token = tree.firstToken(node);
287 pub const ExpectedIntegerLiteral = SingleTokenError("Expected integer literal, found '{s}'");1132 const last_token = tree.lastToken(node);
288 pub const ExpectedIdentifier = SingleTokenError("Expected identifier, found '{s}'");1133 const start = token_starts[first_token];
289 pub const ExpectedStatement = SingleTokenError("Expected statement, found '{s}'");1134 const end = token_starts[last_token] + tree.tokenSlice(last_token).len;
290 pub const ExpectedVarDeclOrFn = SingleTokenError("Expected variable declaration or function, found '{s}'");1135 return tree.source[start..end];
291 pub const ExpectedVarDecl = SingleTokenError("Expected variable declaration, found '{s}'");1136 }
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 };
3401137
341 pub const ExpectedCallOrFnProto = struct {1138 pub fn globalVarDecl(tree: Tree, node: Node.Index) full.VarDecl {
342 node: *Node,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 {1151 pub fn localVarDecl(tree: Tree, node: Node.Index) full.VarDecl {
345 return stream.print("expected " ++ @tagName(Node.Tag.Call) ++ " or " ++1152 assert(tree.nodes.items(.tag)[node] == .local_var_decl);
346 @tagName(Node.Tag.FnProto) ++ ", found {s}", .{@tagName(self.node.tag)});1153 const data = tree.nodes.items(.data)[node];
347 }1154 const extra = tree.extraData(data.lhs, Node.LocalVarDecl);
348 };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 {1164 pub fn simpleVarDecl(tree: Tree, node: Node.Index) full.VarDecl {
351 token: TokenIndex,1165 assert(tree.nodes.items(.tag)[node] == .simple_var_decl);
352 expected_id: Token.Id,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 {1176 pub fn alignedVarDecl(tree: Tree, node: Node.Index) full.VarDecl {
355 const found_token = tokens[self.token];1177 assert(tree.nodes.items(.tag)[node] == .aligned_var_decl);
356 switch (found_token) {1178 const data = tree.nodes.items(.data)[node];
357 .Invalid => {1179 return tree.fullVarDecl(.{
358 return stream.print("expected '{s}', found invalid bytes", .{self.expected_id.symbol()});1180 .type_node = 0,
359 },1181 .align_node = data.lhs,
360 else => {1182 .section_node = 0,
361 const token_name = found_token.symbol();1183 .init_node = data.rhs,
362 return stream.print("expected '{s}', found '{s}'", .{ self.expected_id.symbol(), token_name });1184 .mut_token = tree.nodes.items(.main_token)[node],
363 },1185 });
364 }1186 }
365 }
366 };
3671187
368 pub const ExpectedCommaOrEnd = struct {1188 pub fn ifSimple(tree: Tree, node: Node.Index) full.If {
369 token: TokenIndex,1189 assert(tree.nodes.items(.tag)[node] == .if_simple);
370 end_id: Token.Id,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 {1199 pub fn ifFull(tree: Tree, node: Node.Index) full.If {
373 const actual_token = tokens[self.token];1200 assert(tree.nodes.items(.tag)[node] == .@"if");
374 return stream.print("expected ',' or '{s}', found '{s}'", .{1201 const data = tree.nodes.items(.data)[node];
375 self.end_id.symbol(),1202 const extra = tree.extraData(data.rhs, Node.If);
376 actual_token.symbol(),1203 return tree.fullIf(.{
377 });1204 .cond_expr = data.lhs,
378 }1205 .then_expr = extra.then_expr,
379 };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 {1211 pub fn containerField(tree: Tree, node: Node.Index) full.ContainerField {
382 return struct {1212 assert(tree.nodes.items(.tag)[node] == .container_field);
383 const ThisError = @This();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 {1234 pub fn containerFieldAlign(tree: Tree, node: Node.Index) full.ContainerField {
388 const actual_token = tokens[self.token];1235 assert(tree.nodes.items(.tag)[node] == .container_field_align);
389 return stream.print(msg, .{actual_token.symbol()});1236 const data = tree.nodes.items(.data)[node];
390 }1237 return tree.fullContainerField(.{
391 };1238 .name_token = tree.nodes.items(.main_token)[node],
1239 .type_expr = data.lhs,
1240 .value_expr = 0,
1241 .align_expr = data.rhs,
1242 });
392 }1243 }
3931244
394 fn SimpleError(comptime msg: []const u8) type {1245 pub fn fnProtoSimple(tree: Tree, buffer: *[1]Node.Index, node: Node.Index) full.FnProto {
395 return struct {1246 assert(tree.nodes.items(.tag)[node] == .fn_proto_simple);
396 const ThisError = @This();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 {1275 pub fn fnProtoOne(tree: Tree, buffer: *[1]Node.Index, node: Node.Index) full.FnProto {
401 return stream.writeAll(msg);1276 assert(tree.nodes.items(.tag)[node] == .fn_proto_one);
402 }1277 const data = tree.nodes.items(.data)[node];
403 };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 });
404 }1289 }
405};
4061290
407pub const Node = struct {1291 pub fn fnProto(tree: Tree, node: Node.Index) full.FnProto {
408 tag: Tag,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 {1306 pub fn structInitOne(tree: Tree, buffer: *[1]Node.Index, node: Node.Index) full.StructInit {
411 // Top level1307 assert(tree.nodes.items(.tag)[node] == .struct_init_one or
412 Root,1308 tree.nodes.items(.tag)[node] == .struct_init_one_comma);
413 Use,1309 const data = tree.nodes.items(.data)[node];
414 TestDecl,1310 buffer[0] = data.rhs;
4151311 const fields = if (data.rhs == 0) buffer[0..0] else buffer[0..1];
416 // Statements1312 return tree.fullStructInit(.{
417 VarDecl,1313 .lbrace = tree.nodes.items(.main_token)[node],
418 Defer,1314 .fields = fields,
4191315 .type_expr = data.lhs,
420 // Infix operators1316 });
421 Catch,1317 }
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 }
6801318
681 pub fn isBlock(tag: Tag) bool {1319 pub fn structInitDotTwo(tree: Tree, buffer: *[2]Node.Index, node: Node.Index) full.StructInit {
682 return switch (tag) {1320 assert(tree.nodes.items(.tag)[node] == .struct_init_dot_two or
683 .Block, .LabeledBlock => true,1321 tree.nodes.items(.tag)[node] == .struct_init_dot_two_comma);
684 else => false,1322 const data = tree.nodes.items(.data)[node];
685 };1323 buffer.* = .{ data.lhs, data.rhs };
686 }1324 const fields = if (data.rhs != 0)
687 };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.1337 pub fn structInitDot(tree: Tree, node: Node.Index) full.StructInit {
690 pub fn cast(base: *Node, comptime T: type) ?*T {1338 assert(tree.nodes.items(.tag)[node] == .struct_init_dot or
691 if (std.meta.fieldInfo(T, .base).default_value) |default_base| {1339 tree.nodes.items(.tag)[node] == .struct_init_dot_comma);
692 return base.castTag(default_base.tag);1340 const data = tree.nodes.items(.data)[node];
693 }1341 return tree.fullStructInit(.{
694 inline for (@typeInfo(Tag).Enum.fields) |field| {1342 .lbrace = tree.nodes.items(.main_token)[node],
695 const tag = @intToEnum(Tag, field.value);1343 .fields = tree.extra_data[data.lhs..data.rhs],
696 if (base.tag == tag) {1344 .type_expr = 0,
697 if (T == tag.Type()) {1345 });
698 return @fieldParentPtr(T, "base", base);
699 }
700 return null;
701 }
702 }
703 unreachable;
704 }1346 }
7051347
706 pub fn castTag(base: *Node, comptime tag: Tag) ?*tag.Type() {1348 pub fn structInit(tree: Tree, node: Node.Index) full.StructInit {
707 if (base.tag == tag) {1349 assert(tree.nodes.items(.tag)[node] == .struct_init or
708 return @fieldParentPtr(tag.Type(), "base", base);1350 tree.nodes.items(.tag)[node] == .struct_init_comma);
709 }1351 const data = tree.nodes.items(.data)[node];
710 return null;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 });
711 }1358 }
7121359
713 pub fn iterate(base: *Node, index: usize) ?*Node {1360 pub fn arrayInitOne(tree: Tree, buffer: *[1]Node.Index, node: Node.Index) full.ArrayInit {
714 inline for (@typeInfo(Tag).Enum.fields) |field| {1361 assert(tree.nodes.items(.tag)[node] == .array_init_one or
715 const tag = @intToEnum(Tag, field.value);1362 tree.nodes.items(.tag)[node] == .array_init_one_comma);
716 if (base.tag == tag) {1363 const data = tree.nodes.items(.data)[node];
717 return @fieldParentPtr(tag.Type(), "base", base).iterate(index);1364 buffer[0] = data.rhs;
718 }1365 const elements = if (data.rhs == 0) buffer[0..0] else buffer[0..1];
719 }1366 return .{
720 unreachable;1367 .ast = .{
1368 .lbrace = tree.nodes.items(.main_token)[node],
1369 .elements = elements,
1370 .type_expr = data.lhs,
1371 },
1372 };
721 }1373 }
7221374
723 pub fn firstToken(base: *const Node) TokenIndex {1375 pub fn arrayInitDotTwo(tree: Tree, buffer: *[2]Node.Index, node: Node.Index) full.ArrayInit {
724 inline for (@typeInfo(Tag).Enum.fields) |field| {1376 assert(tree.nodes.items(.tag)[node] == .array_init_dot_two or
725 const tag = @intToEnum(Tag, field.value);1377 tree.nodes.items(.tag)[node] == .array_init_dot_two_comma);
726 if (base.tag == tag) {1378 const data = tree.nodes.items(.data)[node];
727 return @fieldParentPtr(tag.Type(), "base", base).firstToken();1379 buffer.* = .{ data.lhs, data.rhs };
728 }1380 const elements = if (data.rhs != 0)
729 }1381 buffer[0..2]
730 unreachable;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 };
731 }1393 }
7321394
733 pub fn lastToken(base: *const Node) TokenIndex {1395 pub fn arrayInitDot(tree: Tree, node: Node.Index) full.ArrayInit {
734 inline for (@typeInfo(Tag).Enum.fields) |field| {1396 assert(tree.nodes.items(.tag)[node] == .array_init_dot or
735 const tag = @intToEnum(Tag, field.value);1397 tree.nodes.items(.tag)[node] == .array_init_dot_comma);
736 if (base.tag == tag) {1398 const data = tree.nodes.items(.data)[node];
737 return @fieldParentPtr(tag.Type(), "base", base).lastToken();1399 return .{
738 }1400 .ast = .{
739 }1401 .lbrace = tree.nodes.items(.main_token)[node],
740 unreachable;1402 .elements = tree.extra_data[data.lhs..data.rhs],
741 }1403 .type_expr = 0,
7421404 },
743 pub fn requireSemiColon(base: *const Node) bool {1405 };
744 var n = base;1406 }
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 }
7681407
769 return !while_node.body.tag.isBlock();1408 pub fn arrayInit(tree: Tree, node: Node.Index) full.ArrayInit {
770 },1409 assert(tree.nodes.items(.tag)[node] == .array_init or
771 .For => {1410 tree.nodes.items(.tag)[node] == .array_init_comma);
772 const for_node = @fieldParentPtr(For, "base", n);1411 const data = tree.nodes.items(.data)[node];
773 if (for_node.@"else") |@"else"| {1412 const elem_range = tree.extraData(data.rhs, Node.SubRange);
774 n = &@"else".base;1413 return .{
775 continue;1414 .ast = .{
776 }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();1422 pub fn arrayType(tree: Tree, node: Node.Index) full.ArrayType {
779 },1423 assert(tree.nodes.items(.tag)[node] == .array_type);
780 .If => {1424 const data = tree.nodes.items(.data)[node];
781 const if_node = @fieldParentPtr(If, "base", n);1425 return .{
782 if (if_node.@"else") |@"else"| {1426 .ast = .{
783 n = &@"else".base;1427 .lbracket = tree.nodes.items(.main_token)[node],
784 continue;1428 .elem_count = data.lhs,
785 }1429 .sentinel = null,
1430 .elem_type = data.rhs,
1431 },
1432 };
1433 }
7861434
787 return !if_node.body.tag.isBlock();1435 pub fn arrayTypeSentinel(tree: Tree, node: Node.Index) full.ArrayType {
788 },1436 assert(tree.nodes.items(.tag)[node] == .array_type_sentinel);
789 .Else => {1437 const data = tree.nodes.items(.data)[node];
790 const else_node = @fieldParentPtr(Else, "base", n);1438 const extra = tree.extraData(data.rhs, Node.ArrayTypeSentinel);
791 n = else_node.body;1439 return .{
792 continue;1440 .ast = .{
793 },1441 .lbracket = tree.nodes.items(.main_token)[node],
794 .Defer => {1442 .elem_count = data.lhs,
795 const defer_node = @fieldParentPtr(Defer, "base", n);1443 .sentinel = extra.sentinel,
796 return !defer_node.expr.tag.isBlock();1444 .elem_type = extra.elem_type,
797 },1445 },
798 .Comptime => {1446 };
799 const comptime_node = @fieldParentPtr(Comptime, "base", n);1447 }
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 }
8071448
808 return true;1449 pub fn ptrTypeAligned(tree: Tree, node: Node.Index) full.PtrType {
809 },1450 assert(tree.nodes.items(.tag)[node] == .ptr_type_aligned);
810 .Nosuspend => {1451 const data = tree.nodes.items(.data)[node];
811 const nosuspend_node = @fieldParentPtr(Nosuspend, "base", n);1452 return tree.fullPtrType(.{
812 return !nosuspend_node.expr.tag.isBlock();1453 .main_token = tree.nodes.items(.main_token)[node],
813 },1454 .align_node = data.lhs,
814 else => return true,1455 .sentinel = 0,
815 }1456 .bit_range_start = 0,
816 }1457 .bit_range_end = 0,
1458 .child_type = data.rhs,
1459 });
817 }1460 }
8181461
819 /// Asserts the node is a Block or LabeledBlock and returns the statements slice.1462 pub fn ptrTypeSentinel(tree: Tree, node: Node.Index) full.PtrType {
820 pub fn blockStatements(base: *Node) []*Node {1463 assert(tree.nodes.items(.tag)[node] == .ptr_type_sentinel);
821 if (base.castTag(.Block)) |block| {1464 const data = tree.nodes.items(.data)[node];
822 return block.statements();1465 return tree.fullPtrType(.{
823 } else if (base.castTag(.LabeledBlock)) |labeled_block| {1466 .main_token = tree.nodes.items(.main_token)[node],
824 return labeled_block.statements();1467 .align_node = 0,
825 } else {1468 .sentinel = data.lhs,
826 unreachable;1469 .bit_range_start = 0,
827 }1470 .bit_range_end = 0,
1471 .child_type = data.rhs,
1472 });
828 }1473 }
8291474
830 pub fn findFirstWithId(self: *Node, id: Id) ?*Node {1475 pub fn ptrType(tree: Tree, node: Node.Index) full.PtrType {
831 if (self.id == id) return self;1476 assert(tree.nodes.items(.tag)[node] == .ptr_type);
832 var child_i: usize = 0;1477 const data = tree.nodes.items(.data)[node];
833 while (self.iterate(child_i)) |child| : (child_i += 1) {1478 const extra = tree.extraData(data.lhs, Node.PtrType);
834 if (child.findFirstWithId(id)) |result| return result;1479 return tree.fullPtrType(.{
835 }1480 .main_token = tree.nodes.items(.main_token)[node],
836 return null;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 });
837 }1487 }
8381488
839 pub fn dump(self: *Node, indent: usize) void {1489 pub fn ptrTypeBitRange(tree: Tree, node: Node.Index) full.PtrType {
840 {1490 assert(tree.nodes.items(.tag)[node] == .ptr_type_bit_range);
841 var i: usize = 0;1491 const data = tree.nodes.items(.data)[node];
842 while (i < indent) : (i += 1) {1492 const extra = tree.extraData(data.lhs, Node.PtrTypeBitRange);
843 std.debug.warn(" ", .{});1493 return tree.fullPtrType(.{
844 }1494 .main_token = tree.nodes.items(.main_token)[node],
845 }1495 .align_node = extra.align_node,
846 std.debug.warn("{s}\n", .{@tagName(self.tag)});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;1503 pub fn sliceOpen(tree: Tree, node: Node.Index) full.Slice {
849 while (self.iterate(child_i)) |child| : (child_i += 1) {1504 assert(tree.nodes.items(.tag)[node] == .slice_open);
850 child.dump(indent + 2);1505 const data = tree.nodes.items(.data)[node];
851 }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 };
852 }1515 }
8531516
854 /// The decls data follows this struct in memory as an array of Node pointers.1517 pub fn slice(tree: Tree, node: Node.Index) full.Slice {
855 pub const Root = struct {1518 assert(tree.nodes.items(.tag)[node] == .slice);
856 base: Node = Node{ .tag = .Root },1519 const data = tree.nodes.items(.data)[node];
857 eof_token: TokenIndex,1520 const extra = tree.extraData(data.rhs, Node.Slice);
858 decls_len: NodeIndex,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.1532 pub fn sliceSentinel(tree: Tree, node: Node.Index) full.Slice {
861 pub fn create(allocator: *mem.Allocator, decls_len: NodeIndex, eof_token: TokenIndex) !*Root {1533 assert(tree.nodes.items(.tag)[node] == .slice_sentinel);
862 const bytes = try allocator.alignedAlloc(u8, @alignOf(Root), sizeInBytes(decls_len));1534 const data = tree.nodes.items(.data)[node];
863 const self = @ptrCast(*Root, bytes.ptr);1535 const extra = tree.extraData(data.rhs, Node.SliceSentinel);
864 self.* = .{1536 return .{
865 .eof_token = eof_token,1537 .ast = .{
866 .decls_len = decls_len,1538 .sliced = data.lhs,
867 };1539 .lbracket = tree.nodes.items(.main_token)[node],
868 return self;1540 .start = extra.start,
869 }1541 .end = extra.end,
1542 .sentinel = extra.sentinel,
1543 },
1544 };
1545 }
8701546
871 pub fn destroy(self: *Decl, allocator: *mem.Allocator) void {1547 pub fn containerDeclTwo(tree: Tree, buffer: *[2]Node.Index, node: Node.Index) full.ContainerDecl {
872 const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.decls_len)];1548 assert(tree.nodes.items(.tag)[node] == .container_decl_two or
873 allocator.free(bytes);1549 tree.nodes.items(.tag)[node] == .container_decl_two_trailing);
874 }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 {1566 pub fn containerDecl(tree: Tree, node: Node.Index) full.ContainerDecl {
877 var i = index;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];1578 pub fn containerDeclArg(tree: Tree, node: Node.Index) full.ContainerDecl {
880 return null;1579 assert(tree.nodes.items(.tag)[node] == .container_decl_arg or
881 }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 {1591 pub fn taggedUnionTwo(tree: Tree, buffer: *[2]Node.Index, node: Node.Index) full.ContainerDecl {
884 const decls_start = @ptrCast([*]u8, self) + @sizeOf(Root);1592 assert(tree.nodes.items(.tag)[node] == .tagged_union_two or
885 return @ptrCast([*]*Node, decls_start)[0..self.decls_len];1593 tree.nodes.items(.tag)[node] == .tagged_union_two_trailing);
886 }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 {1611 pub fn taggedUnion(tree: Tree, node: Node.Index) full.ContainerDecl {
889 const decls_start = @ptrCast([*]const u8, self) + @sizeOf(Root);1612 assert(tree.nodes.items(.tag)[node] == .tagged_union or
890 return @ptrCast([*]const *Node, decls_start)[0..self.decls_len];1613 tree.nodes.items(.tag)[node] == .tagged_union_trailing);
891 }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 {1624 pub fn taggedUnionEnumTag(tree: Tree, node: Node.Index) full.ContainerDecl {
894 if (self.decls_len == 0) return self.eof_token;1625 assert(tree.nodes.items(.tag)[node] == .tagged_union_enum_tag or
895 return self.declsConst()[0].firstToken();1626 tree.nodes.items(.tag)[node] == .tagged_union_enum_tag_trailing);
896 }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 {1638 pub fn switchCaseOne(tree: Tree, node: Node.Index) full.SwitchCase {
899 if (self.decls_len == 0) return self.eof_token;1639 const data = &tree.nodes.items(.data)[node];
900 return self.declsConst()[self.decls_len - 1].lastToken();1640 const values: *[1]Node.Index = &data.lhs;
901 }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 {1648 pub fn switchCase(tree: Tree, node: Node.Index) full.SwitchCase {
904 return @sizeOf(Root) + @sizeOf(*Node) * @as(usize, decls_len);1649 const data = tree.nodes.items(.data)[node];
905 }1650 const extra = tree.extraData(data.lhs, Node.SubRange);
906 };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 thing1658 pub fn asmSimple(tree: Tree, node: Node.Index) full.Asm {
909 /// determined by a bit in `trailer_flags`.1659 const data = tree.nodes.items(.data)[node];
910 pub const VarDecl = struct {1660 return tree.fullAsm(.{
911 base: Node = Node{ .tag = .VarDecl },1661 .asm_token = tree.nodes.items(.main_token)[node],
912 trailer_flags: TrailerFlags,1662 .template = data.lhs,
913 mut_token: TokenIndex,1663 .items = &.{},
914 name_token: TokenIndex,1664 .rparen = data.rhs,
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,
929 });1665 });
1666 }
9301667
931 pub fn getDocComments(self: *const VarDecl) ?*DocComment {1668 pub fn asmFull(tree: Tree, node: Node.Index) full.Asm {
932 return self.getTrailer(.doc_comments);1669 const data = tree.nodes.items(.data)[node];
933 }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 {1679 pub fn whileSimple(tree: Tree, node: Node.Index) full.While {
936 self.setTrailer(.doc_comments, value);1680 const data = tree.nodes.items(.data)[node];
937 }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 {1690 pub fn whileCont(tree: Tree, node: Node.Index) full.While {
940 return self.getTrailer(.visib_token);1691 const data = tree.nodes.items(.data)[node];
941 }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 {1702 pub fn whileFull(tree: Tree, node: Node.Index) full.While {
944 self.setTrailer(.visib_token, value);1703 const data = tree.nodes.items(.data)[node];
945 }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 {1714 pub fn forSimple(tree: Tree, node: Node.Index) full.While {
948 return self.getTrailer(.thread_local_token);1715 const data = tree.nodes.items(.data)[node];
949 }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 {1725 pub fn forFull(tree: Tree, node: Node.Index) full.While {
952 self.setTrailer(.thread_local_token, value);1726 const data = tree.nodes.items(.data)[node];
953 }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 {1737 pub fn callOne(tree: Tree, buffer: *[1]Node.Index, node: Node.Index) full.Call {
956 return self.getTrailer(.eq_token);1738 const data = tree.nodes.items(.data)[node];
957 }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 {1748 pub fn callFull(tree: Tree, node: Node.Index) full.Call {
960 self.setTrailer(.eq_token, value);1749 const data = tree.nodes.items(.data)[node];
961 }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 {1758 fn fullVarDecl(tree: Tree, info: full.VarDecl.Ast) full.VarDecl {
964 return self.getTrailer(.comptime_token);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 }
965 }1779 }
1780 return result;
1781 }
9661782
967 pub fn setComptimeToken(self: *VarDecl, value: TokenIndex) void {1783 fn fullIf(tree: Tree, info: full.If.Ast) full.If {
968 self.setTrailer(.comptime_token, value);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;
969 }1796 }
9701797 if (info.else_expr != 0) {
971 pub fn getExternExportToken(self: *const VarDecl) ?TokenIndex {1798 // then_expr else |x|
972 return self.getTrailer(.extern_export_token);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 }
973 }1804 }
1805 return result;
1806 }
9741807
975 pub fn setExternExportToken(self: *VarDecl, value: TokenIndex) void {1808 fn fullContainerField(tree: Tree, info: full.ContainerField.Ast) full.ContainerField {
976 self.setTrailer(.extern_export_token, value);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;
977 }1818 }
1819 return result;
1820 }
9781821
979 pub fn getLibName(self: *const VarDecl) ?*Node {1822 fn fullFnProto(tree: Tree, info: full.FnProto.Ast) full.FnProto {
980 return self.getTrailer(.lib_name);1823 const token_tags = tree.tokens.items(.tag);
981 }1824 var result: full.FnProto = .{
9821825 .ast = info,
983 pub fn setLibName(self: *VarDecl, value: *Node) void {1826 .visib_token = null,
984 self.setTrailer(.lib_name, value);1827 .extern_export_token = null,
985 }1828 .lib_name = null,
9861829 .name_token = null,
987 pub fn getTypeNode(self: *const VarDecl) ?*Node {1830 .lparen = undefined,
988 return self.getTrailer(.type_node);1831 };
989 }1832 var i = info.fn_token;
9901833 while (i > 0) {
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;
2760 i -= 1;1834 i -= 1;
27611835 switch (token_tags[i]) {
2762 return null;1836 .keyword_extern, .keyword_export => result.extern_export_token = i,
2763 }1837 .keyword_pub => result.visib_token = i,
27641838 .string_literal => result.lib_name = i,
2765 pub fn firstToken(self: *const ArrayAccess) TokenIndex {1839 else => break,
2766 return self.lhs.firstToken();1840 }
2767 }1841 }
27681842 const after_fn_token = info.fn_token + 1;
2769 pub fn lastToken(self: *const ArrayAccess) TokenIndex {1843 if (token_tags[after_fn_token] == .identifier) {
2770 return self.rtoken;1844 result.name_token = after_fn_token;
1845 result.lparen = after_fn_token + 1;
1846 } else {
1847 result.lparen = after_fn_token;
2771 }1848 }
2772 };1849 assert(token_tags[result.lparen] == .l_paren);
27731850
2774 pub const SimpleSuffixOp = struct {1851 return result;
2775 base: Node,1852 }
2776 rtoken: TokenIndex,
2777 lhs: *Node,
2778
2779 pub fn iterate(self: *const SimpleSuffixOp, index: usize) ?*Node {
2780 var i = index;
27811853
2782 if (i < 1) return self.lhs;1854 fn fullStructInit(tree: Tree, info: full.StructInit.Ast) full.StructInit {
2783 i -= 1;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 }
2786 }1907 }
1908 return result;
1909 }
27871910
2788 pub fn firstToken(self: *const SimpleSuffixOp) TokenIndex {1911 fn fullContainerDecl(tree: Tree, info: full.ContainerDecl.Ast) full.ContainerDecl {
2789 return self.lhs.firstToken();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 => {},
2790 }1920 }
1921 return result;
1922 }
27911923
2792 pub fn lastToken(self: *const SimpleSuffixOp) TokenIndex {1924 fn fullSwitchCase(tree: Tree, info: full.SwitchCase.Ast) full.SwitchCase {
2793 return self.rtoken;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;
2794 }1932 }
2795 };1933 return result;
27961934 }
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;
28131935
2814 if (self.end) |end| {1936 fn fullAsm(tree: Tree, info: full.Asm.Ast) full.Asm {
2815 if (i < 1) return end;1937 const token_tags = tree.tokens.items(.tag);
2816 i -= 1;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;
2817 }1977 }
2818 if (self.sentinel) |sentinel| {1978 } else {
2819 if (i < 1) return sentinel;1979 // asm ("foo" : [_] "" (x) :: "a", "b");
2820 i -= 1;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;
2821 }1987 }
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;
2856 }1988 }
2857 };
28581989
2859 /// Trailed in memory by possibly many things, with each optional thing1990 return result;
2860 /// determined by a bit in `trailer_flags`.1991 }
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 });
28711992
2872 pub const RequiredFields = struct {1993 fn fullWhile(tree: Tree, info: full.While.Ast) full.While {
2873 tag: Tag,1994 const token_tags = tree.tokens.items(.tag);
2874 ltoken: TokenIndex,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,
2875 };2002 };
28762003 var tok_i = info.while_token - 1;
2877 pub fn getRHS(self: *const ControlFlowExpression) ?*Node {2004 if (token_tags[tok_i] == .keyword_inline) {
2878 return self.getTrailer(.rhs);2005 result.inline_token = tok_i;
2879 }2006 tok_i -= 1;
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);
2901 }2007 }
29022008 if (token_tags[tok_i] == .colon and
2903 pub fn create(allocator: *mem.Allocator, required: RequiredFields, trailers: TrailerFlags.InitStruct) !*ControlFlowExpression {2009 token_tags[tok_i - 1] == .identifier)
2904 const trailer_flags = TrailerFlags.init(trailers);2010 {
2905 const bytes = try allocator.alignedAlloc(u8, @alignOf(ControlFlowExpression), sizeInBytes(trailer_flags));2011 result.label_token = tok_i - 1;
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;
2915 }2012 }
29162013 const last_cond_token = tree.lastToken(info.cond_expr);
2917 pub fn destroy(self: *ControlFlowExpression, allocator: *mem.Allocator) void {2014 if (token_tags[last_cond_token + 2] == .pipe) {
2918 const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.trailer_flags)];2015 result.payload_token = last_cond_token + 3;
2919 allocator.free(bytes);
2920 }2016 }
29212017 if (info.else_expr != 0) {
2922 pub fn iterate(self: *const ControlFlowExpression, index: usize) ?*Node {2018 // then_expr else |x|
2923 var i = index;2019 // ^ ^
29242020 result.else_token = tree.lastToken(info.then_expr) + 1;
2925 if (self.getRHS()) |rhs| {2021 if (token_tags[result.else_token + 1] == .pipe) {
2926 if (i < 1) return rhs;2022 result.error_token = result.else_token + 2;
2927 i -= 1;
2928 }2023 }
2929
2930 return null;
2931 }2024 }
2025 return result;
2026 }
29322027
2933 pub fn firstToken(self: *const ControlFlowExpression) TokenIndex {2028 fn fullCall(tree: Tree, info: full.Call.Ast) full.Call {
2934 return self.ltoken;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;
2935 }2037 }
2038 return result;
2039 }
2040};
29362041
2937 pub fn lastToken(self: *const ControlFlowExpression) TokenIndex {2042/// Fully assembled AST node information.
2938 if (self.getRHS()) |rhs| {2043pub const full = struct {
2939 return rhs.lastToken();2044 pub const VarDecl = struct {
2940 }2045 visib_token: ?TokenIndex,
29412046 extern_export_token: ?TokenIndex,
2942 if (self.getLabel()) |label| {2047 lib_name: ?TokenIndex,
2943 return label;2048 threadlocal_token: ?TokenIndex,
2944 }2049 comptime_token: ?TokenIndex,
29452050 ast: Ast,
2946 return self.ltoken;
2947 }
29482051
2949 fn sizeInBytes(trailer_flags: TrailerFlags) usize {2052 pub const Ast = struct {
2950 return @sizeOf(ControlFlowExpression) + trailer_flags.sizeInBytes();2053 mut_token: TokenIndex,
2951 }2054 type_node: Node.Index,
2055 align_node: Node.Index,
2056 section_node: Node.Index,
2057 init_node: Node.Index,
2058 };
2952 };2059 };
29532060
2954 pub const Suspend = struct {2061 pub const If = struct {
2955 base: Node = Node{ .tag = .Suspend },2062 /// Points to the first token after the `|`. Will either be an identifier or
2956 suspend_token: TokenIndex,2063 /// a `*` (with an identifier immediately after it).
2957 body: ?*Node,2064 payload_token: ?TokenIndex,
29582065 /// Points to the identifier after the `|`.
2959 pub fn iterate(self: *const Suspend, index: usize) ?*Node {2066 error_token: ?TokenIndex,
2960 var i = index;2067 /// Populated only if else_expr != 0.
29612068 else_token: TokenIndex,
2962 if (self.body) |body| {2069 ast: Ast,
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 }
29782070
2979 return self.suspend_token;2071 pub const Ast = struct {
2980 }2072 if_token: TokenIndex,
2073 cond_expr: Node.Index,
2074 then_expr: Node.Index,
2075 else_expr: Node.Index,
2076 };
2981 };2077 };
29822078
2983 pub const EnumLiteral = struct {2079 pub const While = struct {
2984 base: Node = Node{ .tag = .EnumLiteral },2080 ast: Ast,
2985 dot: TokenIndex,2081 inline_token: ?TokenIndex,
2986 name: TokenIndex,2082 label_token: ?TokenIndex,
29872083 payload_token: ?TokenIndex,
2988 pub fn iterate(self: *const EnumLiteral, index: usize) ?*Node {2084 error_token: ?TokenIndex,
2989 return null;2085 /// Populated only if else_expr != 0.
2990 }2086 else_token: TokenIndex,
2991
2992 pub fn firstToken(self: *const EnumLiteral) TokenIndex {
2993 return self.dot;
2994 }
29952087
2996 pub fn lastToken(self: *const EnumLiteral) TokenIndex {2088 pub const Ast = struct {
2997 return self.name;2089 while_token: TokenIndex,
2998 }2090 cond_expr: Node.Index,
2091 cont_expr: Node.Index,
2092 then_expr: Node.Index,
2093 else_expr: Node.Index,
2094 };
2999 };2095 };
30002096
3001 /// Parameters are in memory following BuiltinCall.2097 pub const ContainerField = struct {
3002 pub const BuiltinCall = struct {2098 comptime_token: ?TokenIndex,
3003 base: Node = Node{ .tag = .BuiltinCall },2099 ast: Ast,
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 }
30182100
3019 pub fn iterate(self: *const BuiltinCall, index: usize) ?*Node {2101 pub const Ast = struct {
3020 var i = index;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];2109 pub const FnProto = struct {
3023 i -= self.params_len;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;2117 pub const Ast = struct {
3026 }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 {2126 pub const Param = struct {
3029 return self.builtin_token;2127 first_doc_comment: ?TokenIndex,
3030 }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 {2134 /// Abstracts over the fact that anytype and ... are not included
3033 return self.rparen_token;2135 /// in the params slice, since they are simple identifiers and
3034 }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 {2224 pub fn iterate(fn_proto: FnProto, tree: Tree) Iterator {
3037 const decls_start = @ptrCast([*]u8, self) + @sizeOf(BuiltinCall);2225 return .{
3038 return @ptrCast([*]*Node, decls_start)[0..self.params_len];2226 .tree = &tree,
2227 .fn_proto = &fn_proto,
2228 .param_i = 0,
2229 .tok_i = undefined,
2230 .tok_flag = false,
2231 };
3039 }2232 }
2233 };
30402234
3041 pub fn paramsConst(self: *const BuiltinCall) []const *Node {2235 pub const StructInit = struct {
3042 const decls_start = @ptrCast([*]const u8, self) + @sizeOf(BuiltinCall);2236 ast: Ast,
3043 return @ptrCast([*]const *Node, decls_start)[0..self.params_len];
3044 }
30452237
3046 fn sizeInBytes(params_len: NodeIndex) usize {2238 pub const Ast = struct {
3047 return @sizeOf(BuiltinCall) + @sizeOf(*Node) * @as(usize, params_len);2239 lbrace: TokenIndex,
3048 }2240 fields: []const Node.Index,
2241 type_expr: Node.Index,
2242 };
3049 };2243 };
30502244
3051 /// The string literal tokens appear directly in memory after MultilineStringLiteral.2245 pub const ArrayInit = struct {
3052 pub const MultilineStringLiteral = struct {2246 ast: Ast,
3053 base: Node = Node{ .tag = .MultilineStringLiteral },
3054 lines_len: TokenIndex,
30552247
3056 /// After this the caller must initialize the lines list.2248 pub const Ast = struct {
3057 pub fn alloc(allocator: *mem.Allocator, lines_len: NodeIndex) !*MultilineStringLiteral {2249 lbrace: TokenIndex,
3058 const bytes = try allocator.alignedAlloc(u8, @alignOf(MultilineStringLiteral), sizeInBytes(lines_len));2250 elements: []const Node.Index,
3059 return @ptrCast(*MultilineStringLiteral, bytes.ptr);2251 type_expr: Node.Index,
3060 }2252 };
2253 };
30612254
3062 pub fn free(self: *MultilineStringLiteral, allocator: *mem.Allocator) void {2255 pub const ArrayType = struct {
3063 const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.lines_len)];2256 ast: Ast,
3064 allocator.free(bytes);
3065 }
30662257
3067 pub fn iterate(self: *const MultilineStringLiteral, index: usize) ?*Node {2258 pub const Ast = struct {
3068 return null;2259 lbracket: TokenIndex,
3069 }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 {2266 pub const PtrType = struct {
3072 return self.linesConst()[0];2267 size: std.builtin.TypeInfo.Pointer.Size,
3073 }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 {2283 pub const Slice = struct {
3076 return self.linesConst()[self.lines_len - 1];2284 ast: Ast,
3077 }
30782285
3079 pub fn lines(self: *MultilineStringLiteral) []TokenIndex {2286 pub const Ast = struct {
3080 const decls_start = @ptrCast([*]u8, self) + @sizeOf(MultilineStringLiteral);2287 sliced: Node.Index,
3081 return @ptrCast([*]TokenIndex, decls_start)[0..self.lines_len];2288 lbracket: TokenIndex,
3082 }2289 start: Node.Index,
2290 end: Node.Index,
2291 sentinel: Node.Index,
2292 };
2293 };
30832294
3084 pub fn linesConst(self: *const MultilineStringLiteral) []const TokenIndex {2295 pub const ContainerDecl = struct {
3085 const decls_start = @ptrCast([*]const u8, self) + @sizeOf(MultilineStringLiteral);2296 layout_token: ?TokenIndex,
3086 return @ptrCast([*]const TokenIndex, decls_start)[0..self.lines_len];2297 ast: Ast,
3087 }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 {2308 pub const SwitchCase = struct {
3090 return @sizeOf(MultilineStringLiteral) + @sizeOf(TokenIndex) * @as(usize, lines_len);2309 /// Points to the first token after the `|`. Will either be an identifier or
3091 }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 };
3092 };2320 };
30932321
3094 pub const Asm = struct {2322 pub const Asm = struct {
3095 base: Node = Node{ .tag = .Asm },2323 ast: Ast,
3096 asm_token: TokenIndex,
3097 rparen: TokenIndex,
3098 volatile_token: ?TokenIndex,2324 volatile_token: ?TokenIndex,
3099 template: *Node,2325 first_clobber: ?TokenIndex,
3100 outputs: []Output,2326 outputs: []const Node.Index,
3101 inputs: []Input,2327 inputs: []const Node.Index,
3102 /// A clobber node must be a StringLiteral or MultilineStringLiteral.2328
3103 clobbers: []*Node,2329 pub const Ast = struct {
31042330 asm_token: TokenIndex,
3105 pub const Output = struct {2331 template: Node.Index,
3106 lbracket: TokenIndex,2332 items: []const Node.Index,
3107 symbolic_name: *Node,
3108 constraint: *Node,
3109 kind: Kind,
3110 rparen: TokenIndex,2333 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 }
3147 };2334 };
2335 };
31482336
3149 pub const Input = struct {2337 pub const Call = struct {
3150 lbracket: TokenIndex,2338 ast: Ast,
3151 symbolic_name: *Node,2339 async_token: ?TokenIndex,
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;
31612340
3162 if (i < 1) return self.constraint;2341 pub const Ast = struct {
3163 i -= 1;2342 lparen: TokenIndex,
2343 fn_expr: Node.Index,
2344 params: []const Node.Index,
2345 };
2346 };
2347};
31642348
3165 if (i < 1) return self.expr;2349pub const Error = struct {
3166 i -= 1;2350 tag: Tag,
2351 token: TokenIndex,
2352 extra: union {
2353 none: void,
2354 expected_tag: Token.Tag,
2355 } = .{ .none = {} },
31672356
3168 return null;2357 pub const Tag = enum {
3169 }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 {2401pub const Node = struct {
3172 return self.lbracket;2402 tag: Tag,
3173 }2403 main_token: TokenIndex,
2404 data: Data,
31742405
3175 pub fn lastToken(self: *const Input) TokenIndex {2406 pub const Index = u32;
3176 return self.rparen;
3177 }
3178 };
31792407
3180 pub fn iterate(self: *const Asm, index: usize) ?*Node {2408 comptime {
3181 var i = index;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) {2413 /// Note: The FooComma/FooSemicolon variants exist to ease the implementation of
3184 0 => return self.outputs[i / 3].symbolic_name,2414 /// Tree.lastToken()
3185 1 => return self.outputs[i / 3].constraint,2415 pub const Tag = enum {
3186 2 => switch (self.outputs[i / 3].kind) {2416 /// sub_list[lhs...rhs]
3187 .Variable => |variable_name| return &variable_name.base,2417 root,
3188 .Return => |return_type| return return_type,2418 /// `usingnamespace lhs;`. rhs unused. main_token is `usingnamespace`.
3189 },2419 @"usingnamespace",
3190 else => unreachable,2420 /// lhs is test name token (must be string literal), if any.
3191 };2421 /// rhs is the body node.
3192 i -= self.outputs.len * 3;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) {2866 else => false,
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,
3199 };2867 };
3200 i -= self.inputs.len * 3;
3201
3202 return null;
3203 }2868 }
2869 };
32042870
3205 pub fn firstToken(self: *const Asm) TokenIndex {2871 pub const Data = struct {
3206 return self.asm_token;2872 lhs: Index,
3207 }2873 rhs: Index,
2874 };
32082875
3209 pub fn lastToken(self: *const Asm) TokenIndex {2876 pub const LocalVarDecl = struct {
3210 return self.rparen;2877 type_node: Index,
3211 }2878 align_node: Index,
3212 };2879 };
32132880
3214 /// TODO remove from the Node base struct2881 pub const ArrayTypeSentinel = struct {
3215 /// TODO actually maybe remove entirely in favor of iterating backward from Node.firstToken()2882 elem_type: Index,
3216 /// and forwards to find same-line doc comments.2883 sentinel: Index,
3217 pub const DocComment = struct {2884 };
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 }
32272885
3228 pub fn firstToken(self: *const DocComment) TokenIndex {2886 pub const PtrType = struct {
3229 return self.first_line;2887 sentinel: Index,
3230 }2888 align_node: Index,
2889 };
32312890
3232 /// Returns the first doc comment line. Be careful, this may not be the desired behavior,2891 pub const PtrTypeBitRange = struct {
3233 /// which would require the tokens array.2892 sentinel: Index,
3234 pub fn lastToken(self: *const DocComment) TokenIndex {2893 align_node: Index,
3235 return self.first_line;2894 bit_range_start: Index,
3236 }2895 bit_range_end: Index,
3237 };2896 };
32382897
3239 pub const TestDecl = struct {2898 pub const SubRange = struct {
3240 base: Node = Node{ .tag = .TestDecl },2899 /// Index into sub_list.
3241 doc_comments: ?*DocComment,2900 start: Index,
3242 test_token: TokenIndex,2901 /// Index into sub_list.
3243 name: ?*Node,2902 end: Index,
3244 body_node: *Node,2903 };
32452904
3246 pub fn iterate(self: *const TestDecl, index: usize) ?*Node {2905 pub const If = struct {
3247 var i = index;2906 then_expr: Index,
2907 else_expr: Index,
2908 };
32482909
3249 if (i < 1) return self.body_node;2910 pub const ContainerField = struct {
3250 i -= 1;2911 value_expr: Index,
2912 align_expr: Index,
2913 };
32512914
3252 return null;2915 pub const GlobalVarDecl = struct {
3253 }2916 type_node: Index,
2917 align_node: Index,
2918 section_node: Index,
2919 };
32542920
3255 pub fn firstToken(self: *const TestDecl) TokenIndex {2921 pub const Slice = struct {
3256 return self.test_token;2922 start: Index,
3257 }2923 end: Index,
2924 };
32582925
3259 pub fn lastToken(self: *const TestDecl) TokenIndex {2926 pub const SliceSentinel = struct {
3260 return self.body_node.lastToken();2927 start: Index,
3261 }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,
3262 };2936 };
3263};
32642937
3265pub const PtrInfo = struct {2938 pub const WhileCont = struct {
3266 allowzero_token: ?TokenIndex = null,2939 cont_expr: Index,
3267 align_info: ?Align = null,2940 then_expr: Index,
3268 const_token: ?TokenIndex = null,2941 };
3269 volatile_token: ?TokenIndex = null,
3270 sentinel: ?*Node = null,
32712942
3272 pub const Align = struct {2943 pub const FnProtoOne = struct {
3273 node: *Node,2944 /// Populated if there is exactly 1 parameter. Otherwise there are 0 parameters.
3274 bit_range: ?BitRange = null,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 {2954 pub const FnProto = struct {
3277 start: *Node,2955 params_start: Index,
3278 end: *Node,2956 params_end: Index,
3279 };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,
3280 };2963 };
3281};
32822964
3283test "iterate" {2965 pub const Asm = struct {
3284 var root = Node.Root{2966 items_start: Index,
3285 .base = Node{ .tag = Node.Tag.Root },2967 items_end: Index,
3286 .decls_len = 0,2968 /// Needed to make lastToken() work.
3287 .eof_token = 0,2969 rparen: TokenIndex,
3288 };2970 };
3289 var base = &root.base;2971};
3290 testing.expect(base.iterate(0) == null);
3291}
lib/std/zig/parse.zig+3505-2912
...@@ -11,85 +11,181 @@ const Node = ast.Node;...@@ -11,85 +11,181 @@ const Node = ast.Node;
11const Tree = ast.Tree;11const Tree = ast.Tree;
12const AstError = ast.Error;12const AstError = ast.Error;
13const TokenIndex = ast.TokenIndex;13const TokenIndex = ast.TokenIndex;
14const NodeIndex = ast.NodeIndex;
15const Token = std.zig.Token;14const Token = std.zig.Token;
1615
17pub const Error = error{ParseError} || Allocator.Error;16pub const Error = error{ParseError} || Allocator.Error;
1817
19/// Result should be freed with tree.deinit() when there are18/// Result should be freed with tree.deinit() when there are
20/// no more references to any of the tokens or nodes.19/// no more references to any of the tokens or nodes.
21pub fn parse(gpa: *Allocator, source: []const u8) Allocator.Error!*Tree {20pub fn parse(gpa: *Allocator, source: []const u8) Allocator.Error!Tree {
22 var token_ids = std.ArrayList(Token.Id).init(gpa);21 var tokens = ast.TokenList{};
23 defer token_ids.deinit();22 defer tokens.deinit(gpa);
24 var token_locs = std.ArrayList(Token.Loc).init(gpa);
25 defer token_locs.deinit();
2623
27 // Empirically, the zig std lib has an 8:1 ratio of source bytes to token count.24 // Empirically, the zig std lib has an 8:1 ratio of source bytes to token count.
28 const estimated_token_count = source.len / 8;25 const estimated_token_count = source.len / 8;
29 try token_ids.ensureCapacity(estimated_token_count);26 try tokens.ensureCapacity(gpa, estimated_token_count);
30 try token_locs.ensureCapacity(estimated_token_count);
3127
32 var tokenizer = std.zig.Tokenizer.init(source);28 var tokenizer = std.zig.Tokenizer.init(source);
33 while (true) {29 while (true) {
34 const token = tokenizer.next();30 const token = tokenizer.next();
35 try token_ids.append(token.id);31 try tokens.append(gpa, .{
36 try token_locs.append(token.loc);32 .tag = token.tag,
37 if (token.id == .Eof) break;33 .start = @intCast(u32, token.loc.start),
34 });
35 if (token.tag == .eof) break;
38 }36 }
3937
40 var parser: Parser = .{38 var parser: Parser = .{
41 .source = source,39 .source = source,
42 .arena = std.heap.ArenaAllocator.init(gpa),
43 .gpa = gpa,40 .gpa = gpa,
44 .token_ids = token_ids.items,41 .token_tags = tokens.items(.tag),
45 .token_locs = token_locs.items,42 .token_starts = tokens.items(.start),
46 .errors = .{},43 .errors = .{},
44 .nodes = .{},
45 .extra_data = .{},
47 .tok_i = 0,46 .tok_i = 0,
48 };47 };
49 defer parser.errors.deinit(gpa);48 defer parser.errors.deinit(gpa);
50 errdefer parser.arena.deinit();49 defer parser.nodes.deinit(gpa);
5150 defer parser.extra_data.deinit(gpa);
52 while (token_ids.items[parser.tok_i] == .LineComment) parser.tok_i += 1;51
5352 // Empirically, Zig source code has a 2:1 ratio of tokens to AST nodes.
54 const root_node = try parser.parseRoot();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);77 // TODO experiment with compacting the MultiArrayList slices here
57 tree.* = .{78 return Tree{
58 .gpa = gpa,
59 .source = source,79 .source = source,
60 .token_ids = token_ids.toOwnedSlice(),80 .tokens = tokens.toOwnedSlice(),
61 .token_locs = token_locs.toOwnedSlice(),81 .nodes = parser.nodes.toOwnedSlice(),
82 .extra_data = parser.extra_data.toOwnedSlice(gpa),
62 .errors = parser.errors.toOwnedSlice(gpa),83 .errors = parser.errors.toOwnedSlice(gpa),
63 .root_node = root_node,
64 .arena = parser.arena.state,
65 };84 };
66 return tree;
67}85}
6886
87const null_node: Node.Index = 0;
88
69/// Represents in-progress parsing, will be converted to an ast.Tree after completion.89/// Represents in-progress parsing, will be converted to an ast.Tree after completion.
70const Parser = struct {90const Parser = struct {
71 arena: std.heap.ArenaAllocator,
72 gpa: *Allocator,91 gpa: *Allocator,
73 source: []const u8,92 source: []const u8,
74 token_ids: []const Token.Id,93 token_tags: []const Token.Tag,
75 token_locs: []const Token.Loc,94 token_starts: []const ast.ByteOffset,
76 tok_i: TokenIndex,95 tok_i: TokenIndex,
77 errors: std.ArrayListUnmanaged(AstError),96 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 eof104 fn deinit(self: SmallSpan, gpa: *Allocator) void {
80 fn parseRoot(p: *Parser) Allocator.Error!*Node.Root {105 switch (self) {
81 const decls = try parseContainerMembers(p, true);106 .zero_or_one => {},
82 defer p.gpa.free(decls);107 .multi => |list| gpa.free(list),
108 }
109 }
110 };
83111
84 // parseContainerMembers will try to skip as much112 const Members = struct {
85 // invalid tokens as it can so this can only be the EOF113 len: usize,
86 const eof_token = p.eatToken(.Eof).?;114 lhs: Node.Index,
115 rhs: Node.Index,
116 trailing: bool,
87117
88 const decls_len = @intCast(NodeIndex, decls.len);118 fn toSpan(self: Members, p: *Parser) !Node.SubRange {
89 const node = try Node.Root.create(&p.arena.allocator, decls_len, eof_token);119 if (self.len <= 2) {
90 std.mem.copy(*Node, node.decls(), decls);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;
93 }189 }
94190
95 /// ContainerMembers191 /// ContainerMembers
...@@ -99,176 +195,226 @@ const Parser = struct {...@@ -99,176 +195,226 @@ const Parser = struct {
99 /// / ContainerField COMMA ContainerMembers195 /// / ContainerField COMMA ContainerMembers
100 /// / ContainerField196 /// / ContainerField
101 /// /197 /// /
102 fn parseContainerMembers(p: *Parser, top_level: bool) ![]*Node {198 /// TopLevelComptime <- KEYWORD_comptime BlockExpr
103 var list = std.ArrayList(*Node).init(p.gpa);199 fn parseContainerMembers(p: *Parser) !Members {
200 var list = std.ArrayList(Node.Index).init(p.gpa);
104 defer list.deinit();201 defer list.deinit();
105202
106 var field_state: union(enum) {203 var field_state: union(enum) {
107 /// no fields have been seen204 /// No fields have been seen.
108 none,205 none,
109 /// currently parsing fields206 /// Currently parsing fields.
110 seen,207 seen,
111 /// saw fields and then a declaration after them.208 /// Saw fields and then a declaration after them.
112 /// payload is first token of previous declaration.209 /// Payload is first token of previous declaration.
113 end: TokenIndex,210 end: Node.Index,
114 /// ther was a declaration between fields, don't report more errors211 /// There was a declaration between fields, don't report more errors.
115 err,212 err,
116 } = .none;213 } = .none;
117214
118 while (true) {215 // Skip container doc comments.
119 if (try p.parseContainerDocComments()) |node| {216 while (p.eatToken(.container_doc_comment)) |_| {}
120 try list.append(node);
121 continue;
122 }
123
124 const doc_comments = try p.parseDocComment();
125217
126 if (p.parseTestDecl() catch |err| switch (err) {218 var trailing = false;
127 error.OutOfMemory => return error.OutOfMemory,219 while (true) {
128 error.ParseError => {220 const doc_comment = try p.eatDocComments();
129 p.findNextContainerMember();221
130 continue;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;
131 },232 },
132 }) |node| {233 .keyword_comptime => switch (p.token_tags[p.tok_i + 1]) {
133 if (field_state == .seen) {234 .identifier => {
134 field_state = .{ .end = node.firstToken() };235 p.tok_i += 1;
135 }236 const container_field = try p.expectContainerFieldRecoverable();
136 node.cast(Node.TestDecl).?.doc_comments = doc_comments;237 if (container_field != 0) {
137 try list.append(node);238 switch (field_state) {
138 continue;239 .none => field_state = .seen,
139 }240 .err, .seen => {},
140241 .end => |node| {
141 if (p.parseTopLevelComptime() catch |err| switch (err) {242 try p.warnMsg(.{
142 error.OutOfMemory => return error.OutOfMemory,243 .tag = .decl_between_fields,
143 error.ParseError => {244 .token = p.nodes.items(.main_token)[node],
144 p.findNextContainerMember();245 });
145 continue;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 },
146 },298 },
147 }) |node| {299 .keyword_pub => {
148 if (field_state == .seen) {300 p.tok_i += 1;
149 field_state = .{ .end = node.firstToken() };301 const top_level_decl = try p.expectTopLevelDeclRecoverable();
150 }302 if (top_level_decl != 0) {
151 node.cast(Node.Comptime).?.doc_comments = doc_comments;303 if (field_state == .seen) {
152 try list.append(node);304 field_state = .{ .end = top_level_decl };
153 continue;305 }
154 }306 try list.append(top_level_decl);
155307 }
156 const visib_token = p.eatToken(.Keyword_pub);308 trailing = p.token_tags[p.tok_i - 1] == .semicolon;
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;
163 },309 },
164 }) |node| {310 .keyword_usingnamespace => {
165 if (field_state == .seen) {311 const node = try p.expectUsingNamespaceRecoverable();
166 field_state = .{ .end = visib_token orelse node.firstToken() };312 if (node != 0) {
167 }313 if (field_state == .seen) {
168 try list.append(node);314 field_state = .{ .end = node };
169 continue;315 }
170 }316 try list.append(node);
171317 }
172 if (visib_token != null) {318 trailing = p.token_tags[p.tok_i - 1] == .semicolon;
173 try p.errors.append(p.gpa, .{319 },
174 .ExpectedPubItem = .{ .token = p.tok_i },320 .keyword_const,
175 });321 .keyword_var,
176 // ignore this pub322 .keyword_threadlocal,
177 continue;323 .keyword_export,
178 }324 .keyword_extern,
179325 .keyword_inline,
180 if (p.parseContainerField() catch |err| switch (err) {326 .keyword_noinline,
181 error.OutOfMemory => return error.OutOfMemory,327 .keyword_fn,
182 error.ParseError => {328 => {
183 // attempt to recover329 const top_level_decl = try p.expectTopLevelDeclRecoverable();
184 p.findNextContainerMember();330 if (top_level_decl != 0) {
185 continue;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 }
186 },371 },
187 }) |node| {372 .eof, .r_brace => {
188 switch (field_state) {373 if (doc_comment) |tok| {
189 .none => field_state = .seen,374 try p.warnMsg(.{
190 .err, .seen => {},375 .tag = .unattached_doc_comment,
191 .end => |tok| {376 .token = tok,
192 try p.errors.append(p.gpa, .{
193 .DeclBetweenFields = .{ .token = tok },
194 });377 });
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 },
231 }378 }
232 };379 break;
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 });
253 },380 },
254 else => {381 else => {
255 const index = p.tok_i;382 try p.warn(.expected_container_members);
256 if (next == .RBrace) {383 // This was likely not supposed to end yet; try to find the next declaration.
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
263 p.findNextContainerMember();384 p.findNextContainerMember();
264 try p.errors.append(p.gpa, .{
265 .ExpectedContainerMembers = .{ .token = index },
266 });
267 },385 },
268 }386 }
269 }387 }
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 }
272 }418 }
273419
274 /// Attempts to find next container member by searching for certain tokens420 /// Attempts to find next container member by searching for certain tokens
...@@ -276,47 +422,52 @@ const Parser = struct {...@@ -276,47 +422,52 @@ const Parser = struct {
276 var level: u32 = 0;422 var level: u32 = 0;
277 while (true) {423 while (true) {
278 const tok = p.nextToken();424 const tok = p.nextToken();
279 switch (p.token_ids[tok]) {425 switch (p.token_tags[tok]) {
280 // any of these can start a new top level declaration426 // Any of these can start a new top level declaration.
281 .Keyword_test,427 .keyword_test,
282 .Keyword_comptime,428 .keyword_comptime,
283 .Keyword_pub,429 .keyword_pub,
284 .Keyword_export,430 .keyword_export,
285 .Keyword_extern,431 .keyword_extern,
286 .Keyword_inline,432 .keyword_inline,
287 .Keyword_noinline,433 .keyword_noinline,
288 .Keyword_usingnamespace,434 .keyword_usingnamespace,
289 .Keyword_threadlocal,435 .keyword_threadlocal,
290 .Keyword_const,436 .keyword_const,
291 .Keyword_var,437 .keyword_var,
292 .Keyword_fn,438 .keyword_fn,
293 .Identifier,
294 => {439 => {
295 if (level == 0) {440 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;
297 return;448 return;
298 }449 }
299 },450 },
300 .Comma, .Semicolon => {451 .comma, .semicolon => {
301 // this decl was likely meant to end here452 // this decl was likely meant to end here
302 if (level == 0) {453 if (level == 0) {
303 return;454 return;
304 }455 }
305 },456 },
306 .LParen, .LBracket, .LBrace => level += 1,457 .l_paren, .l_bracket, .l_brace => level += 1,
307 .RParen, .RBracket => {458 .r_paren, .r_bracket => {
308 if (level != 0) level -= 1;459 if (level != 0) level -= 1;
309 },460 },
310 .RBrace => {461 .r_brace => {
311 if (level == 0) {462 if (level == 0) {
312 // end of container, exit463 // end of container, exit
313 p.putBackToken(tok);464 p.tok_i -= 1;
314 return;465 return;
315 }466 }
316 level -= 1;467 level -= 1;
317 },468 },
318 .Eof => {469 .eof => {
319 p.putBackToken(tok);470 p.tok_i -= 1;
320 return;471 return;
321 },472 },
322 else => {},473 else => {},
...@@ -329,22 +480,22 @@ const Parser = struct {...@@ -329,22 +480,22 @@ const Parser = struct {
329 var level: u32 = 0;480 var level: u32 = 0;
330 while (true) {481 while (true) {
331 const tok = p.nextToken();482 const tok = p.nextToken();
332 switch (p.token_ids[tok]) {483 switch (p.token_tags[tok]) {
333 .LBrace => level += 1,484 .l_brace => level += 1,
334 .RBrace => {485 .r_brace => {
335 if (level == 0) {486 if (level == 0) {
336 p.putBackToken(tok);487 p.tok_i -= 1;
337 return;488 return;
338 }489 }
339 level -= 1;490 level -= 1;
340 },491 },
341 .Semicolon => {492 .semicolon => {
342 if (level == 0) {493 if (level == 0) {
343 return;494 return;
344 }495 }
345 },496 },
346 .Eof => {497 .eof => {
347 p.putBackToken(tok);498 p.tok_i -= 1;
348 return;499 return;
349 },500 },
350 else => {},501 else => {},
...@@ -352,335 +503,337 @@ const Parser = struct {...@@ -352,335 +503,337 @@ const Parser = struct {
352 }503 }
353 }504 }
354505
355 /// Eat a multiline container doc comment506 /// TestDecl <- KEYWORD_test STRINGLITERALSINGLE? Block
356 fn parseContainerDocComments(p: *Parser) !?*Node {507 fn expectTestDecl(p: *Parser) !Node.Index {
357 if (p.eatToken(.ContainerDocComment)) |first_line| {508 const test_token = p.assertToken(.keyword_test);
358 while (p.eatToken(.ContainerDocComment)) |_| {}509 const name_token = p.eatToken(.string_literal);
359 const node = try p.arena.allocator.create(Node.DocComment);510 const block_node = try p.parseBlock();
360 node.* = .{ .first_line = first_line };511 if (block_node == 0) return p.fail(.expected_block);
361 return &node.base;512 return p.addNode(.{
362 }513 .tag = .test_decl,
363 return null;514 .main_token = test_token,
364 }515 .data = .{
365516 .lhs = name_token orelse 0,
366 /// TestDecl <- KEYWORD_test STRINGLITERALSINGLE Block517 .rhs = block_node,
367 fn parseTestDecl(p: *Parser) !?*Node {518 },
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 },
395 });519 });
520 }
396521
397 const comptime_node = try p.arena.allocator.create(Node.Comptime);522 fn expectTestDeclRecoverable(p: *Parser) error{OutOfMemory}!Node.Index {
398 comptime_node.* = .{523 return p.expectTestDecl() catch |err| switch (err) {
399 .doc_comments = null,524 error.OutOfMemory => return error.OutOfMemory,
400 .comptime_token = tok,525 error.ParseError => {
401 .expr = block_node,526 p.findNextContainerMember();
527 return null_node;
528 },
402 };529 };
403 return &comptime_node.base;
404 }530 }
405531
406 /// TopLevelDecl532 /// TopLevelDecl
407 /// <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / (KEYWORD_inline / KEYWORD_noinline))? FnProto (SEMICOLON / Block)533 /// <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / (KEYWORD_inline / KEYWORD_noinline))? FnProto (SEMICOLON / Block)
408 /// / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl534 /// / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl
409 /// / KEYWORD_usingnamespace Expr SEMICOLON535 /// / KEYWORD_usingnamespace Expr SEMICOLON
410 fn parseTopLevelDecl(p: *Parser, doc_comments: ?*Node.DocComment, visib_token: ?TokenIndex) !?*Node {536 fn expectTopLevelDecl(p: *Parser) !Node.Index {
411 var lib_name: ?*Node = null;537 const extern_export_inline_token = p.nextToken();
412 const extern_export_inline_token = blk: {538 var expect_fn: bool = false;
413 if (p.eatToken(.Keyword_export)) |token| break :blk token;539 var expect_var_or_fn: bool = false;
414 if (p.eatToken(.Keyword_extern)) |token| {540 switch (p.token_tags[extern_export_inline_token]) {
415 lib_name = try p.parseStringLiteralSingle();541 .keyword_extern => {
416 break :blk token;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 },
417 }575 }
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;
430 }576 }
431577 if (expect_fn) {
432 if (extern_export_inline_token) |token| {578 try p.warn(.expected_fn);
433 if (p.token_ids[token] == .Keyword_inline or579 return error.ParseError;
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 }
441 }580 }
442581
443 const thread_local_token = p.eatToken(.Keyword_threadlocal);582 const thread_local_token = p.eatToken(.keyword_threadlocal);
444583 const var_decl = try p.parseVarDecl();
445 if (try p.parseVarDecl(.{584 if (var_decl != 0) {
446 .doc_comments = doc_comments,585 const semicolon_token = try p.expectToken(.semicolon);
447 .visib_token = visib_token,586 return var_decl;
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;
453 }587 }
454
455 if (thread_local_token != null) {588 if (thread_local_token != null) {
456 try p.errors.append(p.gpa, .{589 return p.fail(.expected_var_decl);
457 .ExpectedVarDecl = .{ .token = p.tok_i },
458 });
459 // ignore this and try again;
460 return error.ParseError;
461 }590 }
462591 if (expect_var_or_fn) {
463 if (extern_export_inline_token) |token| {592 return p.fail(.expected_var_decl_or_fn);
464 try p.errors.append(p.gpa, .{
465 .ExpectedVarDeclOrFn = .{ .token = p.tok_i },
466 });
467 // ignore this and try again;
468 return error.ParseError;
469 }593 }
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;600 fn expectTopLevelDeclRecoverable(p: *Parser) error{OutOfMemory}!Node.Index {
472 const expr = try p.expectNode(parseExpr, .{601 return p.expectTopLevelDecl() catch |err| switch (err) {
473 .ExpectedExpr = .{ .token = p.tok_i },602 error.OutOfMemory => return error.OutOfMemory,
474 });603 error.ParseError => {
475 const semicolon_token = try p.expectToken(.Semicolon);604 p.findNextContainerMember();
476605 return null_node;
477 const node = try p.arena.allocator.create(Node.Use);606 },
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,
484 };607 };
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 });
487 }622 }
488623
489 /// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? CallConv? EXCLAMATIONMARK? (Keyword_anytype / TypeExpr)624 fn expectUsingNamespaceRecoverable(p: *Parser) error{OutOfMemory}!Node.Index {
490 fn parseFnProto(p: *Parser, level: enum { top_level, as_type }, fields: struct {625 return p.expectUsingNamespace() catch |err| switch (err) {
491 doc_comments: ?*Node.DocComment = null,626 error.OutOfMemory => return error.OutOfMemory,
492 visib_token: ?TokenIndex = null,627 error.ParseError => {
493 extern_export_inline_token: ?TokenIndex = null,628 p.findNextContainerMember();
494 lib_name: ?*Node = null,629 return null_node;
495 }) !?*Node {630 },
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;
520 };631 };
521 const name_token = p.eatToken(.Identifier);632 }
522 const lparen = try p.expectToken(.LParen);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);
523 const params = try p.parseParamDeclList();638 const params = try p.parseParamDeclList();
524 defer p.gpa.free(params);639 defer params.deinit(p.gpa);
525 const var_args_token = p.eatToken(.Ellipsis3);
526 const rparen = try p.expectToken(.RParen);
527 const align_expr = try p.parseByteAlign();640 const align_expr = try p.parseByteAlign();
528 const section_expr = try p.parseLinkSection();641 const section_expr = try p.parseLinkSection();
529 const callconv_expr = try p.parseCallconv();642 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()) orelse645 const return_type_expr = try p.parseTypeExpr();
533 try p.expectNodeRecoverable(parseTypeExpr, .{646 if (return_type_expr == 0) {
534 // most likely the user forgot to specify the return type.647 // most likely the user forgot to specify the return type.
535 // Mark return type as invalid and try to continue.648 // Mark return type as invalid and try to continue.
536 .ExpectedReturnType = .{ .token = p.tok_i },649 try p.warn(.expected_return_type);
537 });650 }
538651
539 // TODO https://github.com/ziglang/zig/issues/3750652 if (align_expr == 0 and section_expr == 0 and callconv_expr == 0) {
540 const R = Node.FnProto.ReturnType;653 switch (params) {
541 const return_type = if (return_type_expr == null)654 .zero_or_one => |param| return p.addNode(.{
542 R{ .Invalid = rparen }655 .tag = .fn_proto_simple,
543 else if (exclamation_token != null)656 .main_token = fn_token,
544 R{ .InferErrorSet = return_type_expr.? }657 .data = .{
545 else658 .lhs = param,
546 R{ .Explicit = return_type_expr.? };659 .rhs = return_type_expr,
547660 },
548 const body_node: ?*Node = switch (level) {661 }),
549 .top_level => blk: {662 .multi => |list| {
550 if (p.eatToken(.Semicolon)) |_| {663 const span = try p.listToSpan(list);
551 break :blk null;664 return p.addNode(.{
552 }665 .tag = .fn_proto_multi,
553 const body_block = (try p.parseBlock(null)) orelse {666 .main_token = fn_token,
554 // Since parseBlock only return error.ParseError on667 .data = .{
555 // a missing '}' we can assume this function was668 .lhs = try p.addExtra(Node.SubRange{
556 // supposed to end here.669 .start = span.start,
557 try p.errors.append(p.gpa, .{ .ExpectedSemiOrLBrace = .{ .token = p.tok_i } });670 .end = span.end,
558 break :blk null;671 }),
559 };672 .rhs = return_type_expr,
560 break :blk body_block;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 });
561 },708 },
562 .as_type => null,709 }
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;
587 }710 }
588711
589 /// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON712 /// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON
590 fn parseVarDecl(p: *Parser, fields: struct {713 fn parseVarDecl(p: *Parser) !Node.Index {
591 doc_comments: ?*Node.DocComment = null,714 const mut_token = p.eatToken(.keyword_const) orelse
592 visib_token: ?TokenIndex = null,715 p.eatToken(.keyword_var) orelse
593 thread_local_token: ?TokenIndex = null,716 return null_node;
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;
601717
602 const name_token = try p.expectToken(.Identifier);718 _ = try p.expectToken(.identifier);
603 const type_node = if (p.eatToken(.Colon) != null)719 const type_node: Node.Index = if (p.eatToken(.colon) == null) 0 else try p.expectTypeExpr();
604 try p.expectNode(parseTypeExpr, .{
605 .ExpectedTypeExpr = .{ .token = p.tok_i },
606 })
607 else
608 null;
609 const align_node = try p.parseByteAlign();720 const align_node = try p.parseByteAlign();
610 const section_node = try p.parseLinkSection();721 const section_node = try p.parseLinkSection();
611 const eq_token = p.eatToken(.Equal);722 const init_node: Node.Index = if (p.eatToken(.equal) == null) 0 else try p.expectExpr();
612 const init_node = if (eq_token != null) blk: {723 if (section_node == 0) {
613 break :blk try p.expectNode(parseExpr, .{724 if (align_node == 0) {
614 .ExpectedExpr = .{ .token = p.tok_i },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 },
615 });767 });
616 } else null;768 }
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;
639 }769 }
640770
641 /// ContainerField <- KEYWORD_comptime? IDENTIFIER (COLON TypeExpr ByteAlign?)? (EQUAL Expr)?771 /// ContainerField <- KEYWORD_comptime? IDENTIFIER (COLON TypeExpr ByteAlign?)? (EQUAL Expr)?
642 fn parseContainerField(p: *Parser) !?*Node {772 fn expectContainerField(p: *Parser) !Node.Index {
643 const comptime_token = p.eatToken(.Keyword_comptime);773 const comptime_token = p.eatToken(.keyword_comptime);
644 const name_token = p.eatToken(.Identifier) orelse {774 const name_token = p.assertToken(.identifier);
645 if (comptime_token) |t| p.putBackToken(t);775
646 return null;776 var align_expr: Node.Index = 0;
647 };777 var type_expr: Node.Index = 0;
648778 if (p.eatToken(.colon)) |_| {
649 var align_expr: ?*Node = null;779 if (p.eatToken(.keyword_anytype)) |anytype_tok| {
650 var type_expr: ?*Node = null;780 type_expr = try p.addNode(.{
651 if (p.eatToken(.Colon)) |_| {781 .tag = .@"anytype",
652 if (p.eatToken(.Keyword_anytype) orelse p.eatToken(.Keyword_var)) |anytype_tok| {782 .main_token = anytype_tok,
653 const node = try p.arena.allocator.create(Node.OneToken);783 .data = .{
654 node.* = .{784 .lhs = undefined,
655 .base = .{ .tag = .AnyType },785 .rhs = undefined,
656 .token = anytype_tok,786 },
657 };
658 type_expr = &node.base;
659 } else {
660 type_expr = try p.expectNode(parseTypeExpr, .{
661 .ExpectedTypeExpr = .{ .token = p.tok_i },
662 });787 });
788 } else {
789 type_expr = try p.expectTypeExpr();
663 align_expr = try p.parseByteAlign();790 align_expr = try p.parseByteAlign();
664 }791 }
665 }792 }
666793
667 const value_expr = if (p.eatToken(.Equal)) |_|794 const value_expr: Node.Index = if (p.eatToken(.equal) == null) 0 else try p.expectExpr();
668 try p.expectNode(parseExpr, .{795
669 .ExpectedExpr = .{ .token = p.tok_i },796 if (align_expr == 0) {
670 })797 return p.addNode(.{
671 else798 .tag = .container_field_init,
672 null;799 .main_token = name_token,
673800 .data = .{
674 const node = try p.arena.allocator.create(Node.ContainerField);801 .lhs = type_expr,
675 node.* = .{802 .rhs = value_expr,
676 .doc_comments = null,803 },
677 .comptime_token = comptime_token,804 });
678 .name_token = name_token,805 } else if (value_expr == 0) {
679 .type_expr = type_expr,806 return p.addNode(.{
680 .value_expr = value_expr,807 .tag = .container_field_align,
681 .align_expr = align_expr,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 },
682 };836 };
683 return &node.base;
684 }837 }
685838
686 /// Statement839 /// Statement
...@@ -694,833 +847,1632 @@ const Parser = struct {...@@ -694,833 +847,1632 @@ const Parser = struct {
694 /// / LabeledStatement847 /// / LabeledStatement
695 /// / SwitchExpr848 /// / SwitchExpr
696 /// / AssignExpr SEMICOLON849 /// / AssignExpr SEMICOLON
697 fn parseStatement(p: *Parser) Error!?*Node {850 fn parseStatement(p: *Parser) Error!Node.Index {
698 const comptime_token = p.eatToken(.Keyword_comptime);851 const comptime_token = p.eatToken(.keyword_comptime);
699852
700 if (try p.parseVarDecl(.{853 const var_decl = try p.parseVarDecl();
701 .comptime_token = comptime_token,854 if (var_decl != 0) {
702 })) |node| {855 _ = try p.expectTokenRecoverable(.semicolon);
703 return node;856 return var_decl;
704 }857 }
705858
706 if (comptime_token) |token| {859 if (comptime_token) |token| {
707 const block_expr = try p.expectNode(parseBlockExprStatement, .{860 return p.addNode(.{
708 .ExpectedBlockOrAssignment = .{ .token = p.tok_i },861 .tag = .@"comptime",
709 });862 .main_token = token,
710863 .data = .{
711 const node = try p.arena.allocator.create(Node.Comptime);864 .lhs = try p.expectBlockExprStatement(),
712 node.* = .{865 .rhs = undefined,
713 .doc_comments = null,866 },
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 },
723 });867 });
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;
731 }868 }
732869
733 if (p.eatToken(.Keyword_suspend)) |suspend_token| {870 switch (p.token_tags[p.tok_i]) {
734 const semicolon = p.eatToken(.Semicolon);871 .keyword_nosuspend => {
735872 return p.addNode(.{
736 const body_node = if (semicolon == null) blk: {873 .tag = .@"nosuspend",
737 break :blk try p.expectNode(parseBlockExprStatement, .{874 .main_token = p.nextToken(),
738 .ExpectedBlockOrExpression = .{ .token = p.tok_i },875 .data = .{
876 .lhs = try p.expectBlockExprStatement(),
877 .rhs = undefined,
878 },
739 });879 });
740 } else null;880 },
741881 .keyword_suspend => {
742 const node = try p.arena.allocator.create(Node.Suspend);882 const token = p.nextToken();
743 node.* = .{883 const block_expr: Node.Index = if (p.eatToken(.semicolon) != null)
744 .suspend_token = suspend_token,884 0
745 .body = body_node,885 else
746 };886 try p.expectBlockExprStatement();
747 return &node.base;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 => {},
748 }915 }
749916
750 const defer_token = p.eatToken(.Keyword_defer) orelse p.eatToken(.Keyword_errdefer);917 const labeled_statement = try p.parseLabeledStatement();
751 if (defer_token) |token| {918 if (labeled_statement != 0) return labeled_statement;
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 }
767919
768 if (try p.parseIfStatement()) |node| return node;920 const assign_expr = try p.parseAssignExpr();
769 if (try p.parseLabeledStatement()) |node| return node;921 if (assign_expr != 0) {
770 if (try p.parseSwitchExpr()) |node| return node;922 _ = try p.expectTokenRecoverable(.semicolon);
771 if (try p.parseAssignExpr()) |node| {923 return assign_expr;
772 _ = try p.expectTokenRecoverable(.Semicolon);
773 return node;
774 }924 }
775925
776 return null;926 return null_node;
777 }927 }
778928
779 /// IfStatement929 fn expectStatement(p: *Parser) !Node.Index {
780 /// <- IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )?930 const statement = try p.parseStatement();
781 /// / IfPrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement )931 if (statement == 0) {
782 fn parseIfStatement(p: *Parser) !?*Node {932 return p.fail(.expected_statement);
783 const if_node = (try p.parseIfPrefix()) orelse return null;933 }
784 const if_prefix = if_node.cast(Node.If).?;934 return statement;
785935 }
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 });
802936
803 const node = try p.arena.allocator.create(Node.Else);937 /// If a parse error occurs, reports an error, but then finds the next statement
804 node.* = .{938 /// and returns that one instead. If a parse error occurs but there is no following
805 .else_token = else_token,939 /// statement, returns 0.
806 .payload = payload,940 fn expectStatementRecoverable(p: *Parser) error{OutOfMemory}!Node.Index {
807 .body = else_body,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 },
808 };949 };
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;
817 }950 }
951 }
818952
819 if (assign_expr) |body| {953 /// IfStatement
820 if_prefix.body = body;954 /// <- IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )?
821 if (semicolon != null) return if_node;955 /// / IfPrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement )
822 if (else_node != null) {956 fn expectIfStatement(p: *Parser) !Node.Index {
823 if_prefix.@"else" = else_node;957 const if_token = p.assertToken(.keyword_if);
824 return if_node;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);
825 }972 }
826 try p.errors.append(p.gpa, .{973 if (p.eatToken(.semicolon)) |_| {
827 .ExpectedSemiOrElse = .{ .token = p.tok_i },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 },
828 });997 });
829 }998 };
830999 const else_payload = try p.parsePayload();
831 return if_node;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 });
832 }1012 }
8331013
834 /// LabeledStatement <- BlockLabel? (Block / LoopStatement)1014 /// LabeledStatement <- BlockLabel? (Block / LoopStatement)
835 fn parseLabeledStatement(p: *Parser) !?*Node {1015 fn parseLabeledStatement(p: *Parser) !Node.Index {
836 var colon: TokenIndex = undefined;1016 const label_token = p.parseBlockLabel();
837 const label_token = p.parseBlockLabel(&colon);1017 const block = try p.parseBlock();
8381018 if (block != 0) return block;
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 }
8491019
850 if (label_token != null) {1020 const loop_stmt = try p.parseLoopStatement();
851 try p.errors.append(p.gpa, .{1021 if (loop_stmt != 0) return loop_stmt;
852 .ExpectedLabelable = .{ .token = p.tok_i },1022
853 });1023 if (label_token != 0) {
854 return error.ParseError;1024 return p.fail(.expected_labelable);
855 }1025 }
8561026
857 return null;1027 return null_node;
858 }1028 }
8591029
860 /// LoopStatement <- KEYWORD_inline? (ForStatement / WhileStatement)1030 /// LoopStatement <- KEYWORD_inline? (ForStatement / WhileStatement)
861 fn parseLoopStatement(p: *Parser) !?*Node {1031 fn parseLoopStatement(p: *Parser) !Node.Index {
862 const inline_token = p.eatToken(.Keyword_inline);1032 const inline_token = p.eatToken(.keyword_inline);
8631033
864 if (try p.parseForStatement()) |node| {1034 const for_statement = try p.parseForStatement();
865 node.cast(Node.For).?.inline_token = inline_token;1035 if (for_statement != 0) return for_statement;
866 return node;
867 }
8681036
869 if (try p.parseWhileStatement()) |node| {1037 const while_statement = try p.parseWhileStatement();
870 node.cast(Node.While).?.inline_token = inline_token;1038 if (while_statement != 0) return while_statement;
871 return node;1039
872 }1040 if (inline_token == null) return null_node;
873 if (inline_token == null) return null;
8741041
875 // If we've seen "inline", there should have been a "for" or "while"1042 // If we've seen "inline", there should have been a "for" or "while"
876 try p.errors.append(p.gpa, .{1043 return p.fail(.expected_inlinable);
877 .ExpectedInlinable = .{ .token = p.tok_i },
878 });
879 return error.ParseError;
880 }1044 }
8811045
1046 /// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload
882 /// ForStatement1047 /// ForStatement
883 /// <- ForPrefix BlockExpr ( KEYWORD_else Statement )?1048 /// <- ForPrefix BlockExpr ( KEYWORD_else Statement )?
884 /// / ForPrefix AssignExpr ( SEMICOLON / KEYWORD_else Statement )1049 /// / ForPrefix AssignExpr ( SEMICOLON / KEYWORD_else Statement )
885 fn parseForStatement(p: *Parser) !?*Node {1050 fn parseForStatement(p: *Parser) !Node.Index {
886 const node = (try p.parseForPrefix()) orelse return null;1051 const for_token = p.eatToken(.keyword_for) orelse return null_node;
887 const for_prefix = node.cast(Node.For).?;1052 _ = try p.expectToken(.l_paren);
8881053 const array_expr = try p.expectExpr();
889 if (try p.parseBlockExpr()) |block_expr_node| {1054 _ = try p.expectToken(.r_paren);
890 for_prefix.body = block_expr_node;1055 const found_payload = try p.parsePtrIndexPayload();
8911056 if (found_payload == 0) try p.warn(.expected_loop_payload);
892 if (p.eatToken(.Keyword_else)) |else_token| {1057
893 const statement_node = try p.expectNode(parseStatement, .{1058 // TODO propose to change the syntax so that semicolons are always required
894 .InvalidToken = .{ .token = p.tok_i },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 },
895 });1076 });
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;
906 }1077 }
9071078 else_required = true;
908 return node;1079 break :blk assign_expr;
909 }1080 };
9101081 const else_token = p.eatToken(.keyword_else) orelse {
911 for_prefix.body = try p.expectNode(parseAssignExpr, .{1082 if (else_required) {
912 .ExpectedBlockOrAssignment = .{ .token = p.tok_i },1083 try p.warn(.expected_semi_or_else);
913 });1084 }
9141085 return p.addNode(.{
915 if (p.eatToken(.Semicolon) != null) return node;1086 .tag = .for_simple,
9161087 .main_token = for_token,
917 if (p.eatToken(.Keyword_else)) |else_token| {1088 .data = .{
918 const statement_node = try p.expectNode(parseStatement, .{1089 .lhs = array_expr,
919 .ExpectedStatement = .{ .token = p.tok_i },1090 .rhs = then_expr,
1091 },
920 });1092 });
9211093 };
922 const else_node = try p.arena.allocator.create(Node.Else);1094 return p.addNode(.{
923 else_node.* = .{1095 .tag = .@"for",
924 .else_token = else_token,1096 .main_token = for_token,
925 .payload = null,1097 .data = .{
926 .body = statement_node,1098 .lhs = array_expr,
927 };1099 .rhs = try p.addExtra(Node.If{
928 for_prefix.@"else" = else_node;1100 .then_expr = then_expr,
929 return node;1101 .else_expr = try p.expectStatement(),
930 }1102 }),
9311103 },
932 try p.errors.append(p.gpa, .{
933 .ExpectedSemiOrElse = .{ .token = p.tok_i },
934 });1104 });
935
936 return node;
937 }1105 }
9381106
1107 /// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr?
939 /// WhileStatement1108 /// WhileStatement
940 /// <- WhilePrefix BlockExpr ( KEYWORD_else Payload? Statement )?1109 /// <- WhilePrefix BlockExpr ( KEYWORD_else Payload? Statement )?
941 /// / WhilePrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement )1110 /// / WhilePrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement )
942 fn parseWhileStatement(p: *Parser) !?*Node {1111 fn parseWhileStatement(p: *Parser) !Node.Index {
943 const node = (try p.parseWhilePrefix()) orelse return null;1112 const while_token = p.eatToken(.keyword_while) orelse return null_node;
944 const while_prefix = node.cast(Node.While).?;1113 _ = try p.expectToken(.l_paren);
9451114 const condition = try p.expectExpr();
946 if (try p.parseBlockExpr()) |block_expr_node| {1115 _ = try p.expectToken(.r_paren);
947 while_prefix.body = block_expr_node;1116 const then_payload = try p.parsePtrPayload();
9481117 const cont_expr = try p.parseWhileContinueExpr();
949 if (p.eatToken(.Keyword_else)) |else_token| {1118
950 const payload = try p.parsePayload();1119 // TODO propose to change the syntax so that semicolons are always required
9511120 // inside while statements, even if there is an `else`.
952 const statement_node = try p.expectNode(parseStatement, .{1121 var else_required = false;
953 .InvalidToken = .{ .token = p.tok_i },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 },
954 });1180 });
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;
965 }1181 }
9661182 };
967 return node;1183 const else_payload = try p.parsePayload();
968 }1184 const else_expr = try p.expectStatement();
9691185 return p.addNode(.{
970 while_prefix.body = try p.expectNode(parseAssignExpr, .{1186 .tag = .@"while",
971 .ExpectedBlockOrAssignment = .{ .token = p.tok_i },1187 .main_token = while_token,
972 });1188 .data = .{
9731189 .lhs = condition,
974 if (p.eatToken(.Semicolon) != null) return node;1190 .rhs = try p.addExtra(Node.While{
9751191 .cont_expr = cont_expr,
976 if (p.eatToken(.Keyword_else)) |else_token| {1192 .then_expr = then_expr,
977 const payload = try p.parsePayload();1193 .else_expr = else_expr,
9781194 }),
979 const statement_node = try p.expectNode(parseStatement, .{1195 },
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 },
995 });1196 });
996
997 return node;
998 }1197 }
9991198
1000 /// BlockExprStatement1199 /// BlockExprStatement
1001 /// <- BlockExpr1200 /// <- BlockExpr
1002 /// / AssignExpr SEMICOLON1201 /// / AssignExpr SEMICOLON
1003 fn parseBlockExprStatement(p: *Parser) !?*Node {1202 fn parseBlockExprStatement(p: *Parser) !Node.Index {
1004 if (try p.parseBlockExpr()) |node| return node;1203 const block_expr = try p.parseBlockExpr();
1005 if (try p.parseAssignExpr()) |node| {1204 if (block_expr != 0) {
1006 _ = try p.expectTokenRecoverable(.Semicolon);1205 return block_expr;
1007 return node;
1008 }1206 }
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;
1010 }1213 }
10111214
1012 /// BlockExpr <- BlockLabel? Block1215 fn expectBlockExprStatement(p: *Parser) !Node.Index {
1013 fn parseBlockExpr(p: *Parser) Error!?*Node {1216 const node = try p.parseBlockExprStatement();
1014 var colon: TokenIndex = undefined;1217 if (node == 0) {
1015 const label_token = p.parseBlockLabel(&colon);1218 return p.fail(.expected_block_or_expr);
1016 const block_node = (try p.parseBlock(label_token)) orelse {1219 }
1017 if (label_token) |label| {1220 return node;
1018 p.putBackToken(label + 1); // ":"
1019 p.putBackToken(label); // IDENTIFIER
1020 }
1021 return null;
1022 };
1023 return block_node;
1024 }1221 }
10251222
1026 /// AssignExpr <- Expr (AssignOp Expr)?1223 /// BlockExpr <- BlockLabel? Block
1027 fn parseAssignExpr(p: *Parser) !?*Node {1224 fn parseBlockExpr(p: *Parser) Error!Node.Index {
1028 return p.parseBinOpExpr(parseAssignOp, parseExpr, .Once);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 }
1029 }1239 }
10301240
1031 /// Expr <- BoolOrExpr1241 /// AssignExpr <- Expr (AssignOp Expr)?
1032 fn parseExpr(p: *Parser) Error!?*Node {1242 /// AssignOp
1033 return p.parsePrefixOpExpr(parseTry, parseBoolOrExpr);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 });
1034 }1286 }
10351287
1036 /// BoolOrExpr <- BoolAndExpr (KEYWORD_or BoolAndExpr)*1288 fn expectAssignExpr(p: *Parser) !Node.Index {
1037 fn parseBoolOrExpr(p: *Parser) !?*Node {1289 const expr = try p.parseAssignExpr();
1038 return p.parseBinOpExpr(1290 if (expr == 0) {
1039 SimpleBinOpParseFn(.Keyword_or, .BoolOr),1291 return p.fail(.expected_expr_or_assignment);
1040 parseBoolAndExpr,1292 }
1041 .Infinitely,1293 return expr;
1042 );
1043 }1294 }
10441295
1045 /// BoolAndExpr <- CompareExpr (KEYWORD_and CompareExpr)*1296 /// Expr <- BoolOrExpr
1046 fn parseBoolAndExpr(p: *Parser) !?*Node {1297 fn parseExpr(p: *Parser) Error!Node.Index {
1047 return p.parseBinOpExpr(1298 return p.parseBoolOrExpr();
1048 SimpleBinOpParseFn(.Keyword_and, .BoolAnd),
1049 parseCompareExpr,
1050 .Infinitely,
1051 );
1052 }1299 }
10531300
1054 /// CompareExpr <- BitwiseExpr (CompareOp BitwiseExpr)?1301 fn expectExpr(p: *Parser) Error!Node.Index {
1055 fn parseCompareExpr(p: *Parser) !?*Node {1302 const node = try p.parseExpr();
1056 return p.parseBinOpExpr(parseCompareOp, parseBitwiseExpr, .Once);1303 if (node == 0) {
1304 return p.fail(.expected_expr);
1305 } else {
1306 return node;
1307 }
1057 }1308 }
10581309
1059 /// BitwiseExpr <- BitShiftExpr (BitwiseOp BitShiftExpr)*1310 /// BoolOrExpr <- BoolAndExpr (KEYWORD_or BoolAndExpr)*
1060 fn parseBitwiseExpr(p: *Parser) !?*Node {1311 fn parseBoolOrExpr(p: *Parser) Error!Node.Index {
1061 return p.parseBinOpExpr(parseBitwiseOp, parseBitShiftExpr, .Infinitely);1312 var res = try p.parseBoolAndExpr();
1062 }1313 if (res == 0) return null_node;
10631314
1064 /// BitShiftExpr <- AdditionExpr (BitShiftOp AdditionExpr)*1315 while (true) {
1065 fn parseBitShiftExpr(p: *Parser) !?*Node {1316 switch (p.token_tags[p.tok_i]) {
1066 return p.parseBinOpExpr(parseBitShiftOp, parseAdditionExpr, .Infinitely);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 }
1067 }1335 }
10681336
1069 /// AdditionExpr <- MultiplyExpr (AdditionOp MultiplyExpr)*1337 /// BoolAndExpr <- CompareExpr (KEYWORD_and CompareExpr)*
1070 fn parseAdditionExpr(p: *Parser) !?*Node {1338 fn parseBoolAndExpr(p: *Parser) !Node.Index {
1071 return p.parseBinOpExpr(parseAdditionOp, parseMultiplyExpr, .Infinitely);1339 var res = try p.parseCompareExpr();
1072 }1340 if (res == 0) return null_node;
10731341
1074 /// MultiplyExpr <- PrefixExpr (MultiplyOp PrefixExpr)*1342 while (true) {
1075 fn parseMultiplyExpr(p: *Parser) !?*Node {1343 switch (p.token_tags[p.tok_i]) {
1076 return p.parseBinOpExpr(parseMultiplyOp, parsePrefixExpr, .Infinitely);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 }
1077 }1367 }
10781368
1079 /// PrefixExpr <- PrefixOp* PrimaryExpr1369 /// CompareExpr <- BitwiseExpr (CompareOp BitwiseExpr)?
1080 fn parsePrefixExpr(p: *Parser) !?*Node {1370 /// CompareOp
1081 return p.parsePrefixOpExpr(parsePrefixOp, parsePrimaryExpr);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 });
1082 }1398 }
10831399
1084 /// PrimaryExpr1400 /// BitwiseExpr <- BitShiftExpr (BitwiseOp BitShiftExpr)*
1085 /// <- AsmExpr1401 /// BitwiseOp
1086 /// / IfExpr1402 /// <- AMPERSAND
1087 /// / KEYWORD_break BreakLabel? Expr?1403 /// / CARET
1088 /// / KEYWORD_comptime Expr1404 /// / PIPE
1089 /// / KEYWORD_nosuspend Expr1405 /// / KEYWORD_orelse
1090 /// / KEYWORD_continue BreakLabel?1406 /// / KEYWORD_catch Payload?
1091 /// / KEYWORD_resume Expr1407 fn parseBitwiseExpr(p: *Parser) !Node.Index {
1092 /// / KEYWORD_return Expr?1408 var res = try p.parseBitShiftExpr();
1093 /// / BlockLabel? LoopExpr1409 if (res == 0) return null_node;
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 }
11121410
1113 if (p.eatToken(.Keyword_comptime)) |token| {1411 while (true) {
1114 const expr_node = try p.expectNode(parseExpr, .{1412 const tag: Node.Tag = switch (p.token_tags[p.tok_i]) {
1115 .ExpectedExpr = .{ .token = p.tok_i },1413 .ampersand => .bit_and,
1116 });1414 .caret => .bit_xor,
1117 const node = try p.arena.allocator.create(Node.Comptime);1415 .pipe => .bit_or,
1118 node.* = .{1416 .keyword_orelse => .@"orelse",
1119 .doc_comments = null,1417 .keyword_catch => {
1120 .comptime_token = token,1418 const catch_token = p.nextToken();
1121 .expr = expr_node,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,
1122 };1435 };
1123 return &node.base;1436 res = try p.addNode(.{
1124 }1437 .tag = tag,
11251438 .main_token = p.nextToken(),
1126 if (p.eatToken(.Keyword_nosuspend)) |token| {1439 .data = .{
1127 const expr_node = try p.expectNode(parseExpr, .{1440 .lhs = res,
1128 .ExpectedExpr = .{ .token = p.tok_i },1441 .rhs = try p.expectBitShiftExpr(),
1442 },
1129 });1443 });
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;
1136 }1444 }
1445 }
11371446
1138 if (p.eatToken(.Keyword_continue)) |token| {1447 fn expectBitwiseExpr(p: *Parser) Error!Node.Index {
1139 const label = try p.parseBreakLabel();1448 const node = try p.parseBitwiseExpr();
1140 const node = try Node.ControlFlowExpression.create(&p.arena.allocator, .{1449 if (node == 0) {
1141 .tag = .Continue,1450 return p.fail(.invalid_token);
1142 .ltoken = token,1451 } else {
1143 }, .{1452 return node;
1144 .label = label,
1145 .rhs = null,
1146 });
1147 return &node.base;
1148 }1453 }
1454 }
11491455
1150 if (p.eatToken(.Keyword_resume)) |token| {1456 /// BitShiftExpr <- AdditionExpr (BitShiftOp AdditionExpr)*
1151 const expr_node = try p.expectNode(parseExpr, .{1457 /// BitShiftOp
1152 .ExpectedExpr = .{ .token = p.tok_i },1458 /// <- LARROW2
1153 });1459 /// / RARROW2
1154 const node = try p.arena.allocator.create(Node.SimplePrefixOp);1460 fn parseBitShiftExpr(p: *Parser) Error!Node.Index {
1155 node.* = .{1461 var res = try p.parseAdditionExpr();
1156 .base = .{ .tag = .Resume },1462 if (res == 0) return null_node;
1157 .op_token = token,
1158 .rhs = expr_node,
1159 };
1160 return &node.base;
1161 }
11621463
1163 if (p.eatToken(.Keyword_return)) |token| {1464 while (true) {
1164 const expr_node = try p.parseExpr();1465 const tag: Node.Tag = switch (p.token_tags[p.tok_i]) {
1165 const node = try Node.ControlFlowExpression.create(&p.arena.allocator, .{1466 .angle_bracket_angle_bracket_left => .bit_shift_left,
1166 .tag = .Return,1467 .angle_bracket_angle_bracket_right => .bit_shift_right,
1167 .ltoken = token,1468 else => return res,
1168 }, .{1469 };
1169 .rhs = expr_node,1470 res = try p.addNode(.{
1471 .tag = tag,
1472 .main_token = p.nextToken(),
1473 .data = .{
1474 .lhs = res,
1475 .rhs = try p.expectAdditionExpr(),
1476 },
1170 });1477 });
1171 return &node.base;
1172 }1478 }
1479 }
11731480
1174 var colon: TokenIndex = undefined;1481 fn expectBitShiftExpr(p: *Parser) Error!Node.Index {
1175 const label = p.parseBlockLabel(&colon);1482 const node = try p.parseBitShiftExpr();
1176 if (try p.parseLoopExpr()) |node| {1483 if (node == 0) {
1177 if (node.cast(Node.For)) |for_node| {1484 return p.fail(.invalid_token);
1178 for_node.label = label;1485 } else {
1179 } else if (node.cast(Node.While)) |while_node| {
1180 while_node.label = label;
1181 } else unreachable;
1182 return node;1486 return node;
1183 }1487 }
1184 if (label) |token| {1488 }
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;
11911489
1192 return null;1490 /// AdditionExpr <- MultiplyExpr (AdditionOp MultiplyExpr)*
1193 }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)?1501 while (true) {
1196 fn parseIfExpr(p: *Parser) !?*Node {1502 const tag: Node.Tag = switch (p.token_tags[p.tok_i]) {
1197 return p.parseIf(parseExpr);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 }
1198 }1519 }
11991520
1200 /// Block <- LBRACE Statement* RBRACE1521 fn expectAdditionExpr(p: *Parser) Error!Node.Index {
1201 fn parseBlock(p: *Parser, label_token: ?TokenIndex) !?*Node {1522 const node = try p.parseAdditionExpr();
1202 const lbrace = p.eatToken(.LBrace) orelse return null;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);1529 /// MultiplyExpr <- PrefixExpr (MultiplyOp PrefixExpr)*
1205 defer statements.deinit();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
1207 while (true) {1541 while (true) {
1208 const statement = (p.parseStatement() catch |err| switch (err) {1542 const tag: Node.Tag = switch (p.token_tags[p.tok_i]) {
1209 error.OutOfMemory => return error.OutOfMemory,1543 .pipe_pipe => .merge_error_sets,
1210 error.ParseError => {1544 .asterisk => .mul,
1211 // try to skip to the next statement1545 .slash => .div,
1212 p.findNextStmt();1546 .percent => .mod,
1213 continue;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(),
1214 },1557 },
1215 }) orelse break;1558 });
1216 try statements.append(statement);
1217 }1559 }
1560 }
12181561
1219 const rbrace = try p.expectToken(.RBrace);1562 fn expectMultiplyExpr(p: *Parser) Error!Node.Index {
12201563 const node = try p.parseMultiplyExpr();
1221 const statements_len = @intCast(NodeIndex, statements.items.len);1564 if (node == 0) {
12221565 return p.fail(.invalid_token);
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;
1242 }1566 }
1567 return node;
1243 }1568 }
12441569
1245 /// LoopExpr <- KEYWORD_inline? (ForExpr / WhileExpr)1570 /// PrefixExpr <- PrefixOp* PrimaryExpr
1246 fn parseLoopExpr(p: *Parser) !?*Node {1571 /// PrefixOp
1247 const inline_token = p.eatToken(.Keyword_inline);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| {1600 fn expectPrefixExpr(p: *Parser) Error!Node.Index {
1250 node.cast(Node.For).?.inline_token = inline_token;1601 const node = try p.parsePrefixExpr();
1251 return node;1602 if (node == 0) {
1603 return p.fail(.expected_prefix_expr);
1252 }1604 }
1605 return node;
1606 }
12531607
1254 if (try p.parseWhileExpr()) |node| {1608 /// TypeExpr <- PrefixTypeOp* ErrorUnionExpr
1255 node.cast(Node.While).?.inline_token = inline_token;1609 /// PrefixTypeOp
1256 return node;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(),
1257 }1858 }
1859 }
12581860
1259 if (inline_token == null) return null;1861 fn expectTypeExpr(p: *Parser) Error!Node.Index {
12601862 const node = try p.parseTypeExpr();
1261 // If we've seen "inline", there should have been a "for" or "while"1863 if (node == 0) {
1262 try p.errors.append(p.gpa, .{1864 return p.fail(.expected_type_expr);
1263 .ExpectedInlinable = .{ .token = p.tok_i },1865 }
1264 });1866 return node;
1265 return error.ParseError;
1266 }1867 }
12671868
1268 /// ForExpr <- ForPrefix Expr (KEYWORD_else Expr)?1869 /// PrimaryExpr
1269 fn parseForExpr(p: *Parser) !?*Node {1870 /// <- AsmExpr
1270 const node = (try p.parseForPrefix()) orelse return null;1871 /// / IfExpr
1271 const for_prefix = node.cast(Node.For).?;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, .{1995 /// IfExpr <- IfPrefix Expr (KEYWORD_else Payload? Expr)?
1274 .ExpectedExpr = .{ .token = p.tok_i },1996 fn parseIfExpr(p: *Parser) !Node.Index {
1275 });1997 return p.parseIf(parseExpr);
1276 for_prefix.body = body_node;1998 }
12771999
1278 if (p.eatToken(.Keyword_else)) |else_token| {2000 /// Block <- LBRACE Statement* RBRACE
1279 const body = try p.expectNode(parseExpr, .{2001 fn parseBlock(p: *Parser) !Node.Index {
1280 .ExpectedExpr = .{ .token = p.tok_i },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 },
1281 });2012 });
2013 }
12822014
1283 const else_node = try p.arena.allocator.create(Node.Else);2015 const stmt_one = try p.expectStatementRecoverable();
1284 else_node.* = .{2016 if (p.eatToken(.r_brace)) |_| {
1285 .else_token = else_token,2017 const semicolon = p.token_tags[p.tok_i - 2] == .semicolon;
1286 .payload = null,2018 return p.addNode(.{
1287 .body = body,2019 .tag = if (semicolon) .block_two_semicolon else .block_two,
1288 };2020 .main_token = lbrace,
12892021 .data = .{
1290 for_prefix.@"else" = else_node;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 });
1291 }2038 }
12922039
1293 return node;2040 var statements = std.ArrayList(Node.Index).init(p.gpa);
1294 }2041 defer statements.deinit();
12952042
1296 /// WhileExpr <- WhilePrefix Expr (KEYWORD_else Payload? Expr)?2043 try statements.appendSlice(&.{ stmt_one, stmt_two });
1297 fn parseWhileExpr(p: *Parser) !?*Node {
1298 const node = (try p.parseWhilePrefix()) orelse return null;
1299 const while_prefix = node.cast(Node.While).?;
13002044
1301 const body_node = try p.expectNode(parseExpr, .{2045 while (true) {
1302 .ExpectedExpr = .{ .token = p.tok_i },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 },
1303 });2061 });
1304 while_prefix.body = body_node;2062 }
13052063
1306 if (p.eatToken(.Keyword_else)) |else_token| {2064 /// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload
1307 const payload = try p.parsePayload();2065 /// ForExpr <- ForPrefix Expr (KEYWORD_else Expr)?
1308 const body = try p.expectNode(parseExpr, .{2066 fn parseForExpr(p: *Parser) !Node.Index {
1309 .ExpectedExpr = .{ .token = p.tok_i },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 },
1310 });2083 });
13112084 };
1312 const else_node = try p.arena.allocator.create(Node.Else);2085 const else_expr = try p.expectExpr();
1313 else_node.* = .{2086 return p.addNode(.{
1314 .else_token = else_token,2087 .tag = .@"for",
1315 .payload = payload,2088 .main_token = for_token,
1316 .body = body,2089 .data = .{
1317 };2090 .lhs = array_expr,
13182091 .rhs = try p.addExtra(Node.If{
1319 while_prefix.@"else" = else_node;2092 .then_expr = then_expr,
1320 }2093 .else_expr = else_expr,
13212094 }),
1322 return node;2095 },
2096 });
1323 }2097 }
13242098
1325 /// CurlySuffixExpr <- TypeExpr InitList?2099 /// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr?
1326 fn parseCurlySuffixExpr(p: *Parser) !?*Node {2100 /// WhileExpr <- WhilePrefix Expr (KEYWORD_else Payload? Expr)?
1327 const lhs = (try p.parseTypeExpr()) orelse return null;2101 fn parseWhileExpr(p: *Parser) !Node.Index {
1328 const suffix_op = (try p.parseInitList(lhs)) orelse return lhs;2102 const while_token = p.eatToken(.keyword_while) orelse return null_node;
1329 return suffix_op;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 });
1330 }2148 }
13312149
2150 /// CurlySuffixExpr <- TypeExpr InitList?
1332 /// InitList2151 /// InitList
1333 /// <- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE2152 /// <- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE
1334 /// / LBRACE Expr (COMMA Expr)* COMMA? RBRACE2153 /// / LBRACE Expr (COMMA Expr)* COMMA? RBRACE
1335 /// / LBRACE RBRACE2154 /// / LBRACE RBRACE
1336 fn parseInitList(p: *Parser, lhs: *Node) !?*Node {2155 fn parseCurlySuffixExpr(p: *Parser) !Node.Index {
1337 const lbrace = p.eatToken(.LBrace) orelse return null;2156 const lhs = try p.parseTypeExpr();
1338 var init_list = std.ArrayList(*Node).init(p.gpa);2157 if (lhs == 0) return null_node;
1339 defer init_list.deinit();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| {
1342 try init_list.append(field_init);2190 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| {2192 while (true) {
1358 try init_list.append(expr);2193 const next = try p.expectFieldInit();
1359 while (p.eatToken(.Comma)) |_| {
1360 const next = (try p.parseExpr()) orelse break;
1361 try init_list.append(next);2194 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 }
1362 }2213 }
1363 const node = try Node.ArrayInitializer.alloc(&p.arena.allocator, init_list.items.len);2214 const span = try p.listToSpan(init_list.items);
1364 node.* = .{2215 return p.addNode(.{
1365 .lhs = lhs,2216 .tag = if (p.token_tags[p.tok_i - 2] == .comma) .struct_init_comma else .struct_init,
1366 .rtoken = try p.expectToken(.RBrace),2217 .main_token = lbrace,
1367 .list_len = init_list.items.len,2218 .data = .{
1368 };2219 .lhs = lhs,
1369 std.mem.copy(*Node, node.list(), init_list.items);2220 .rhs = try p.addExtra(Node.SubRange{
1370 return &node.base;2221 .start = span.start,
2222 .end = span.end,
2223 }),
2224 },
2225 });
1371 }2226 }
13722227
1373 const node = try p.arena.allocator.create(Node.StructInitializer);2228 const elem_init = try p.expectExpr();
1374 node.* = .{2229 const comma_one = p.eatToken(.comma);
1375 .lhs = lhs,2230 if (p.eatToken(.r_brace)) |_| {
1376 .rtoken = try p.expectToken(.RBrace),2231 return p.addNode(.{
1377 .list_len = 0,2232 .tag = if (comma_one != null) .array_init_one_comma else .array_init_one,
1378 };2233 .main_token = lbrace,
1379 return &node.base;2234 .data = .{
1380 }2235 .lhs = lhs,
2236 .rhs = elem_init,
2237 },
2238 });
2239 }
2240 if (comma_one == null) {
2241 try p.warnExpected(.comma);
2242 }
13812243
1382 /// InitList2244 var init_list = std.ArrayList(Node.Index).init(p.gpa);
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);
1389 defer init_list.deinit();2245 defer init_list.deinit();
13902246
1391 if (try p.parseFieldInit()) |field_init| {2247 try init_list.append(elem_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 }
14062248
1407 if (try p.parseExpr()) |expr| {2249 var trailing_comma = true;
1408 try init_list.append(expr);2250 var next = try p.parseExpr();
1409 while (p.eatToken(.Comma)) |_| {2251 while (next != 0) : (next = try p.parseExpr()) {
1410 const next = (try p.parseExpr()) orelse break;2252 try init_list.append(next);
1411 try init_list.append(next);2253 if (p.eatToken(.comma) == null) {
2254 trailing_comma = false;
2255 break;
1412 }2256 }
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;
1421 }2257 }
14222258 _ = try p.expectToken(.r_brace);
1423 const node = try p.arena.allocator.create(Node.StructInitializerDot);2259 const span = try p.listToSpan(init_list.items);
1424 node.* = .{2260 return p.addNode(.{
1425 .dot = dot,2261 .tag = if (trailing_comma) .array_init_comma else .array_init,
1426 .rtoken = try p.expectToken(.RBrace),2262 .main_token = lbrace,
1427 .list_len = 0,2263 .data = .{
1428 };2264 .lhs = lhs,
1429 return &node.base;2265 .rhs = try p.addExtra(Node.SubRange{
1430 }2266 .start = span.start,
14312267 .end = span.end,
1432 /// TypeExpr <- PrefixTypeOp* ErrorUnionExpr2268 }),
1433 fn parseTypeExpr(p: *Parser) Error!?*Node {2269 },
1434 return p.parsePrefixOpExpr(parsePrefixTypeOp, parseErrorUnionExpr);2270 });
1435 }2271 }
14362272
1437 /// ErrorUnionExpr <- SuffixExpr (EXCLAMATIONMARK TypeExpr)?2273 /// ErrorUnionExpr <- SuffixExpr (EXCLAMATIONMARK TypeExpr)?
1438 fn parseErrorUnionExpr(p: *Parser) !?*Node {2274 fn parseErrorUnionExpr(p: *Parser) !Node.Index {
1439 const suffix_expr = (try p.parseSuffixExpr()) orelse return null;2275 const suffix_expr = try p.parseSuffixExpr();
14402276 if (suffix_expr == 0) return null_node;
1441 if (try SimpleBinOpParseFn(.Bang, .ErrorUnion)(p)) |node| {2277 const bang = p.eatToken(.bang) orelse return suffix_expr;
1442 const error_union = node.castTag(.ErrorUnion).?;2278 return p.addNode(.{
1443 const type_expr = try p.expectNode(parseTypeExpr, .{2279 .tag = .error_union,
1444 .ExpectedTypeExpr = .{ .token = p.tok_i },2280 .main_token = bang,
1445 });2281 .data = .{
1446 error_union.lhs = suffix_expr;2282 .lhs = suffix_expr,
1447 error_union.rhs = type_expr;2283 .rhs = try p.expectTypeExpr(),
1448 return node;2284 },
1449 }2285 });
1450
1451 return suffix_expr;
1452 }2286 }
14532287
1454 /// SuffixExpr2288 /// SuffixExpr
1455 /// <- KEYWORD_async PrimaryTypeExpr SuffixOp* FnCallArguments2289 /// <- KEYWORD_async PrimaryTypeExpr SuffixOp* FnCallArguments
1456 /// / PrimaryTypeExpr (SuffixOp / FnCallArguments)*2290 /// / PrimaryTypeExpr (SuffixOp / FnCallArguments)*
1457 fn parseSuffixExpr(p: *Parser) !?*Node {2291 /// FnCallArguments <- LPAREN ExprList RPAREN
1458 const maybe_async = p.eatToken(.Keyword_async);2292 /// ExprList <- (Expr COMMA)* Expr?
1459 if (maybe_async) |async_token| {2293 fn parseSuffixExpr(p: *Parser) !Node.Index {
1460 const token_fn = p.eatToken(.Keyword_fn);2294 if (p.eatToken(.keyword_async)) |async_token| {
1461 if (token_fn != null) {2295 var res = try p.expectPrimaryTypeExpr();
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 });
14742296
1475 while (try p.parseSuffixOp(res)) |node| {2297 while (true) {
2298 const node = try p.parseSuffixOp(res);
2299 if (node == 0) break;
1476 res = node;2300 res = node;
1477 }2301 }
14782302 const lparen = p.nextToken();
1479 const params = (try p.parseFnCallArguments()) orelse {2303 if (p.token_tags[lparen] != .l_paren) {
1480 try p.errors.append(p.gpa, .{2304 p.tok_i -= 1;
1481 .ExpectedParamList = .{ .token = p.tok_i },2305 try p.warn(.expected_param_list);
1482 });
1483 // ignore this, continue parsing
1484 return res;2306 return res;
1485 };2307 }
1486 defer p.gpa.free(params.list);2308 if (p.eatToken(.r_paren)) |_| {
1487 const node = try Node.Call.alloc(&p.arena.allocator, params.list.len);2309 return p.addNode(.{
1488 node.* = .{2310 .tag = .async_call_one,
1489 .lhs = res,2311 .main_token = lparen,
1490 .params_len = params.list.len,2312 .data = .{
1491 .async_token = async_token,2313 .lhs = res,
1492 .rtoken = params.rparen,2314 .rhs = 0,
1493 };2315 },
1494 std.mem.copy(*Node, node.params(), params.list);2316 });
1495 return &node.base;2317 }
1496 }2318 const param_one = try p.expectExpr();
1497 if (try p.parsePrimaryTypeExpr()) |expr| {2319 const comma_one = p.eatToken(.comma);
1498 var res = expr;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
1500 while (true) {2339 while (true) {
1501 if (try p.parseSuffixOp(res)) |node| {2340 const next = try p.expectExpr();
1502 res = node;2341 try param_list.append(next);
1503 continue;2342 switch (p.token_tags[p.nextToken()]) {
1504 }2343 .comma => {
1505 if (try p.parseFnCallArguments()) |params| {2344 if (p.eatToken(.r_paren)) |_| {
1506 defer p.gpa.free(params.list);2345 const span = try p.listToSpan(param_list.items);
1507 const call = try Node.Call.alloc(&p.arena.allocator, params.list.len);2346 return p.addNode(.{
1508 call.* = .{2347 .tag = .async_call_comma,
1509 .lhs = res,2348 .main_token = lparen,
1510 .params_len = params.list.len,2349 .data = .{
1511 .async_token = null,2350 .lhs = res,
1512 .rtoken = params.rparen,2351 .rhs = try p.addExtra(Node.SubRange{
1513 };2352 .start = span.start,
1514 std.mem.copy(*Node, call.params(), params.list);2353 .end = span.end,
1515 res = &call.base;2354 }),
1516 continue;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 },
1517 }2383 }
1518 break;
1519 }2384 }
1520 return res;
1521 }2385 }
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 }
1524 }2476 }
15252477
1526 /// PrimaryTypeExpr2478 /// PrimaryTypeExpr
...@@ -1528,6 +2480,7 @@ const Parser = struct {...@@ -1528,6 +2480,7 @@ const Parser = struct {
1528 /// / CHAR_LITERAL2480 /// / CHAR_LITERAL
1529 /// / ContainerDecl2481 /// / ContainerDecl
1530 /// / DOT IDENTIFIER2482 /// / DOT IDENTIFIER
2483 /// / DOT InitList
1531 /// / ErrorSetDecl2484 /// / ErrorSetDecl
1532 /// / FLOAT2485 /// / FLOAT
1533 /// / FnProto2486 /// / FnProto
...@@ -1546,260 +2499,546 @@ const Parser = struct {...@@ -1546,260 +2499,546 @@ const Parser = struct {
1546 /// / KEYWORD_unreachable2499 /// / KEYWORD_unreachable
1547 /// / STRINGLITERAL2500 /// / STRINGLITERAL
1548 /// / SwitchExpr2501 /// / 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
1615 /// ContainerDecl <- (KEYWORD_extern / KEYWORD_packed)? ContainerDeclAuto2502 /// ContainerDecl <- (KEYWORD_extern / KEYWORD_packed)? ContainerDeclAuto
1616 fn parseContainerDecl(p: *Parser) !?*Node {2503 /// ContainerDeclAuto <- ContainerDeclType LBRACE ContainerMembers RBRACE
1617 const layout_token = p.eatToken(.Keyword_extern) orelse2504 /// InitList
1618 p.eatToken(.Keyword_packed);2505 /// <- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE
16192506 /// / LBRACE Expr (COMMA Expr)* COMMA? RBRACE
1620 const node = (try p.parseContainerDeclAuto()) orelse {2507 /// / LBRACE RBRACE
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
1629 /// ErrorSetDecl <- KEYWORD_error LBRACE IdentifierList RBRACE2508 /// 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
1651 /// GroupedExpr <- LPAREN Expr RPAREN2509 /// 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
1668 /// IfTypeExpr <- IfPrefix TypeExpr (KEYWORD_else Payload? TypeExpr)?2510 /// IfTypeExpr <- IfPrefix TypeExpr (KEYWORD_else Payload? TypeExpr)?
1669 fn parseIfTypeExpr(p: *Parser) !?*Node {
1670 return p.parseIf(parseTypeExpr);
1671 }
1672
1673 /// LabeledTypeExpr2511 /// LabeledTypeExpr
1674 /// <- BlockLabel Block2512 /// <- BlockLabel Block
1675 /// / BlockLabel? LoopTypeExpr2513 /// / BlockLabel? LoopTypeExpr
1676 fn parseLabeledTypeExpr(p: *Parser) !?*Node {2514 /// LoopTypeExpr <- KEYWORD_inline? (ForTypeExpr / WhileTypeExpr)
1677 var colon: TokenIndex = undefined;2515 fn parsePrimaryTypeExpr(p: *Parser) !Node.Index {
1678 const label = p.parseBlockLabel(&colon);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| {2601 .builtin => return p.parseBuiltinCall(),
1681 if (try p.parseBlock(label_token)) |node| return node;2602 .keyword_fn => return p.parseFnProto(),
1682 }2603 .keyword_if => return p.parseIf(parseTypeExpr),
2604 .keyword_switch => return p.expectSwitchExpr(),
16832605
1684 if (try p.parseLoopTypeExpr()) |node| {2606 .keyword_extern,
1685 switch (node.tag) {2607 .keyword_packed,
1686 .For => node.cast(Node.For).?.label = label,2608 => {
1687 .While => node.cast(Node.While).?.label = label,2609 p.tok_i += 1;
1688 else => unreachable,2610 return p.parseContainerDeclAuto();
1689 }2611 },
1690 return node;
1691 }
16922612
1693 if (label) |token| {2613 .keyword_struct,
1694 p.putBackToken(colon);2614 .keyword_opaque,
1695 p.putBackToken(token);2615 .keyword_enum,
1696 }2616 .keyword_union,
1697 return null;2617 => return p.parseContainerDeclAuto(),
1698 }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)2785 const elem_init_one = try p.expectExpr();
1701 fn parseLoopTypeExpr(p: *Parser) !?*Node {2786 const comma_one = p.eatToken(.comma);
1702 const inline_token = p.eatToken(.Keyword_inline);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| {2868 while (true) {
1705 node.cast(Node.For).?.inline_token = inline_token;2869 const doc_comment = try p.eatDocComments();
1706 return node;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,
1707 }2923 }
2924 }
17082925
1709 if (try p.parseWhileTypeExpr()) |node| {2926 fn expectPrimaryTypeExpr(p: *Parser) !Node.Index {
1710 node.cast(Node.While).?.inline_token = inline_token;2927 const node = try p.parsePrimaryTypeExpr();
1711 return node;2928 if (node == 0) {
2929 return p.fail(.expected_primary_type_expr);
1712 }2930 }
17132931 return node;
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;
1721 }2932 }
17222933
2934 /// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload
1723 /// ForTypeExpr <- ForPrefix TypeExpr (KEYWORD_else TypeExpr)?2935 /// ForTypeExpr <- ForPrefix TypeExpr (KEYWORD_else TypeExpr)?
1724 fn parseForTypeExpr(p: *Parser) !?*Node {2936 fn parseForTypeExpr(p: *Parser) !Node.Index {
1725 const node = (try p.parseForPrefix()) orelse return null;2937 const for_token = p.eatToken(.keyword_for) orelse return null_node;
1726 const for_prefix = node.cast(Node.For).?;2938 _ = try p.expectToken(.l_paren);
17272939 const array_expr = try p.expectExpr();
1728 const type_expr = try p.expectNode(parseTypeExpr, .{2940 _ = try p.expectToken(.r_paren);
1729 .ExpectedTypeExpr = .{ .token = p.tok_i },2941 const found_payload = try p.parsePtrIndexPayload();
1730 });2942 if (found_payload == 0) try p.warn(.expected_loop_payload);
1731 for_prefix.body = type_expr;2943
17322944 const then_expr = try p.expectExpr();
1733 if (p.eatToken(.Keyword_else)) |else_token| {2945 const else_token = p.eatToken(.keyword_else) orelse {
1734 const else_expr = try p.expectNode(parseTypeExpr, .{2946 return p.addNode(.{
1735 .ExpectedTypeExpr = .{ .token = p.tok_i },2947 .tag = .for_simple,
2948 .main_token = for_token,
2949 .data = .{
2950 .lhs = array_expr,
2951 .rhs = then_expr,
2952 },
1736 });2953 });
17372954 };
1738 const else_node = try p.arena.allocator.create(Node.Else);2955 const else_expr = try p.expectTypeExpr();
1739 else_node.* = .{2956 return p.addNode(.{
1740 .else_token = else_token,2957 .tag = .@"for",
1741 .payload = null,2958 .main_token = for_token,
1742 .body = else_expr,2959 .data = .{
1743 };2960 .lhs = array_expr,
17442961 .rhs = try p.addExtra(Node.If{
1745 for_prefix.@"else" = else_node;2962 .then_expr = then_expr,
1746 }2963 .else_expr = else_expr,
17472964 }),
1748 return node;2965 },
2966 });
1749 }2967 }
17502968
2969 /// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr?
1751 /// WhileTypeExpr <- WhilePrefix TypeExpr (KEYWORD_else Payload? TypeExpr)?2970 /// WhileTypeExpr <- WhilePrefix TypeExpr (KEYWORD_else Payload? TypeExpr)?
1752 fn parseWhileTypeExpr(p: *Parser) !?*Node {2971 fn parseWhileTypeExpr(p: *Parser) !Node.Index {
1753 const node = (try p.parseWhilePrefix()) orelse return null;2972 const while_token = p.eatToken(.keyword_while) orelse return null_node;
1754 const while_prefix = node.cast(Node.While).?;2973 _ = try p.expectToken(.l_paren);
17552974 const condition = try p.expectExpr();
1756 const type_expr = try p.expectNode(parseTypeExpr, .{2975 _ = try p.expectToken(.r_paren);
1757 .ExpectedTypeExpr = .{ .token = p.tok_i },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 },
1758 });3017 });
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;
1779 }3018 }
17803019
1781 /// SwitchExpr <- KEYWORD_switch LPAREN Expr RPAREN LBRACE SwitchProngList RBRACE3020 /// SwitchExpr <- KEYWORD_switch LPAREN Expr RPAREN LBRACE SwitchProngList RBRACE
1782 fn parseSwitchExpr(p: *Parser) !?*Node {3021 fn expectSwitchExpr(p: *Parser) !Node.Index {
1783 const switch_token = p.eatToken(.Keyword_switch) orelse return null;3022 const switch_token = p.assertToken(.keyword_switch);
1784 _ = try p.expectToken(.LParen);3023 _ = try p.expectToken(.l_paren);
1785 const expr_node = try p.expectNode(parseExpr, .{3024 const expr_node = try p.expectExpr();
1786 .ExpectedExpr = .{ .token = p.tok_i },3025 _ = try p.expectToken(.r_paren);
1787 });3026 _ = try p.expectToken(.l_brace);
1788 _ = try p.expectToken(.RParen);
1789 _ = try p.expectToken(.LBrace);
1790 const cases = try p.parseSwitchProngList();3027 const cases = try p.parseSwitchProngList();
1791 defer p.gpa.free(cases);3028 const trailing_comma = p.token_tags[p.tok_i - 1] == .comma;
1792 const rbrace = try p.expectToken(.RBrace);3029 _ = try p.expectToken(.r_brace);
17933030
1794 const node = try Node.Switch.alloc(&p.arena.allocator, cases.len);3031 return p.addNode(.{
1795 node.* = .{3032 .tag = if (trailing_comma) .switch_comma else .@"switch",
1796 .switch_token = switch_token,3033 .main_token = switch_token,
1797 .expr = expr_node,3034 .data = .{
1798 .cases_len = cases.len,3035 .lhs = expr_node,
1799 .rbrace = rbrace,3036 .rhs = try p.addExtra(Node.SubRange{
1800 };3037 .start = cases.start,
1801 std.mem.copy(*Node, node.cases(), cases);3038 .end = cases.end,
1802 return &node.base;3039 }),
3040 },
3041 });
1803 }3042 }
18043043
1805 /// AsmExpr <- KEYWORD_asm KEYWORD_volatile? LPAREN Expr AsmOutput? RPAREN3044 /// AsmExpr <- KEYWORD_asm KEYWORD_volatile? LPAREN Expr AsmOutput? RPAREN
...@@ -1807,1696 +3046,1050 @@ const Parser = struct {...@@ -1807,1696 +3046,1050 @@ const Parser = struct {
1807 /// AsmInput <- COLON AsmInputList AsmClobbers?3046 /// AsmInput <- COLON AsmInputList AsmClobbers?
1808 /// AsmClobbers <- COLON StringList3047 /// AsmClobbers <- COLON StringList
1809 /// StringList <- (STRINGLITERAL COMMA)* STRINGLITERAL?3048 /// StringList <- (STRINGLITERAL COMMA)* STRINGLITERAL?
1810 fn parseAsmExpr(p: *Parser) !?*Node {3049 /// AsmOutputList <- (AsmOutputItem COMMA)* AsmOutputItem?
1811 const asm_token = p.eatToken(.Keyword_asm) orelse return null;3050 /// AsmInputList <- (AsmInputItem COMMA)* AsmInputItem?
1812 const volatile_token = p.eatToken(.Keyword_volatile);3051 fn expectAsmExpr(p: *Parser) !Node.Index {
1813 _ = try p.expectToken(.LParen);3052 const asm_token = p.assertToken(.keyword_asm);
1814 const template = try p.expectNode(parseExpr, .{3053 _ = p.eatToken(.keyword_volatile);
1815 .ExpectedExpr = .{ .token = p.tok_i },3054 _ = try p.expectToken(.l_paren);
1816 });3055 const template = try p.expectExpr();
18173056
1818 var arena_outputs: []Node.Asm.Output = &[0]Node.Asm.Output{};3057 if (p.eatToken(.r_paren)) |rparen| {
1819 var arena_inputs: []Node.Asm.Input = &[0]Node.Asm.Input{};3058 return p.addNode(.{
1820 var arena_clobbers: []*Node = &[0]*Node{};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) {3068 _ = try p.expectToken(.colon);
1823 const outputs = try p.parseAsmOutputList();
1824 defer p.gpa.free(outputs);
1825 arena_outputs = try p.arena.allocator.dupe(Node.Asm.Output, outputs);
18263069
1827 if (p.eatToken(.Colon) != null) {3070 var list = std.ArrayList(Node.Index).init(p.gpa);
1828 const inputs = try p.parseAsmInputList();3071 defer list.deinit();
1829 defer p.gpa.free(inputs);
1830 arena_inputs = try p.arena.allocator.dupe(Node.Asm.Input, inputs);
18313072
1832 if (p.eatToken(.Colon) != null) {3073 while (true) {
1833 const clobbers = try ListParseFn(*Node, parseStringLiteral)(p);3074 const output_item = try p.parseAsmOutputItem();
1834 defer p.gpa.free(clobbers);3075 if (output_item == 0) break;
1835 arena_clobbers = try p.arena.allocator.dupe(*Node, clobbers);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 }
1836 }3113 }
1837 }3114 }
1838 }3115 }
18393116 const rparen = try p.expectToken(.r_paren);
1840 const node = try p.arena.allocator.create(Node.Asm);3117 const span = try p.listToSpan(list.items);
1841 node.* = .{3118 return p.addNode(.{
1842 .asm_token = asm_token,3119 .tag = .@"asm",
1843 .volatile_token = volatile_token,3120 .main_token = asm_token,
1844 .template = template,3121 .data = .{
1845 .outputs = arena_outputs,3122 .lhs = template,
1846 .inputs = arena_inputs,3123 .rhs = try p.addExtra(Node.Asm{
1847 .clobbers = arena_clobbers,3124 .items_start = span.start,
1848 .rparen = try p.expectToken(.RParen),3125 .items_end = span.end,
1849 };3126 .rparen = rparen,
18503127 }),
1851 return &node.base;3128 },
1852 }3129 });
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;
1874 }3130 }
18753131
1876 /// AsmOutputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN (MINUSRARROW TypeExpr / IDENTIFIER) RPAREN3132 /// AsmOutputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN (MINUSRARROW TypeExpr / IDENTIFIER) RPAREN
1877 fn parseAsmOutputItem(p: *Parser) !?Node.Asm.Output {3133 fn parseAsmOutputItem(p: *Parser) !Node.Index {
1878 const lbracket = p.eatToken(.LBracket) orelse return null;3134 _ = p.eatToken(.l_bracket) orelse return null_node;
1879 const name = try p.expectNode(parseIdentifier, .{3135 const identifier = try p.expectToken(.identifier);
1880 .ExpectedIdentifier = .{ .token = p.tok_i },3136 _ = try p.expectToken(.r_bracket);
1881 });3137 _ = try p.expectToken(.string_literal);
1882 _ = try p.expectToken(.RBracket);3138 _ = try p.expectToken(.l_paren);
18833139 const type_expr: Node.Index = blk: {
1884 const constraint = try p.expectNode(parseStringLiteral, .{3140 if (p.eatToken(.arrow)) |_| {
1885 .ExpectedStringLiteral = .{ .token = p.tok_i },3141 break :blk try p.expectTypeExpr();
1886 });3142 } else {
18873143 _ = try p.expectToken(.identifier);
1888 _ = try p.expectToken(.LParen);3144 break :blk null_node;
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 };
1895 }3145 }
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,
1909 };3146 };
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 });
1910 }3156 }
19113157
1912 /// AsmInputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN Expr RPAREN3158 /// AsmInputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN Expr RPAREN
1913 fn parseAsmInputItem(p: *Parser) !?Node.Asm.Input {3159 fn parseAsmInputItem(p: *Parser) !Node.Index {
1914 const lbracket = p.eatToken(.LBracket) orelse return null;3160 _ = p.eatToken(.l_bracket) orelse return null_node;
1915 const name = try p.expectNode(parseIdentifier, .{3161 const identifier = try p.expectToken(.identifier);
1916 .ExpectedIdentifier = .{ .token = p.tok_i },3162 _ = try p.expectToken(.r_bracket);
1917 });3163 _ = try p.expectToken(.string_literal);
1918 _ = try p.expectToken(.RBracket);3164 _ = try p.expectToken(.l_paren);
19193165 const expr = try p.expectExpr();
1920 const constraint = try p.expectNode(parseStringLiteral, .{3166 const rparen = try p.expectToken(.r_paren);
1921 .ExpectedStringLiteral = .{ .token = p.tok_i },3167 return p.addNode(.{
1922 });3168 .tag = .asm_input,
19233169 .main_token = identifier,
1924 _ = try p.expectToken(.LParen);3170 .data = .{
1925 const expr = try p.expectNode(parseExpr, .{3171 .lhs = expr,
1926 .ExpectedExpr = .{ .token = p.tok_i },3172 .rhs = rparen,
3173 },
1927 });3174 });
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 };
1937 }3175 }
19383176
1939 /// BreakLabel <- COLON IDENTIFIER3177 /// BreakLabel <- COLON IDENTIFIER
1940 fn parseBreakLabel(p: *Parser) !?TokenIndex {3178 fn parseBreakLabel(p: *Parser) !TokenIndex {
1941 _ = p.eatToken(.Colon) orelse return null;3179 _ = p.eatToken(.colon) orelse return @as(TokenIndex, 0);
1942 const ident = try p.expectToken(.Identifier);3180 return p.expectToken(.identifier);
1943 return ident;
1944 }3181 }
19453182
1946 /// BlockLabel <- IDENTIFIER COLON3183 /// BlockLabel <- IDENTIFIER COLON
1947 fn parseBlockLabel(p: *Parser, colon_token: *TokenIndex) ?TokenIndex {3184 fn parseBlockLabel(p: *Parser) TokenIndex {
1948 const identifier = p.eatToken(.Identifier) orelse return null;3185 if (p.token_tags[p.tok_i] == .identifier and
1949 if (p.eatToken(.Colon)) |colon| {3186 p.token_tags[p.tok_i + 1] == .colon)
1950 colon_token.* = colon;3187 {
3188 const identifier = p.tok_i;
3189 p.tok_i += 2;
1951 return identifier;3190 return identifier;
1952 }3191 }
1953 p.putBackToken(identifier);3192 return 0;
1954 return null;
1955 }3193 }
19563194
1957 /// FieldInit <- DOT IDENTIFIER EQUAL Expr3195 /// FieldInit <- DOT IDENTIFIER EQUAL Expr
1958 fn parseFieldInit(p: *Parser) !?*Node {3196 fn parseFieldInit(p: *Parser) !Node.Index {
1959 const period_token = p.eatToken(.Period) orelse return null;3197 if (p.token_tags[p.tok_i + 0] == .period and
1960 const name_token = p.eatToken(.Identifier) orelse {3198 p.token_tags[p.tok_i + 1] == .identifier and
1961 // Because of anon literals `.{` is also valid.3199 p.token_tags[p.tok_i + 2] == .equal)
1962 p.putBackToken(period_token);3200 {
1963 return null;3201 p.tok_i += 3;
1964 };3202 return p.expectExpr();
1965 const eq_token = p.eatToken(.Equal) orelse {3203 } else {
1966 // `.Name` may also be an enum literal, which is a later rule.3204 return null_node;
1967 p.putBackToken(name_token);3205 }
1968 p.putBackToken(period_token);3206 }
1969 return null;
1970 };
1971 const expr_node = try p.expectNode(parseExpr, .{
1972 .ExpectedExpr = .{ .token = p.tok_i },
1973 });
19743207
1975 const node = try p.arena.allocator.create(Node.FieldInitializer);3208 fn expectFieldInit(p: *Parser) !Node.Index {
1976 node.* = .{3209 _ = try p.expectToken(.period);
1977 .period_token = period_token,3210 _ = try p.expectToken(.identifier);
1978 .name_token = name_token,3211 _ = try p.expectToken(.equal);
1979 .expr = expr_node,3212 return p.expectExpr();
1980 };
1981 return &node.base;
1982 }3213 }
19833214
1984 /// WhileContinueExpr <- COLON LPAREN AssignExpr RPAREN3215 /// WhileContinueExpr <- COLON LPAREN AssignExpr RPAREN
1985 fn parseWhileContinueExpr(p: *Parser) !?*Node {3216 fn parseWhileContinueExpr(p: *Parser) !Node.Index {
1986 _ = p.eatToken(.Colon) orelse return null;3217 _ = p.eatToken(.colon) orelse return null_node;
1987 _ = try p.expectToken(.LParen);3218 _ = try p.expectToken(.l_paren);
1988 const node = try p.expectNode(parseAssignExpr, .{3219 const node = try p.parseAssignExpr();
1989 .ExpectedExprOrAssignment = .{ .token = p.tok_i },3220 if (node == 0) return p.fail(.expected_expr_or_assignment);
1990 });3221 _ = try p.expectToken(.r_paren);
1991 _ = try p.expectToken(.RParen);
1992 return node;3222 return node;
1993 }3223 }
19943224
1995 /// LinkSection <- KEYWORD_linksection LPAREN Expr RPAREN3225 /// LinkSection <- KEYWORD_linksection LPAREN Expr RPAREN
1996 fn parseLinkSection(p: *Parser) !?*Node {3226 fn parseLinkSection(p: *Parser) !Node.Index {
1997 _ = p.eatToken(.Keyword_linksection) orelse return null;3227 _ = p.eatToken(.keyword_linksection) orelse return null_node;
1998 _ = try p.expectToken(.LParen);3228 _ = try p.expectToken(.l_paren);
1999 const expr_node = try p.expectNode(parseExpr, .{3229 const expr_node = try p.expectExpr();
2000 .ExpectedExpr = .{ .token = p.tok_i },3230 _ = try p.expectToken(.r_paren);
2001 });
2002 _ = try p.expectToken(.RParen);
2003 return expr_node;3231 return expr_node;
2004 }3232 }
20053233
2006 /// CallConv <- KEYWORD_callconv LPAREN Expr RPAREN3234 /// CallConv <- KEYWORD_callconv LPAREN Expr RPAREN
2007 fn parseCallconv(p: *Parser) !?*Node {3235 fn parseCallconv(p: *Parser) !Node.Index {
2008 _ = p.eatToken(.Keyword_callconv) orelse return null;3236 _ = p.eatToken(.keyword_callconv) orelse return null_node;
2009 _ = try p.expectToken(.LParen);3237 _ = try p.expectToken(.l_paren);
2010 const expr_node = try p.expectNode(parseExpr, .{3238 const expr_node = try p.expectExpr();
2011 .ExpectedExpr = .{ .token = p.tok_i },3239 _ = try p.expectToken(.r_paren);
2012 });
2013 _ = try p.expectToken(.RParen);
2014 return expr_node;3240 return expr_node;
2015 }3241 }
20163242
2017 /// ParamDecl <- (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType3243 /// ParamDecl
2018 fn parseParamDecl(p: *Parser) !?Node.FnProto.ParamDecl {3244 /// <- (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType
2019 const doc_comments = try p.parseDocComment();3245 /// / DOT3
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
2052 /// ParamType3246 /// ParamType
2053 /// <- Keyword_anytype3247 /// <- Keyword_anytype
2054 /// / DOT3
2055 /// / TypeExpr3248 /// / TypeExpr
2056 fn parseParamType(p: *Parser) !?Node.FnProto.ParamDecl.ParamType {3249 /// This function can return null nodes and then still return nodes afterwards,
2057 // TODO cast from tuple to error union is broken3250 /// such as in the case of anytype and `...`. Caller must look for rparen to find
2058 const P = Node.FnProto.ParamDecl.ParamType;3251 /// out when there are no more param decls left.
2059 if (try p.parseAnyType()) |node| return P{ .any_type = node };3252 fn expectParamDecl(p: *Parser) !Node.Index {
2060 if (try p.parseTypeExpr()) |node| return P{ .type_expr = node };3253 _ = try p.eatDocComments();
2061 return null;3254 switch (p.token_tags[p.tok_i]) {
2062 }3255 .keyword_noalias, .keyword_comptime => p.tok_i += 1,
20633256 .ellipsis3 => {
2064 /// IfPrefix <- KEYWORD_if LPAREN Expr RPAREN PtrPayload?3257 p.tok_i += 1;
2065 fn parseIfPrefix(p: *Parser) !?*Node {3258 return null_node;
2066 const if_token = p.eatToken(.Keyword_if) orelse return null;3259 },
2067 _ = try p.expectToken(.LParen);3260 else => {},
2068 const condition = try p.expectNode(parseExpr, .{3261 }
2069 .ExpectedExpr = .{ .token = p.tok_i },3262 if (p.token_tags[p.tok_i] == .identifier and
2070 });3263 p.token_tags[p.tok_i + 1] == .colon)
2071 _ = try p.expectToken(.RParen);3264 {
2072 const payload = try p.parsePtrPayload();3265 p.tok_i += 2;
20733266 }
2074 const node = try p.arena.allocator.create(Node.If);3267 switch (p.token_tags[p.tok_i]) {
2075 node.* = .{3268 .keyword_anytype => {
2076 .if_token = if_token,3269 p.tok_i += 1;
2077 .condition = condition,3270 return null_node;
2078 .payload = payload,3271 },
2079 .body = undefined, // set by caller3272 else => return p.expectTypeExpr(),
2080 .@"else" = null,3273 }
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;
2137 }3274 }
21383275
2139 /// Payload <- PIPE IDENTIFIER PIPE3276 /// Payload <- PIPE IDENTIFIER PIPE
2140 fn parsePayload(p: *Parser) !?*Node {3277 fn parsePayload(p: *Parser) !TokenIndex {
2141 const lpipe = p.eatToken(.Pipe) orelse return null;3278 _ = p.eatToken(.pipe) orelse return @as(TokenIndex, 0);
2142 const identifier = try p.expectNode(parseIdentifier, .{3279 const identifier = try p.expectToken(.identifier);
2143 .ExpectedIdentifier = .{ .token = p.tok_i },3280 _ = try p.expectToken(.pipe);
2144 });3281 return identifier;
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;
2154 }3282 }
21553283
2156 /// PtrPayload <- PIPE ASTERISK? IDENTIFIER PIPE3284 /// PtrPayload <- PIPE ASTERISK? IDENTIFIER PIPE
2157 fn parsePtrPayload(p: *Parser) !?*Node {3285 fn parsePtrPayload(p: *Parser) !TokenIndex {
2158 const lpipe = p.eatToken(.Pipe) orelse return null;3286 _ = p.eatToken(.pipe) orelse return @as(TokenIndex, 0);
2159 const asterisk = p.eatToken(.Asterisk);3287 _ = p.eatToken(.asterisk);
2160 const identifier = try p.expectNode(parseIdentifier, .{3288 const identifier = try p.expectToken(.identifier);
2161 .ExpectedIdentifier = .{ .token = p.tok_i },3289 _ = try p.expectToken(.pipe);
2162 });3290 return identifier;
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;
2173 }3291 }
21743292
2175 /// PtrIndexPayload <- PIPE ASTERISK? IDENTIFIER (COMMA IDENTIFIER)? PIPE3293 /// PtrIndexPayload <- PIPE ASTERISK? IDENTIFIER (COMMA IDENTIFIER)? PIPE
2176 fn parsePtrIndexPayload(p: *Parser) !?*Node {3294 /// Returns the first identifier token, if any.
2177 const lpipe = p.eatToken(.Pipe) orelse return null;3295 fn parsePtrIndexPayload(p: *Parser) !TokenIndex {
2178 const asterisk = p.eatToken(.Asterisk);3296 _ = p.eatToken(.pipe) orelse return @as(TokenIndex, 0);
2179 const identifier = try p.expectNode(parseIdentifier, .{3297 _ = p.eatToken(.asterisk);
2180 .ExpectedIdentifier = .{ .token = p.tok_i },3298 const identifier = try p.expectToken(.identifier);
2181 });3299 if (p.eatToken(.comma) != null) {
21823300 _ = try p.expectToken(.identifier);
2183 const index = if (p.eatToken(.Comma) == null)3301 }
2184 null3302 _ = try p.expectToken(.pipe);
2185 else3303 return identifier;
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;
2201 }3304 }
22023305
2203 /// SwitchProng <- SwitchCase EQUALRARROW PtrPayload? AssignExpr3306 /// 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
2220 /// SwitchCase3307 /// SwitchCase
2221 /// <- SwitchItem (COMMA SwitchItem)* COMMA?3308 /// <- SwitchItem (COMMA SwitchItem)* COMMA?
2222 /// / KEYWORD_else3309 /// / KEYWORD_else
2223 fn parseSwitchCase(p: *Parser) !?*Node {3310 fn parseSwitchProng(p: *Parser) !Node.Index {
2224 var list = std.ArrayList(*Node).init(p.gpa);3311 if (p.eatToken(.keyword_else)) |_| {
2225 defer list.deinit();3312 const arrow_token = try p.expectToken(.equal_angle_bracket_right);
22263313 _ = try p.parsePtrPayload();
2227 if (try p.parseSwitchItem()) |first_item| {3314 return p.addNode(.{
2228 try list.append(first_item);3315 .tag = .switch_case_one,
2229 while (p.eatToken(.Comma) != null) {3316 .main_token = arrow_token,
2230 const next_item = (try p.parseSwitchItem()) orelse break;3317 .data = .{
2231 try list.append(next_item);3318 .lhs = 0,
2232 }3319 .rhs = try p.expectAssignExpr(),
2233 } else if (p.eatToken(.Keyword_else)) |else_token| {3320 },
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 },
2258 });3321 });
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;
2268 }3322 }
2269 return expr;3323 const first_item = try p.parseSwitchItem();
2270 }3324 if (first_item == 0) return null_node;
22713325
2272 /// AssignOp3326 if (p.eatToken(.equal_angle_bracket_right)) |arrow_token| {
2273 /// <- ASTERISKEQUAL3327 _ = try p.parsePtrPayload();
2274 /// / SLASHEQUAL3328 return p.addNode(.{
2275 /// / PERCENTEQUAL3329 .tag = .switch_case_one,
2276 /// / PLUSEQUAL3330 .main_token = arrow_token,
2277 /// / MINUSEQUAL3331 .data = .{
2278 /// / LARROW2EQUAL3332 .lhs = first_item,
2279 /// / RARROW2EQUAL3333 .rhs = try p.expectAssignExpr(),
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
2512 },3334 },
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 },
2669 });3335 });
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;
2697 }3336 }
26983337
2699 if (p.eatToken(.PeriodAsterisk)) |period_asterisk| {3338 var list = std.ArrayList(Node.Index).init(p.gpa);
2700 const node = try p.arena.allocator.create(Node.SimpleSuffixOp);3339 defer list.deinit();
2701 node.* = .{
2702 .base = .{ .tag = .Deref },
2703 .lhs = lhs,
2704 .rtoken = period_asterisk,
2705 };
2706 return &node.base;
2707 }
27083340
2709 if (p.eatToken(.Invalid_periodasterisks)) |period_asterisk| {3341 try list.append(first_item);
2710 try p.errors.append(p.gpa, .{3342 while (p.eatToken(.comma)) |_| {
2711 .AsteriskAfterPointerDereference = .{ .token = period_asterisk },3343 const next_item = try p.parseSwitchItem();
2712 });3344 if (next_item == 0) break;
2713 const node = try p.arena.allocator.create(Node.SimpleSuffixOp);3345 try list.append(next_item);
2714 node.* = .{3346 }
2715 .base = .{ .tag = .Deref },3347 const span = try p.listToSpan(list.items);
2716 .lhs = lhs,3348 const arrow_token = try p.expectToken(.equal_angle_bracket_right);
2717 .rtoken = period_asterisk,3349 _ = try p.parsePtrPayload();
2718 };3350 return p.addNode(.{
2719 return &node.base;3351 .tag = .switch_case,
2720 }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| {3363 /// SwitchItem <- Expr (DOT3 Expr)?
2723 if (try p.parseIdentifier()) |identifier| {3364 fn parseSwitchItem(p: *Parser) !Node.Index {
2724 const node = try p.arena.allocator.create(Node.SimpleInfixOp);3365 const expr = try p.parseExpr();
2725 node.* = .{3366 if (expr == 0) return null_node;
2726 .base = Node{ .tag = .Period },3367
2727 .op_token = period,3368 if (p.eatToken(.ellipsis3)) |token| {
2728 .lhs = lhs,3369 return p.addNode(.{
2729 .rhs = identifier,3370 .tag = .switch_range,
2730 };3371 .main_token = token,
2731 return &node.base;3372 .data = .{
2732 }3373 .lhs = expr,
2733 if (p.eatToken(.QuestionMark)) |question_mark| {3374 .rhs = try p.expectExpr(),
2734 const node = try p.arena.allocator.create(Node.SimpleSuffixOp);3375 },
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 },
2744 });3376 });
2745 return null;
2746 }3377 }
27473378 return expr;
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 };
2759 }3379 }
27603380
2761 const AnnotatedParamList = struct {3381 const PtrModifiers = struct {
2762 list: []*Node,3382 align_node: Node.Index,
2763 rparen: TokenIndex,3383 bit_range_start: Node.Index,
3384 bit_range_end: Node.Index,
2764 };3385 };
27653386
2766 /// ArrayTypeStart <- LBRACKET Expr? (COLON Expr)? RBRACKET3387 fn parsePtrModifiers(p: *Parser) !PtrModifiers {
2767 fn parseArrayTypeStart(p: *Parser) !?*Node {3388 var result: PtrModifiers = .{
2768 const lbracket = p.eatToken(.LBracket) orelse return null;3389 .align_node = 0,
2769 const expr = try p.parseExpr();3390 .bit_range_start = 0,
2770 const sentinel = if (p.eatToken(.Colon)) |_|3391 .bit_range_end = 0,
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 },
2804 };3392 };
2805 return &node.base;3393 var saw_const = false;
2806 }3394 var saw_volatile = false;
28073395 var saw_allowzero = false;
2808 /// PtrTypeStart3396 while (true) {
2809 /// <- ASTERISK3397 switch (p.token_tags[p.tok_i]) {
2810 /// / ASTERISK23398 .keyword_align => {
2811 /// / LBRACKET ASTERISK (LETTERC / COLON Expr)? RBRACKET3399 if (result.align_node != 0) {
2812 fn parsePtrTypeStart(p: *Parser) !?*Node {3400 try p.warn(.extra_align_qualifier);
2813 if (p.eatToken(.Asterisk)) |asterisk| {3401 }
2814 const sentinel = if (p.eatToken(.Colon)) |_|3402 p.tok_i += 1;
2815 try p.expectNode(parseExpr, .{3403 _ = try p.expectToken(.l_paren);
2816 .ExpectedExpr = .{ .token = p.tok_i },3404 result.align_node = try p.expectExpr();
2817 })3405
2818 else3406 if (p.eatToken(.colon)) |_| {
2819 null;3407 result.bit_range_start = try p.expectExpr();
2820 const node = try p.arena.allocator.create(Node.PtrType);3408 _ = try p.expectToken(.colon);
2821 node.* = .{3409 result.bit_range_end = try p.expectExpr();
2822 .op_token = asterisk,3410 }
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;
28433411
2844 return &node.base;3412 _ = try p.expectToken(.r_paren);
2845 }3413 },
2846 if (p.eatToken(.LBracket)) |lbracket| {3414 .keyword_const => {
2847 const asterisk = p.eatToken(.Asterisk) orelse {3415 if (saw_const) {
2848 p.putBackToken(lbracket);3416 try p.warn(.extra_const_qualifier);
2849 return null;3417 }
2850 };3418 p.tok_i += 1;
2851 if (p.eatToken(.Identifier)) |ident| {3419 saw_const = true;
2852 const token_loc = p.token_locs[ident];3420 },
2853 const token_slice = p.source[token_loc.start..token_loc.end];3421 .keyword_volatile => {
2854 if (!std.mem.eql(u8, token_slice, "c")) {3422 if (saw_volatile) {
2855 p.putBackToken(ident);3423 try p.warn(.extra_volatile_qualifier);
2856 } else {3424 }
2857 _ = try p.expectToken(.RBracket);3425 p.tok_i += 1;
2858 const node = try p.arena.allocator.create(Node.PtrType);3426 saw_volatile = true;
2859 node.* = .{3427 },
2860 .op_token = lbracket,3428 .keyword_allowzero => {
2861 .rhs = undefined, // set by caller3429 if (saw_allowzero) {
2862 };3430 try p.warn(.extra_allowzero_qualifier);
2863 return &node.base;3431 }
2864 }3432 p.tok_i += 1;
3433 saw_allowzero = true;
3434 },
3435 else => return result,
2865 }3436 }
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;
2880 }3437 }
2881 return null;
2882 }3438 }
28833439
2884 /// ContainerDeclAuto <- ContainerDeclType LBRACE ContainerMembers RBRACE3440 /// SuffixOp
2885 fn parseContainerDeclAuto(p: *Parser) !?*Node {3441 /// <- LBRACKET Expr (DOT2 (Expr (COLON Expr)?)?)? RBRACKET
2886 const container_decl_type = (try p.parseContainerDeclType()) orelse return null;3442 /// / DOT IDENTIFIER
2887 const lbrace = try p.expectToken(.LBrace);3443 /// / DOTASTERISK
2888 const members = try p.parseContainerMembers(false);3444 /// / DOTQUESTIONMARK
2889 defer p.gpa.free(members);3445 fn parseSuffixOp(p: *Parser, lhs: Node.Index) !Node.Index {
2890 const rbrace = try p.expectToken(.RBrace);3446 switch (p.token_tags[p.tok_i]) {
28913447 .l_bracket => {
2892 const members_len = @intCast(NodeIndex, members.len);3448 const lbracket = p.nextToken();
2893 const node = try Node.ContainerDecl.alloc(&p.arena.allocator, members_len);3449 const index_expr = try p.expectExpr();
2894 node.* = .{3450
2895 .layout_token = null,3451 if (p.eatToken(.ellipsis2)) |_| {
2896 .kind_token = container_decl_type.kind_token,3452 const end_expr = try p.parseExpr();
2897 .init_arg_expr = container_decl_type.init_arg_expr,3453 if (end_expr == 0) {
2898 .fields_and_decls_len = members_len,3454 _ = try p.expectToken(.r_bracket);
2899 .lbrace_token = lbrace,3455 return p.addNode(.{
2900 .rbrace_token = rbrace,3456 .tag = .slice_open,
2901 };3457 .main_token = lbracket,
2902 std.mem.copy(*Node, node.fieldsAndDecls(), members);3458 .data = .{
2903 return &node.base;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 }
2904 }3548 }
29053549
2906 /// Holds temporary data until we are ready to construct the full ContainerDecl AST node.3550 /// Caller must have already verified the first token.
2907 const ContainerDeclType = struct {
2908 kind_token: TokenIndex,
2909 init_arg_expr: Node.ContainerDecl.InitArg,
2910 };
2911
2912 /// ContainerDeclType3551 /// ContainerDeclType
2913 /// <- KEYWORD_struct3552 /// <- KEYWORD_struct
2914 /// / KEYWORD_enum (LPAREN Expr RPAREN)?3553 /// / KEYWORD_enum (LPAREN Expr RPAREN)?
2915 /// / KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? / Expr) RPAREN)?3554 /// / KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? / Expr) RPAREN)?
2916 /// / KEYWORD_opaque3555 /// / KEYWORD_opaque
2917 fn parseContainerDeclType(p: *Parser) !?ContainerDeclType {3556 fn parseContainerDeclAuto(p: *Parser) !Node.Index {
2918 const kind_token = p.nextToken();3557 const main_token = p.nextToken();
29193558 const arg_expr = switch (p.token_tags[main_token]) {
2920 const init_arg_expr = switch (p.token_ids[kind_token]) {3559 .keyword_struct, .keyword_opaque => null_node,
2921 .Keyword_struct, .Keyword_opaque => Node.ContainerDecl.InitArg{ .None = {} },3560 .keyword_enum => blk: {
2922 .Keyword_enum => blk: {3561 if (p.eatToken(.l_paren)) |_| {
2923 if (p.eatToken(.LParen) != null) {3562 const expr = try p.expectExpr();
2924 const expr = try p.expectNode(parseExpr, .{3563 _ = try p.expectToken(.r_paren);
2925 .ExpectedExpr = .{ .token = p.tok_i },3564 break :blk expr;
2926 });3565 } else {
2927 _ = try p.expectToken(.RParen);3566 break :blk null_node;
2928 break :blk Node.ContainerDecl.InitArg{ .Type = expr };
2929 }3567 }
2930 break :blk Node.ContainerDecl.InitArg{ .None = {} };3568 },
2931 },3569 .keyword_union => blk: {
2932 .Keyword_union => blk: {3570 if (p.eatToken(.l_paren)) |_| {
2933 if (p.eatToken(.LParen) != null) {3571 if (p.eatToken(.keyword_enum)) |_| {
2934 if (p.eatToken(.Keyword_enum) != null) {3572 if (p.eatToken(.l_paren)) |_| {
2935 if (p.eatToken(.LParen) != null) {3573 const enum_tag_expr = try p.expectExpr();
2936 const expr = try p.expectNode(parseExpr, .{3574 _ = try p.expectToken(.r_paren);
2937 .ExpectedExpr = .{ .token = p.tok_i },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 },
2938 });3591 });
2939 _ = try p.expectToken(.RParen);3592 } else {
2940 _ = try p.expectToken(.RParen);3593 _ = try p.expectToken(.r_paren);
2941 break :blk Node.ContainerDecl.InitArg{ .Enum = expr };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 }
2942 }3624 }
2943 _ = try p.expectToken(.RParen);3625 } else {
2944 break :blk Node.ContainerDecl.InitArg{ .Enum = null };3626 const expr = try p.expectExpr();
3627 _ = try p.expectToken(.r_paren);
3628 break :blk expr;
2945 }3629 }
2946 const expr = try p.expectNode(parseExpr, .{3630 } else {
2947 .ExpectedExpr = .{ .token = p.tok_i },3631 break :blk null_node;
2948 });
2949 _ = try p.expectToken(.RParen);
2950 break :blk Node.ContainerDecl.InitArg{ .Type = expr };
2951 }3632 }
2952 break :blk Node.ContainerDecl.InitArg{ .None = {} };
2953 },3633 },
2954 else => {3634 else => {
2955 p.putBackToken(kind_token);3635 p.tok_i -= 1;
2956 return null;3636 return p.fail(.expected_container);
2957 },3637 },
2958 };3638 };
29593639 _ = try p.expectToken(.l_brace);
2960 return ContainerDeclType{3640 const members = try p.parseContainerMembers();
2961 .kind_token = kind_token,3641 _ = try p.expectToken(.r_brace);
2962 .init_arg_expr = init_arg_expr,3642 if (arg_expr == 0) {
2963 };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 }
2964 }3686 }
29653687
3688 /// Holds temporary data until we are ready to construct the full ContainerDecl AST node.
2966 /// ByteAlign <- KEYWORD_align LPAREN Expr RPAREN3689 /// ByteAlign <- KEYWORD_align LPAREN Expr RPAREN
2967 fn parseByteAlign(p: *Parser) !?*Node {3690 fn parseByteAlign(p: *Parser) !Node.Index {
2968 _ = p.eatToken(.Keyword_align) orelse return null;3691 _ = p.eatToken(.keyword_align) orelse return null_node;
2969 _ = try p.expectToken(.LParen);3692 _ = try p.expectToken(.l_paren);
2970 const expr = try p.expectNode(parseExpr, .{3693 const expr = try p.expectExpr();
2971 .ExpectedExpr = .{ .token = p.tok_i },3694 _ = try p.expectToken(.r_paren);
2972 });
2973 _ = try p.expectToken(.RParen);
2974 return expr;3695 return expr;
2975 }3696 }
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
2983 /// SwitchProngList <- (SwitchProng COMMA)* SwitchProng?3698 /// SwitchProngList <- (SwitchProng COMMA)* SwitchProng?
2984 fn parseSwitchProngList(p: *Parser) ![]*Node {3699 fn parseSwitchProngList(p: *Parser) !Node.SubRange {
2985 return ListParseFn(*Node, parseSwitchProng)(p);3700 return ListParseFn(parseSwitchProng)(p);
2986 }3701 }
29873702
2988 /// AsmOutputList <- (AsmOutputItem COMMA)* AsmOutputItem?3703 /// ParamDeclList <- (ParamDecl COMMA)* ParamDecl?
2989 fn parseAsmOutputList(p: *Parser) Error![]Node.Asm.Output {3704 fn parseParamDeclList(p: *Parser) !SmallSpan {
2990 return ListParseFn(Node.Asm.Output, parseAsmOutputItem)(p);3705 _ = try p.expectToken(.l_paren);
2991 }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?3729 const param_two = while (true) {
2994 fn parseAsmInputList(p: *Parser) Error![]Node.Asm.Input {3730 switch (p.token_tags[p.nextToken()]) {
2995 return ListParseFn(Node.Asm.Input, parseAsmInputItem)(p);3731 .comma => {
2996 }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?3753 var list = std.ArrayList(Node.Index).init(p.gpa);
2999 fn parseParamDeclList(p: *Parser) ![]Node.FnProto.ParamDecl {3754 defer list.deinit();
3000 return ListParseFn(Node.FnProto.ParamDecl, parseParamDecl)(p);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 }
3001 }3784 }
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) {
3006 return struct {3789 return struct {
3007 pub fn parse(p: *Parser) ![]E {3790 pub fn parse(p: *Parser) Error!Node.SubRange {
3008 var list = std.ArrayList(E).init(p.gpa);3791 var list = std.ArrayList(Node.Index).init(p.gpa);
3009 defer list.deinit();3792 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
3012 try list.append(item);3798 try list.append(item);
30133799
3014 switch (p.token_ids[p.tok_i]) {3800 switch (p.token_tags[p.tok_i]) {
3015 .Comma => _ = p.nextToken(),3801 .comma => p.tok_i += 1,
3016 // all possible delimiters3802 // all possible delimiters
3017 .Colon, .RParen, .RBrace, .RBracket => break,3803 .colon, .r_paren, .r_brace, .r_bracket => break,
3018 else => {3804 else => {
3019 // this is likely just a missing comma,3805 // This is likely just a missing comma;
3020 // continue parsing this list and give an error3806 // give an error but continue parsing this list.
3021 try p.errors.append(p.gpa, .{3807 try p.warnExpected(.comma);
3022 .ExpectedToken = .{ .token = p.tok_i, .expected_id = .Comma },
3023 });
3024 },3808 },
3025 }3809 }
3026 }3810 }
3027 return list.toOwnedSlice();3811 return p.listToSpan(list.items);
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;
3054 }3812 }
3055 }.parse;3813 }.parse;
3056 }3814 }
30573815
3058 // Helper parsers not included in the grammar3816 /// FnCallArguments <- LPAREN ExprList RPAREN
30593817 /// ExprList <- (Expr COMMA)* Expr?
3060 fn parseBuiltinCall(p: *Parser) !?*Node {3818 fn parseBuiltinCall(p: *Parser) !Node.Index {
3061 const token = p.eatToken(.Builtin) orelse return null;3819 const builtin_token = p.assertToken(.builtin);
3062 const params = (try p.parseFnCallArguments()) orelse {3820 if (p.token_tags[p.nextToken()] != .l_paren) {
3063 try p.errors.append(p.gpa, .{3821 p.tok_i -= 1;
3064 .ExpectedParamList = .{ .token = p.tok_i },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 },
3065 });3831 });
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 parsing3902 var list = std.ArrayList(Node.Index).init(p.gpa);
3068 const node = try p.arena.allocator.create(Node.OneToken);3903 defer list.deinit();
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 }
31193904
3120 fn createLiteral(p: *Parser, tag: ast.Node.Tag, token: TokenIndex) !*Node {3905 try list.appendSlice(&.{ param_one, param_two });
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 }
31283906
3129 fn parseStringLiteralSingle(p: *Parser) !?*Node {3907 while (true) {
3130 if (p.eatToken(.StringLiteral)) |token| {3908 const param = try p.expectExpr();
3131 const node = try p.arena.allocator.create(Node.OneToken);3909 try list.append(param);
3132 node.* = .{3910 switch (p.token_tags[p.nextToken()]) {
3133 .base = .{ .tag = .StringLiteral },3911 .comma => {
3134 .token = token,3912 if (p.eatToken(.r_paren)) |_| {
3135 };3913 const params = try p.listToSpan(list.items);
3136 return &node.base;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 }
3137 }3943 }
3138 return null;
3139 }3944 }
31403945
3141 // string literal or multiline string literal3946 // string literal or multiline string literal
3142 fn parseStringLiteral(p: *Parser) !?*Node {3947 fn parseStringLiteral(p: *Parser) !Node.Index {
3143 if (try p.parseStringLiteralSingle()) |node| return node;3948 switch (p.token_tags[p.tok_i]) {
31443949 .string_literal => {
3145 if (p.eatToken(.MultilineStringLiteralLine)) |first_line| {3950 const main_token = p.nextToken();
3146 const start_tok_i = p.tok_i;3951 return p.addNode(.{
3147 var tok_i = start_tok_i;3952 .tag = .string_literal,
3148 var count: usize = 1; // including first_line3953 .main_token = main_token,
3149 while (true) : (tok_i += 1) {3954 .data = .{
3150 switch (p.token_ids[tok_i]) {3955 .lhs = undefined,
3151 .LineComment => continue,3956 .rhs = undefined,
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;
3169 },3957 },
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;
3171 }3964 }
3172 }3965 return p.addNode(.{
3173 p.tok_i = tok_i;3966 .tag = .multiline_string_literal,
3174 return &node.base;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,
3175 }3975 }
3176
3177 return null;
3178 }3976 }
31793977
3180 fn parseIntegerLiteral(p: *Parser) !?*Node {3978 fn expectStringLiteral(p: *Parser) !Node.Index {
3181 const token = p.eatToken(.IntegerLiteral) orelse return null;3979 const node = try p.parseStringLiteral();
3182 const node = try p.arena.allocator.create(Node.OneToken);3980 if (node == 0) {
3183 node.* = .{3981 return p.fail(.expected_string_literal);
3184 .base = .{ .tag = .IntegerLiteral },3982 }
3185 .token = token,3983 return node;
3186 };
3187 return &node.base;
3188 }3984 }
31893985
3190 fn parseFloatLiteral(p: *Parser) !?*Node {3986 fn expectIntegerLiteral(p: *Parser) !Node.Index {
3191 const token = p.eatToken(.FloatLiteral) orelse return null;3987 return p.addNode(.{
3192 const node = try p.arena.allocator.create(Node.OneToken);3988 .tag = .integer_literal,
3193 node.* = .{3989 .main_token = try p.expectToken(.integer_literal),
3194 .base = .{ .tag = .FloatLiteral },3990 .data = .{
3195 .token = token,3991 .lhs = undefined,
3196 };3992 .rhs = undefined,
3197 return &node.base;3993 },
3994 });
3198 }3995 }
31993996
3200 fn parseTry(p: *Parser) !?*Node {3997 /// KEYWORD_if LPAREN Expr RPAREN PtrPayload? Body (KEYWORD_else Payload? Body)?
3201 const token = p.eatToken(.Keyword_try) orelse return null;3998 fn parseIf(p: *Parser, bodyParseFn: NodeParseFn) !Node.Index {
3202 const node = try p.arena.allocator.create(Node.SimplePrefixOp);3999 const if_token = p.eatToken(.keyword_if) orelse return null_node;
3203 node.* = .{4000 _ = try p.expectToken(.l_paren);
3204 .base = .{ .tag = .Try },4001 const condition = try p.expectExpr();
3205 .op_token = token,4002 _ = try p.expectToken(.r_paren);
3206 .rhs = undefined, // set by caller4003 const then_payload = try p.parsePtrPayload();
3207 };
3208 return &node.base;
3209 }
32104004
3211 /// IfPrefix Body (KEYWORD_else Payload? Body)?4005 const then_expr = try bodyParseFn(p);
3212 fn parseIf(p: *Parser, bodyParseFn: NodeParseFn) !?*Node {4006 if (then_expr == 0) return p.fail(.invalid_token);
3213 const node = (try p.parseIfPrefix()) orelse return null;
3214 const if_prefix = node.cast(Node.If).?;
32154007
3216 if_prefix.body = try p.expectNode(bodyParseFn, .{4008 const else_token = p.eatToken(.keyword_else) orelse return p.addNode(.{
3217 .InvalidToken = .{ .token = p.tok_i },4009 .tag = .if_simple,
4010 .main_token = if_token,
4011 .data = .{
4012 .lhs = condition,
4013 .rhs = then_expr,
4014 },
3218 });4015 });
32194016 const else_payload = try p.parsePayload();
3220 const else_token = p.eatToken(.Keyword_else) orelse return node;4017 const else_expr = try bodyParseFn(p);
3221 const payload = try p.parsePayload();4018 if (else_expr == 0) return p.fail(.invalid_token);
3222 const else_expr = try p.expectNode(bodyParseFn, .{4019
3223 .InvalidToken = .{ .token = p.tok_i },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 },
3224 });4030 });
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;
3234 }4031 }
32354032
3236 /// Eat a multiline doc comment4033 /// Skips over doc comment tokens. Returns the first one, if any.
3237 fn parseDocComment(p: *Parser) !?*Node.DocComment {4034 fn eatDocComments(p: *Parser) !?TokenIndex {
3238 if (p.eatToken(.DocComment)) |first_line| {4035 if (p.eatToken(.doc_comment)) |tok| {
3239 while (p.eatToken(.DocComment)) |_| {}4036 var first_line = tok;
3240 const node = try p.arena.allocator.create(Node.DocComment);4037 if (tok > 0 and tokensOnSameLine(p, tok - 1, tok)) {
3241 node.* = .{ .first_line = first_line };4038 try p.warnMsg(.{
3242 return node;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;
3243 }4046 }
3244 return null;4047 return null;
3245 }4048 }
32464049
3247 fn tokensOnSameLine(p: *Parser, token1: TokenIndex, token2: TokenIndex) bool {4050 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;
3249 }4052 }
32504053
3251 /// Eat a single-line doc comment on the same line as another node4054 fn eatToken(p: *Parser, tag: Token.Tag) ?TokenIndex {
3252 fn parseAppendedDocComment(p: *Parser, after_token: TokenIndex) !?*Node.DocComment {4055 return if (p.token_tags[p.tok_i] == tag) p.nextToken() else null;
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;
3261 }4056 }
32624057
3263 /// Op* Child4058 fn assertToken(p: *Parser, tag: Token.Tag) TokenIndex {
3264 fn parsePrefixOpExpr(p: *Parser, comptime opParseFn: NodeParseFn, comptime childParseFn: NodeParseFn) Error!?*Node {4059 const token = p.nextToken();
3265 if (try opParseFn(p)) |first_op| {4060 assert(p.token_tags[token] == tag);
3266 var rightmost_op = first_op;4061 return token;
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);
3380 }4062 }
33814063
3382 /// Child (Op Child)*4064 fn expectToken(p: *Parser, tag: Token.Tag) Error!TokenIndex {
3383 /// Child (Op Child)?4065 const token = p.nextToken();
3384 fn parseBinOpExpr(4066 if (p.token_tags[token] != tag) {
3385 p: *Parser,4067 p.tok_i -= 1; // Go back so that we can recover properly.
3386 opParseFn: NodeParseFn,4068 return p.failMsg(.{
3387 childParseFn: NodeParseFn,4069 .tag = .expected_token,
3388 chain: enum {4070 .token = token,
3389 Once,4071 .extra = .{ .expected_tag = tag },
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 },
3398 });4072 });
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 }
3414 }4073 }
34154074 return token;
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;
3436 }4075 }
34374076
3438 fn expectTokenRecoverable(p: *Parser, id: Token.Id) !?TokenIndex {4077 fn expectTokenRecoverable(p: *Parser, tag: Token.Tag) !?TokenIndex {
3439 const token = p.nextToken();4078 if (p.token_tags[p.tok_i] != tag) {
3440 if (p.token_ids[token] != id) {4079 try p.warnExpected(tag);
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);
3446 return null;4080 return null;
4081 } else {
4082 return p.nextToken();
3447 }4083 }
3448 return token;
3449 }4084 }
34504085
3451 fn nextToken(p: *Parser) TokenIndex {4086 fn nextToken(p: *Parser) TokenIndex {
3452 const result = p.tok_i;4087 const result = p.tok_i;
3453 p.tok_i += 1;4088 p.tok_i += 1;
3454 assert(p.token_ids[result] != .LineComment);4089 return result;
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 };
3493 }4090 }
3494};4091};
34954092
3496fn ParseFn(comptime T: type) type {4093test {
3497 return fn (p: *Parser) Error!T;
3498}
3499
3500test "std.zig.parser" {
3501 _ = @import("parser_test.zig");4094 _ = @import("parser_test.zig");
3502}4095}
lib/std/zig/parser_test.zig+1753-949
...@@ -3,275 +3,34 @@...@@ -3,275 +3,34 @@
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.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 copies4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.5// and substantial portions of the software.
6test "zig fmt: convert var to anytype" {6
7 // TODO remove in next release cycle7// TODO Remove this after zig 0.9.0 is released.
8test "zig fmt: rewrite inline functions as callconv(.Inline)" {
8 try testTransform(9 try testTransform(
9 \\pub fn main(10 \\inline fn foo() void {}
10 \\ a: var,11 \\
11 \\ bar: var,
12 \\) void {}
13 ,12 ,
14 \\pub fn main(13 \\fn foo() callconv(.Inline) void {}
15 \\ a: anytype,
16 \\ bar: anytype,
17 \\) void {}
18 \\14 \\
19 );15 );
20}16}
2117
22test "zig fmt: noasync to nosuspend" {18test "zig fmt: simple top level comptime block" {
23 // TODO: remove this19 try testCanonical(
24 try testTransform(20 \\// line comment
25 \\pub fn main() void {21 \\comptime {}
26 \\ noasync call();
27 \\}
28 ,
29 \\pub fn main() void {
30 \\ nosuspend call();
31 \\}
32 \\22 \\
33 );23 );
34}24}
3525
36test "recovery: top level" {26test "zig fmt: two spaced line comments before decl" {
37 try testError(27 try testCanonical(
38 \\test "" {inline}28 \\// line comment
39 \\test "" {inline}29 \\
40 , &[_]Error{30 \\// another
41 .ExpectedInlinable,31 \\comptime {}
42 .ExpectedInlinable,32 \\
43 });33 );
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 });
275}34}
27635
277test "zig fmt: respect line breaks after var declarations" {36test "zig fmt: respect line breaks after var declarations" {
...@@ -325,6 +84,35 @@ test "zig fmt: empty file" {...@@ -325,6 +84,35 @@ test "zig fmt: empty file" {
325 );84 );
326}85}
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
328test "zig fmt: if statment" {116test "zig fmt: if statment" {
329 try testCanonical(117 try testCanonical(
330 \\test "" {118 \\test "" {
...@@ -357,7 +145,7 @@ test "zig fmt: decl between fields" {...@@ -357,7 +145,7 @@ test "zig fmt: decl between fields" {
357 \\ b: usize,145 \\ b: usize,
358 \\};146 \\};
359 , &[_]Error{147 , &[_]Error{
360 .DeclBetweenFields,148 .decl_between_fields,
361 });149 });
362}150}
363151
...@@ -365,7 +153,7 @@ test "zig fmt: eof after missing comma" {...@@ -365,7 +153,7 @@ test "zig fmt: eof after missing comma" {
365 try testError(153 try testError(
366 \\foo()154 \\foo()
367 , &[_]Error{155 , &[_]Error{
368 .ExpectedToken,156 .expected_token,
369 });157 });
370}158}
371159
...@@ -402,7 +190,7 @@ test "zig fmt: nosuspend await" {...@@ -402,7 +190,7 @@ test "zig fmt: nosuspend await" {
402 );190 );
403}191}
404192
405test "zig fmt: trailing comma in container declaration" {193test "zig fmt: container declaration, single line" {
406 try testCanonical(194 try testCanonical(
407 \\const X = struct { foo: i32 };195 \\const X = struct { foo: i32 };
408 \\const X = struct { foo: i32, bar: i32 };196 \\const X = struct { foo: i32, bar: i32 };
...@@ -411,7 +199,23 @@ test "zig fmt: trailing comma in container declaration" {...@@ -411,7 +199,23 @@ test "zig fmt: trailing comma in container declaration" {
411 \\const X = struct { foo: i32 align(4) = 1, bar: i32 align(4) = 2 };199 \\const X = struct { foo: i32 align(4) = 1, bar: i32 align(4) = 2 };
412 \\200 \\
413 );201 );
202}
203
204test "zig fmt: container declaration, one item, multi line trailing comma" {
414 try testCanonical(205 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(
415 \\test "" {219 \\test "" {
416 \\ comptime {220 \\ comptime {
417 \\ const X = struct {221 \\ const X = struct {
...@@ -420,30 +224,113 @@ test "zig fmt: trailing comma in container declaration" {...@@ -420,30 +224,113 @@ test "zig fmt: trailing comma in container declaration" {
420 \\ }224 \\ }
421 \\}225 \\}
422 \\226 \\
227 ,
228 \\test "" {
229 \\ comptime {
230 \\ const X = struct { x: i32 };
231 \\ }
232 \\}
233 \\
423 );234 );
235}
236
237test "zig fmt: container declaration, line break, no trailing comma" {
424 try testTransform(238 try testTransform(
425 \\const X = struct {239 \\const X = struct {
426 \\ foo: i32, bar: i8 };240 \\ 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, };
427 ,251 ,
428 \\const X = struct {252 \\const X = struct {
429 \\ foo: i32, bar: i8253 \\ foo: i32,
254 \\ bar: i8,
430 \\};255 \\};
431 \\256 \\
432 );257 );
433}258}
434259
435test "zig fmt: trailing comma in fn parameter list" {260test "zig fmt: remove empty lines at start/end of container decl" {
436 try testCanonical(261 try testTransform(
437 \\pub fn f(262 \\const X = struct {
438 \\ a: i32,263 \\
439 \\ b: i32,264 \\ foo: i32,
440 \\) i32 {}265 \\
441 \\pub fn f(266 \\ bar: i8,
442 \\ a: i32,267 \\
443 \\ b: i32,268 \\};
444 \\) align(8) i32 {}269 \\
445 \\pub fn f(270 ,
446 \\ a: i32,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,
447 \\ b: i32,334 \\ b: i32,
448 \\) linksection(".text") i32 {}335 \\) linksection(".text") i32 {}
449 \\pub fn f(336 \\pub fn f(
...@@ -480,6 +367,31 @@ test "zig fmt: comptime struct field" {...@@ -480,6 +367,31 @@ test "zig fmt: comptime struct field" {
480 );367 );
481}368}
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
483test "zig fmt: c pointer type" {395test "zig fmt: c pointer type" {
484 try testCanonical(396 try testCanonical(
485 \\pub extern fn repro() [*c]const u8;397 \\pub extern fn repro() [*c]const u8;
...@@ -535,6 +447,19 @@ test "zig fmt: anytype struct field" {...@@ -535,6 +447,19 @@ test "zig fmt: anytype struct field" {
535 );447 );
536}448}
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
538test "zig fmt: sentinel-terminated array type" {463test "zig fmt: sentinel-terminated array type" {
539 try testCanonical(464 try testCanonical(
540 \\pub fn cStrToPrefixedFileW(s: [*:0]const u8) ![PATH_MAX_WIDE:0]u16 {465 \\pub fn cStrToPrefixedFileW(s: [*:0]const u8) ![PATH_MAX_WIDE:0]u16 {
...@@ -553,6 +478,58 @@ test "zig fmt: sentinel-terminated slice type" {...@@ -553,6 +478,58 @@ test "zig fmt: sentinel-terminated slice type" {
553 );478 );
554}479}
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
556test "zig fmt: anon literal in array" {533test "zig fmt: anon literal in array" {
557 try testCanonical(534 try testCanonical(
558 \\var arr: [2]Foo = .{535 \\var arr: [2]Foo = .{
...@@ -581,100 +558,91 @@ test "zig fmt: alignment in anonymous literal" {...@@ -581,100 +558,91 @@ test "zig fmt: alignment in anonymous literal" {
581 );558 );
582}559}
583560
584test "zig fmt: anon struct literal syntax" {561test "zig fmt: anon struct literal 0 element" {
585 try testCanonical(562 try testCanonical(
586 \\const x = .{563 \\test {
587 \\ .a = b,564 \\ const x = .{};
588 \\ .c = d,565 \\}
589 \\};
590 \\566 \\
591 );567 );
592}568}
593569
594test "zig fmt: anon list literal syntax" {570test "zig fmt: anon struct literal 1 element" {
595 try testCanonical(571 try testCanonical(
596 \\const x = .{ a, b, c };572 \\test {
573 \\ const x = .{ .a = b };
574 \\}
597 \\575 \\
598 );576 );
599}577}
600578
601test "zig fmt: async function" {579test "zig fmt: anon struct literal 1 element comma" {
602 try testCanonical(580 try testCanonical(
603 \\pub const Server = struct {581 \\test {
604 \\ handleRequestFn: fn (*Server, *const std.net.Address, File) callconv(.Async) void,582 \\ const x = .{
605 \\};583 \\ .a = b,
606 \\test "hi" {584 \\ };
607 \\ var ptr = @ptrCast(fn (i32) callconv(.Async) void, other);
608 \\}585 \\}
609 \\586 \\
610 );587 );
611}588}
612589
613test "zig fmt: whitespace fixes" {590test "zig fmt: anon struct literal 2 element" {
614 try testTransform("test \"\" {\r\n\tconst hi = x;\r\n}\n// zig fmt: off\ntest \"\"{\r\n\tconst a = b;}\r\n",591 try testCanonical(
615 \\test "" {592 \\test {
616 \\ const hi = x;593 \\ const x = .{ .a = b, .c = d };
617 \\}594 \\}
618 \\// zig fmt: off
619 \\test ""{
620 \\ const a = b;}
621 \\595 \\
622 );596 );
623}597}
624598
625test "zig fmt: while else err prong with no block" {599test "zig fmt: anon struct literal 2 element comma" {
626 try testCanonical(600 try testCanonical(
627 \\test "" {601 \\test {
628 \\ const result = while (returnError()) |value| {602 \\ const x = .{
629 \\ break value;603 \\ .a = b,
630 \\ } else |err| @as(i32, 2);604 \\ .c = d,
631 \\ expect(result == 2);605 \\ };
632 \\}606 \\}
633 \\607 \\
634 );608 );
635}609}
636610
637test "zig fmt: tagged union with enum values" {611test "zig fmt: anon struct literal 3 element" {
638 try testCanonical(612 try testCanonical(
639 \\const MultipleChoice2 = union(enum(u32)) {613 \\test {
640 \\ Unspecified1: i32,614 \\ const x = .{ .a = b, .c = d, .e = f };
641 \\ A: f32 = 20,615 \\}
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 \\};
650 \\616 \\
651 );617 );
652}618}
653619
654test "zig fmt: allowzero pointer" {620test "zig fmt: anon struct literal 3 element comma" {
655 try testCanonical(621 try testCanonical(
656 \\const T = [*]allowzero const u8;622 \\test {
623 \\ const x = .{
624 \\ .a = b,
625 \\ .c = d,
626 \\ .e = f,
627 \\ };
628 \\}
657 \\629 \\
658 );630 );
659}631}
660632
661test "zig fmt: enum literal" {633test "zig fmt: struct literal 0 element" {
662 try testCanonical(634 try testCanonical(
663 \\const x = .hi;635 \\test {
636 \\ const x = X{};
637 \\}
664 \\638 \\
665 );639 );
666}640}
667641
668test "zig fmt: enum literal inside array literal" {642test "zig fmt: struct literal 1 element" {
669 try testCanonical(643 try testCanonical(
670 \\test "enums in arrays" {644 \\test {
671 \\ var colors = []Color{.Green};645 \\ const x = X{ .a = b };
672 \\ colors = []Colors{ .Green, .Cyan };
673 \\ colors = []Colors{
674 \\ .Grey,
675 \\ .Green,
676 \\ .Cyan,
677 \\ };
678 \\}646 \\}
679 \\647 \\
680 );648 );
...@@ -682,656 +650,1016 @@ test "zig fmt: enum literal inside array literal" {...@@ -682,656 +650,1016 @@ test "zig fmt: enum literal inside array literal" {
682650
683test "zig fmt: Unicode code point literal larger than u8" {651test "zig fmt: Unicode code point literal larger than u8" {
684 try testCanonical(652 try testCanonical(
685 \\const x = '\u{01f4a9}';653 \\test {
654 \\ const x = X{
655 \\ .a = b,
656 \\ };
657 \\}
686 \\658 \\
687 );659 );
688}660}
689661
690test "zig fmt: infix operator and then multiline string literal" {662test "zig fmt: struct literal 2 element" {
691 try testCanonical(663 try testCanonical(
692 \\const x = "" ++664 \\test {
693 \\ \\ hi665 \\ const x = X{ .a = b, .c = d };
694 \\;666 \\}
695 \\667 \\
696 );668 );
697}669}
698670
699test "zig fmt: infix operator and then multiline string literal" {671test "zig fmt: struct literal 2 element comma" {
700 try testCanonical(672 try testCanonical(
701 \\const x = "" ++673 \\test {
702 \\ \\ hi0674 \\ const x = X{
703 \\ \\ hi1675 \\ .a = b,
704 \\ \\ hi2676 \\ .c = d,
705 \\;677 \\ };
678 \\}
706 \\679 \\
707 );680 );
708}681}
709682
710test "zig fmt: C pointers" {683test "zig fmt: struct literal 3 element" {
711 try testCanonical(684 try testCanonical(
712 \\const Ptr = [*c]i32;685 \\test {
686 \\ const x = X{ .a = b, .c = d, .e = f };
687 \\}
713 \\688 \\
714 );689 );
715}690}
716691
717test "zig fmt: threadlocal" {692test "zig fmt: struct literal 3 element comma" {
718 try testCanonical(693 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 \\}
720 \\701 \\
721 );702 );
722}703}
723704
724test "zig fmt: linksection" {705test "zig fmt: anon list literal 1 element" {
725 try testCanonical(706 try testCanonical(
726 \\export var aoeu: u64 linksection(".text.derp") = 1234;707 \\test {
727 \\export fn _start() linksection(".text.boot") callconv(.Naked) noreturn {}708 \\ const x = .{a};
709 \\}
728 \\710 \\
729 );711 );
730}712}
731713
732test "zig fmt: correctly move doc comments on struct fields" {714test "zig fmt: anon list literal 1 element comma" {
733 try testTransform(715 try testCanonical(
734 \\pub const section_64 = extern struct {716 \\test {
735 \\ sectname: [16]u8, /// name of this section717 \\ const x = .{
736 \\ segname: [16]u8, /// segment this section goes in718 \\ a,
737 \\};719 \\ };
738 ,720 \\}
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 \\};
745 \\721 \\
746 );722 );
747}723}
748724
749test "zig fmt: correctly space struct fields with doc comments" {725test "zig fmt: anon list literal 2 element" {
750 try testTransform(726 try testCanonical(
751 \\pub const S = struct {727 \\test {
752 \\ /// A728 \\ const x = .{ a, b };
753 \\ a: u8,729 \\}
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 \\};
774 \\730 \\
775 );731 );
776}732}
777733
778test "zig fmt: doc comments on param decl" {734test "zig fmt: anon list literal 2 element comma" {
779 try testCanonical(735 try testCanonical(
780 \\pub const Allocator = struct {736 \\test {
781 \\ shrinkFn: fn (737 \\ const x = .{
782 \\ self: *Allocator,738 \\ a,
783 \\ /// Guaranteed to be the same as what was returned from most recent call to739 \\ b,
784 \\ /// `allocFn`, `reallocFn`, or `shrinkFn`.740 \\ };
785 \\ old_mem: []u8,741 \\}
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 \\};
795 \\742 \\
796 );743 );
797}744}
798745
799test "zig fmt: aligned struct field" {746test "zig fmt: anon list literal 3 element" {
800 try testCanonical(747 try testCanonical(
801 \\pub const S = struct {748 \\test {
802 \\ f: i32 align(32),749 \\ const x = .{ a, b, c };
803 \\};750 \\}
804 \\
805 );
806 try testCanonical(
807 \\pub const S = struct {
808 \\ f: i32 align(32) = 1,
809 \\};
810 \\751 \\
811 );752 );
812}753}
813754
814test "zig fmt: comment to disable/enable zig fmt first" {755test "zig fmt: anon list literal 3 element comma" {
815 try testCanonical(756 try testCanonical(
816 \\// Test trailing comma syntax757 \\test {
817 \\// zig fmt: off758 \\ const x = .{
759 \\ a,
760 \\ // foo
761 \\ b,
818 \\762 \\
819 \\const struct_trailing_comma = struct { x: i32, y: i32, };763 \\ c,
820 );764 \\ };
821}765 \\}
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;
836 \\766 \\
837 );767 );
838}768}
839769
840test "zig fmt: line comment following 'zig fmt: off'" {770test "zig fmt: array literal 0 element" {
841 try testCanonical(771 try testCanonical(
842 \\// zig fmt: off772 \\test {
843 \\// Test773 \\ const x = [_]u32{};
844 \\const e = f;774 \\}
775 \\
845 );776 );
846}777}
847778
848test "zig fmt: doc comment following 'zig fmt: off'" {779test "zig fmt: array literal 1 element" {
849 try testCanonical(780 try testCanonical(
850 \\// zig fmt: off781 \\test {
851 \\/// test782 \\ const x = [_]u32{a};
852 \\const e = f;783 \\}
784 \\
853 );785 );
854}786}
855787
856test "zig fmt: line and doc comment following 'zig fmt: off'" {788test "zig fmt: array literal 1 element comma" {
857 try testCanonical(789 try testCanonical(
858 \\// zig fmt: off790 \\test {
859 \\// test 1791 \\ const x = [1]u32{
860 \\/// test 2792 \\ a,
861 \\const e = f;793 \\ };
794 \\}
795 \\
862 );796 );
863}797}
864798
865test "zig fmt: doc and line comment following 'zig fmt: off'" {799test "zig fmt: array literal 2 element" {
866 try testCanonical(800 try testCanonical(
867 \\// zig fmt: off801 \\test {
868 \\/// test 1802 \\ const x = [_]u32{ a, b };
869 \\// test 2803 \\}
870 \\const e = f;804 \\
871 );805 );
872}806}
873807
874test "zig fmt: alternating 'zig fmt: off' and 'zig fmt: on'" {808test "zig fmt: array literal 2 element comma" {
875 try testCanonical(809 try testCanonical(
876 \\// zig fmt: off810 \\test {
877 \\// zig fmt: on811 \\ const x = [2]u32{
878 \\// zig fmt: off812 \\ a,
879 \\const e = f;813 \\ b,
880 \\// zig fmt: off814 \\ };
881 \\// zig fmt: on815 \\}
882 \\// zig fmt: off
883 \\const a = b;
884 \\// zig fmt: on
885 \\const c = d;
886 \\// zig fmt: on
887 \\816 \\
888 );817 );
889}818}
890819
891test "zig fmt: line comment following 'zig fmt: on'" {820test "zig fmt: array literal 3 element" {
892 try testCanonical(821 try testCanonical(
893 \\// zig fmt: off822 \\test {
894 \\const e = f;823 \\ const x = [_]u32{ a, b, c };
895 \\// zig fmt: on824 \\}
896 \\// test
897 \\const e = f;
898 \\825 \\
899 );826 );
900}827}
901828
902test "zig fmt: doc comment following 'zig fmt: on'" {829test "zig fmt: array literal 3 element comma" {
903 try testCanonical(830 try testCanonical(
904 \\// zig fmt: off831 \\test {
905 \\const e = f;832 \\ const x = [3]u32{
906 \\// zig fmt: on833 \\ a,
907 \\/// test834 \\ b,
908 \\const e = f;835 \\ c,
836 \\ };
837 \\}
909 \\838 \\
910 );839 );
911}840}
912841
913test "zig fmt: line and doc comment following 'zig fmt: on'" {842test "zig fmt: sentinel array literal 1 element" {
914 try testCanonical(843 try testCanonical(
915 \\// zig fmt: off844 \\test {
916 \\const e = f;845 \\ const x = [_:9000]u32{a};
917 \\// zig fmt: on846 \\}
918 \\// test1
919 \\/// test2
920 \\const e = f;
921 \\847 \\
922 );848 );
923}849}
924850
925test "zig fmt: doc and line comment following 'zig fmt: on'" {851test "zig fmt: slices" {
926 try testCanonical(852 try testCanonical(
927 \\// zig fmt: off853 \\const a = b[0..];
928 \\const e = f;854 \\const c = d[0..1];
929 \\// zig fmt: on855 \\const e = f[0..1 :0];
930 \\/// test1
931 \\// test2
932 \\const e = f;
933 \\856 \\
934 );857 );
935}858}
936859
937test "zig fmt: pointer of unknown length" {860test "zig fmt: slices with spaces in bounds" {
938 try testCanonical(861 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];
940 \\865 \\
941 );866 );
942}867}
943868
944test "zig fmt: spaces around slice operator" {869test "zig fmt: block in slice expression" {
945 try testCanonical(870 try testCanonical(
946 \\var a = b[c..d];871 \\const a = b[{
947 \\var a = b[c..d :0];872 \\ _ = x;
948 \\var a = b[c + 1 .. d];873 \\}..];
949 \\var a = b[c + 1 ..];874 \\const c = d[0..{
950 \\var a = b[c .. d + 1];875 \\ _ = x;
951 \\var a = b[c .. d + 1 :0];876 \\ _ = y;
952 \\var a = b[c.a..d.e];877 \\}];
953 \\var a = b[c.a..d.e :0];878 \\const e = f[0..1 :{
879 \\ _ = x;
880 \\ _ = y;
881 \\ _ = z;
882 \\}];
954 \\883 \\
955 );884 );
956}885}
957886
958test "zig fmt: async call in if condition" {887test "zig fmt: async function" {
959 try testCanonical(888 try testCanonical(
960 \\comptime {889 \\pub const Server = struct {
961 \\ if (async b()) {890 \\ handleRequestFn: fn (*Server, *const std.net.Address, File) callconv(.Async) void,
962 \\ a();891 \\};
963 \\ }892 \\test "hi" {
893 \\ var ptr = @ptrCast(fn (i32) callconv(.Async) void, other);
964 \\}894 \\}
965 \\895 \\
966 );896 );
967}897}
968898
969test "zig fmt: 2nd arg multiline string" {899test "zig fmt: whitespace fixes" {
970 try testCanonical(900 try testTransform("test \"\" {\r\n\tconst hi = x;\r\n}\n// zig fmt: off\ntest \"\"{\r\n\tconst a = b;}\r\n",
971 \\comptime {901 \\test "" {
972 \\ cases.addAsm("hello world linux x86_64",902 \\ const hi = x;
973 \\ \\.text
974 \\ , "Hello, world!\n");
975 \\}903 \\}
904 \\// zig fmt: off
905 \\test ""{
906 \\ const a = b;}
976 \\907 \\
977 );908 );
978}909}
979910
980test "zig fmt: 2nd arg multiline string many args" {911test "zig fmt: while else err prong with no block" {
981 try testCanonical(912 try testCanonical(
982 \\comptime {913 \\test "" {
983 \\ cases.addAsm("hello world linux x86_64",914 \\ const result = while (returnError()) |value| {
984 \\ \\.text915 \\ break value;
985 \\ , "Hello, world!\n", "Hello, world!\n");916 \\ } else |err| @as(i32, 2);
917 \\ expect(result == 2);
986 \\}918 \\}
987 \\919 \\
988 );920 );
989}921}
990922
991test "zig fmt: final arg multiline string" {923test "zig fmt: tagged union with enum values" {
992 try testCanonical(924 try testCanonical(
993 \\comptime {925 \\const MultipleChoice2 = union(enum(u32)) {
994 \\ cases.addAsm("hello world linux x86_64", "Hello, world!\n",926 \\ Unspecified1: i32,
995 \\ \\.text927 \\ A: f32 = 20,
996 \\ );928 \\ Unspecified2: void,
997 \\}929 \\ B: bool = 40,
930 \\ Unspecified3: i32,
931 \\ C: i8 = 60,
932 \\ Unspecified4: void,
933 \\ D: void = 1000,
934 \\ Unspecified5: i32,
935 \\};
998 \\936 \\
999 );937 );
1000}938}
1001939
1002test "zig fmt: if condition wraps" {940test "zig fmt: tagged union enum tag last token" {
1003 try testTransform(941 try testCanonical(
1004 \\comptime {942 \\test {
1005 \\ if (cond and943 \\ const U = union(enum(u32)) {};
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;
1037 \\}944 \\}
1038 ,945 \\
1039 \\comptime {946 \\test {
1040 \\ if (cond and947 \\ const U = union(enum(u32)) { foo };
1041 \\ cond)948 \\}
1042 \\ {949 \\
1043 \\ return x;950 \\test {
1044 \\ }951 \\ const U = union(enum(u32)) {
1045 \\ while (cond and952 \\ foo,
1046 \\ cond)953 \\ };
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;
1078 \\}954 \\}
1079 \\955 \\
1080 );956 );
1081}957}
1082958
1083test "zig fmt: if condition has line break but must not wrap" {959test "zig fmt: allowzero pointer" {
1084 try testCanonical(960 try testCanonical(
1085 \\comptime {961 \\const T = [*]allowzero const u8;
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 \\}
1103 \\962 \\
1104 );963 );
1105}964}
1106965
1107test "zig fmt: if condition has line break but must not wrap" {966test "zig fmt: enum literal" {
1108 try testCanonical(967 try testCanonical(
1109 \\comptime {968 \\const x = .hi;
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 \\}
1124 \\969 \\
1125 );970 );
1126}971}
1127972
1128test "zig fmt: function call with multiline argument" {973test "zig fmt: enum literal inside array literal" {
1129 try testCanonical(974 try testCanonical(
1130 \\comptime {975 \\test "enums in arrays" {
1131 \\ self.user_input_options.put(name, UserInputOption{976 \\ var colors = []Color{.Green};
1132 \\ .name = name,977 \\ colors = []Colors{ .Green, .Cyan };
1133 \\ .used = false,978 \\ colors = []Colors{
1134 \\ });979 \\ .Grey,
980 \\ .Green,
981 \\ .Cyan,
982 \\ };
1135 \\}983 \\}
1136 \\984 \\
1137 );985 );
1138}986}
1139987
1140test "zig fmt: same-line doc comment on variable declaration" {988test "zig fmt: character literal larger than u8" {
1141 try testTransform(989 try testCanonical(
1142 \\pub const MAP_ANONYMOUS = 0x1000; /// allocated from memory, swap space990 \\const x = '\u{01f4a9}';
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;
1161 \\991 \\
1162 );992 );
1163}993}
1164994
1165test "zig fmt: if-else with comment before else" {995test "zig fmt: infix operator and then multiline string literal" {
1166 try testCanonical(996 try testCanonical(
1167 \\comptime {997 \\const x = "" ++
1168 \\ // cexp(finite|nan +- i inf|nan) = nan + i nan998 \\ \\ hi
1169 \\ if ((hx & 0x7fffffff) != 0x7f800000) {999 \\;
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 \\}
1179 \\1000 \\
1180 );1001 );
1181}1002}
11821003
1183test "zig fmt: if nested" {1004test "zig fmt: infix operator and then multiline string literal" {
1184 try testCanonical(1005 try testCanonical(
1185 \\pub fn foo() void {1006 \\const x = "" ++
1186 \\ return if ((aInt & bInt) >= 0)1007 \\ \\ hi0
1187 \\ if (aInt < bInt)1008 \\ \\ hi1
1188 \\ GE_LESS1009 \\ \\ hi2
1189 \\ else if (aInt == bInt)1010 \\;
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 \\}
1200 \\1011 \\
1201 );1012 );
1202}1013}
12031014
1204test "zig fmt: respect line breaks in if-else" {1015test "zig fmt: C pointers" {
1205 try testCanonical(1016 try testCanonical(
1206 \\comptime {1017 \\const Ptr = [*c]i32;
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 \\}
1219 \\1018 \\
1220 );1019 );
1221}1020}
12221021
1223test "zig fmt: respect line breaks after infix operators" {1022test "zig fmt: threadlocal" {
1224 try testCanonical(1023 try testCanonical(
1225 \\comptime {1024 \\threadlocal var x: i32 = 1234;
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 \\}
1236 \\1025 \\
1237 );1026 );
1238}1027}
12391028
1240test "zig fmt: fn decl with trailing comma" {1029test "zig fmt: linksection" {
1241 try testTransform(1030 try testCanonical(
1242 \\fn foo(a: i32, b: i32,) void {}1031 \\export var aoeu: u64 linksection(".text.derp") = 1234;
1243 ,1032 \\export fn _start() linksection(".text.boot") callconv(.Naked) noreturn {}
1244 \\fn foo(
1245 \\ a: i32,
1246 \\ b: i32,
1247 \\) void {}
1248 \\1033 \\
1249 );1034 );
1250}1035}
12511036
1252test "zig fmt: enum decl with no trailing comma" {1037test "zig fmt: correctly space struct fields with doc comments" {
1253 try testTransform(1038 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 \\
1255 ,1051 ,
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 \\};
1257 \\1062 \\
1258 );1063 );
1259}1064}
12601065
1261test "zig fmt: switch comment before prong" {1066test "zig fmt: doc comments on param decl" {
1262 try testCanonical(1067 try testCanonical(
1263 \\comptime {1068 \\pub const Allocator = struct {
1264 \\ switch (a) {1069 \\ shrinkFn: fn (
1265 \\ // hi1070 \\ self: *Allocator,
1266 \\ 0 => {},1071 \\ /// Guaranteed to be the same as what was returned from most recent call to
1267 \\ }1072 \\ /// `allocFn`, `reallocFn`, or `shrinkFn`.
1268 \\}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 \\};
1269 \\1083 \\
1270 );1084 );
1271}1085}
12721086
1273test "zig fmt: struct literal no trailing comma" {1087test "zig fmt: aligned struct field" {
1274 try testTransform(1088 try testCanonical(
1275 \\const a = foo{ .x = 1, .y = 2 };1089 \\pub const S = struct {
1276 \\const a = foo{ .x = 1,1090 \\ f: i32 align(32),
1277 \\ .y = 2 };1091 \\};
1278 ,1092 \\
1279 \\const a = foo{ .x = 1, .y = 2 };1093 );
1280 \\const a = foo{1094 try testCanonical(
1281 \\ .x = 1,1095 \\pub const S = struct {
1282 \\ .y = 2,1096 \\ f: i32 align(32) = 1,
1283 \\};1097 \\};
1284 \\1098 \\
1285 );1099 );
1286}1100}
12871101
1288test "zig fmt: struct literal containing a multiline expression" {1102test "zig fmt: comment to disable/enable zig fmt first" {
1289 try testTransform(1103 try testCanonical(
1290 \\const a = A{ .x = if (f1()) 10 else 20 };1104 \\// Test trailing comma syntax
1291 \\const a = A{ .x = if (f1()) 10 else 20, };1105 \\// zig fmt: off
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 \\};
1328 \\1106 \\
1107 \\const struct_trailing_comma = struct { x: i32, y: i32, };
1329 );1108 );
1330}1109}
13311110
1332test "zig fmt: array literal with hint" {1111test "zig fmt: comment to disable/enable zig fmt" {
1333 try testTransform(1112 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{
1335 \\ 1, 2, //1663 \\ 1, 2, //
1336 \\ 3,1664 \\ 3,
1337 \\ 4,1665 \\ 4,
...@@ -1368,19 +1696,19 @@ test "zig fmt: array literal with hint" {...@@ -1368,19 +1696,19 @@ test "zig fmt: array literal with hint" {
1368 \\};1696 \\};
1369 ,1697 ,
1370 \\const a = []u8{1698 \\const a = []u8{
1371 \\ 1, 2,1699 \\ 1, 2, //
1372 \\ 3, 4,1700 \\ 3, 4,
1373 \\ 5, 6,1701 \\ 5, 6,
1374 \\ 7,1702 \\ 7,
1375 \\};1703 \\};
1376 \\const a = []u8{1704 \\const a = []u8{
1377 \\ 1, 2,1705 \\ 1, 2, //
1378 \\ 3, 4,1706 \\ 3, 4,
1379 \\ 5, 6,1707 \\ 5, 6,
1380 \\ 7, 8,1708 \\ 7, 8,
1381 \\};1709 \\};
1382 \\const a = []u8{1710 \\const a = []u8{
1383 \\ 1, 2,1711 \\ 1, 2, //
1384 \\ 3, 4,1712 \\ 3, 4,
1385 \\ 5,1713 \\ 5,
1386 \\ 6, // blah1714 \\ 6, // blah
...@@ -1388,21 +1716,19 @@ test "zig fmt: array literal with hint" {...@@ -1388,21 +1716,19 @@ test "zig fmt: array literal with hint" {
1388 \\ 8,1716 \\ 8,
1389 \\};1717 \\};
1390 \\const a = []u8{1718 \\const a = []u8{
1391 \\ 1, 2,1719 \\ 1, 2, //
1392 \\ 3, //1720 \\ 3, //
1393 \\ 4,1721 \\ 4,
1394 \\ 5, 6,1722 \\ 5,
1723 \\ 6,
1395 \\ 7,1724 \\ 7,
1396 \\};1725 \\};
1397 \\const a = []u8{1726 \\const a = []u8{
1398 \\ 1,1727 \\ 1,
1399 \\ 2,1728 \\ 2,
1400 \\ 3,1729 \\ 3, 4, //
1401 \\ 4,1730 \\ 5, 6, //
1402 \\ 5,1731 \\ 7, 8, //
1403 \\ 6,
1404 \\ 7,
1405 \\ 8,
1406 \\};1732 \\};
1407 \\1733 \\
1408 );1734 );
...@@ -1508,11 +1834,21 @@ test "zig fmt: empty block with only comment" {...@@ -1508,11 +1834,21 @@ test "zig fmt: empty block with only comment" {
1508 );1834 );
1509}1835}
15101836
1511test "zig fmt: no trailing comma on struct decl" {1837test "zig fmt: trailing commas on struct decl" {
1512 try testCanonical(1838 try testTransform(
1513 \\const RoundParam = struct {1839 \\const RoundParam = struct {
1514 \\ k: usize, s: u32, t: u321840 \\ k: usize, s: u32, t: u32
1515 \\};1841 \\};
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 \\};
1516 \\1852 \\
1517 );1853 );
1518}1854}
...@@ -1560,11 +1896,7 @@ test "zig fmt: simple asm" {...@@ -1560,11 +1896,7 @@ test "zig fmt: simple asm" {
1560 \\ : [a] "x" (-> i32)1896 \\ : [a] "x" (-> i32)
1561 \\ : [a] "x" (1)1897 \\ : [a] "x" (1)
1562 \\ );1898 \\ );
1563 \\ asm ("still not real assembly"1899 \\ asm ("still not real assembly" ::: "a", "b");
1564 \\ :
1565 \\ :
1566 \\ : "a", "b"
1567 \\ );
1568 \\}1900 \\}
1569 \\1901 \\
1570 );1902 );
...@@ -1581,7 +1913,7 @@ test "zig fmt: nested struct literal with one item" {...@@ -1581,7 +1913,7 @@ test "zig fmt: nested struct literal with one item" {
15811913
1582test "zig fmt: switch cases trailing comma" {1914test "zig fmt: switch cases trailing comma" {
1583 try testTransform(1915 try testTransform(
1584 \\fn switch_cases(x: i32) void {1916 \\test "switch cases trailing comma"{
1585 \\ switch (x) {1917 \\ switch (x) {
1586 \\ 1,2,3 => {},1918 \\ 1,2,3 => {},
1587 \\ 4,5, => {},1919 \\ 4,5, => {},
...@@ -1590,7 +1922,7 @@ test "zig fmt: switch cases trailing comma" {...@@ -1590,7 +1922,7 @@ test "zig fmt: switch cases trailing comma" {
1590 \\ }1922 \\ }
1591 \\}1923 \\}
1592 ,1924 ,
1593 \\fn switch_cases(x: i32) void {1925 \\test "switch cases trailing comma" {
1594 \\ switch (x) {1926 \\ switch (x) {
1595 \\ 1, 2, 3 => {},1927 \\ 1, 2, 3 => {},
1596 \\ 4,1928 \\ 4,
...@@ -1657,18 +1989,18 @@ test "zig fmt: line comment after doc comment" {...@@ -1657,18 +1989,18 @@ test "zig fmt: line comment after doc comment" {
1657 );1989 );
1658}1990}
16591991
1660test "zig fmt: float literal with exponent" {1992test "zig fmt: bit field alignment" {
1661 try testCanonical(1993 try testCanonical(
1662 \\test "bit field alignment" {1994 \\test {
1663 \\ assert(@TypeOf(&blah.b) == *align(1:3:6) const u3);1995 \\ assert(@TypeOf(&blah.b) == *align(1:3:6) const u3);
1664 \\}1996 \\}
1665 \\1997 \\
1666 );1998 );
1667}1999}
16682000
1669test "zig fmt: float literal with exponent" {2001test "zig fmt: nested switch" {
1670 try testCanonical(2002 try testCanonical(
1671 \\test "aoeu" {2003 \\test {
1672 \\ switch (state) {2004 \\ switch (state) {
1673 \\ TermState.Start => switch (c) {2005 \\ TermState.Start => switch (c) {
1674 \\ '\x1b' => state = TermState.Escape,2006 \\ '\x1b' => state = TermState.Escape,
...@@ -1679,6 +2011,7 @@ test "zig fmt: float literal with exponent" {...@@ -1679,6 +2011,7 @@ test "zig fmt: float literal with exponent" {
1679 \\2011 \\
1680 );2012 );
1681}2013}
2014
1682test "zig fmt: float literal with exponent" {2015test "zig fmt: float literal with exponent" {
1683 try testCanonical(2016 try testCanonical(
1684 \\pub const f64_true_min = 4.94065645841246544177e-324;2017 \\pub const f64_true_min = 4.94065645841246544177e-324;
...@@ -2135,7 +2468,7 @@ test "zig fmt: preserve spacing" {...@@ -2135,7 +2468,7 @@ test "zig fmt: preserve spacing" {
2135test "zig fmt: return types" {2468test "zig fmt: return types" {
2136 try testCanonical(2469 try testCanonical(
2137 \\pub fn main() !void {}2470 \\pub fn main() !void {}
2138 \\pub fn main() anytype {}2471 \\pub fn main() FooBar {}
2139 \\pub fn main() i32 {}2472 \\pub fn main() i32 {}
2140 \\2473 \\
2141 );2474 );
...@@ -2207,6 +2540,33 @@ test "zig fmt: return" {...@@ -2207,6 +2540,33 @@ test "zig fmt: return" {
2207 );2540 );
2208}2541}
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
2210test "zig fmt: pointer attributes" {2570test "zig fmt: pointer attributes" {
2211 try testCanonical(2571 try testCanonical(
2212 \\extern fn f1(s: *align(*u8) u8) c_int;2572 \\extern fn f1(s: *align(*u8) u8) c_int;
...@@ -2220,11 +2580,11 @@ test "zig fmt: pointer attributes" {...@@ -2220,11 +2580,11 @@ test "zig fmt: pointer attributes" {
22202580
2221test "zig fmt: slice attributes" {2581test "zig fmt: slice attributes" {
2222 try testCanonical(2582 try testCanonical(
2223 \\extern fn f1(s: *align(*u8) u8) c_int;2583 \\extern fn f1(s: []align(*u8) u8) c_int;
2224 \\extern fn f2(s: **align(1) *const *volatile u8) c_int;2584 \\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;2585 \\extern fn f3(s: []align(1) const [:0]align(1) volatile []const volatile u8) c_int;
2226 \\extern fn f4(s: *align(1) const volatile u8) c_int;2586 \\extern fn f4(s: []align(1) const volatile u8) c_int;
2227 \\extern fn f5(s: [*:0]align(1) const volatile u8) c_int;2587 \\extern fn f5(s: [:0]align(1) const volatile u8) c_int;
2228 \\2588 \\
2229 );2589 );
2230}2590}
...@@ -2241,7 +2601,7 @@ test "zig fmt: test declaration" {...@@ -2241,7 +2601,7 @@ test "zig fmt: test declaration" {
22412601
2242test "zig fmt: infix operators" {2602test "zig fmt: infix operators" {
2243 try testCanonical(2603 try testCanonical(
2244 \\test "infix operators" {2604 \\test {
2245 \\ var i = undefined;2605 \\ var i = undefined;
2246 \\ i = 2;2606 \\ i = 2;
2247 \\ i *= 2;2607 \\ i *= 2;
...@@ -2345,7 +2705,7 @@ test "zig fmt: call expression" {...@@ -2345,7 +2705,7 @@ test "zig fmt: call expression" {
23452705
2346test "zig fmt: anytype type" {2706test "zig fmt: anytype type" {
2347 try testCanonical(2707 try testCanonical(
2348 \\fn print(args: anytype) anytype {}2708 \\fn print(args: anytype) @This() {}
2349 \\2709 \\
2350 );2710 );
2351}2711}
...@@ -2570,7 +2930,11 @@ test "zig fmt: catch" {...@@ -2570,7 +2930,11 @@ test "zig fmt: catch" {
2570 \\test "catch" {2930 \\test "catch" {
2571 \\ const a: anyerror!u8 = 0;2931 \\ const a: anyerror!u8 = 0;
2572 \\ _ = a catch return;2932 \\ _ = a catch return;
2933 \\ _ = a catch
2934 \\ return;
2573 \\ _ = a catch |err| return;2935 \\ _ = a catch |err| return;
2936 \\ _ = a catch |err|
2937 \\ return;
2574 \\}2938 \\}
2575 \\2939 \\
2576 );2940 );
...@@ -2746,12 +3110,6 @@ test "zig fmt: for" {...@@ -2746,12 +3110,6 @@ test "zig fmt: for" {
2746 \\ d => {},3110 \\ d => {},
2747 \\ };3111 \\ };
2748 \\3112 \\
2749 \\ for (a) |b|
2750 \\ switch (b) {
2751 \\ c => {},
2752 \\ d => {},
2753 \\ };
2754 \\
2755 \\ const res = for (a) |v, i| {3113 \\ const res = for (a) |v, i| {
2756 \\ break v;3114 \\ break v;
2757 \\ } else {3115 \\ } else {
...@@ -2777,7 +3135,8 @@ test "zig fmt: for" {...@@ -2777,7 +3135,8 @@ test "zig fmt: for" {
2777 \\test "fix for" {3135 \\test "fix for" {
2778 \\ for (a) |x|3136 \\ for (a) |x|
2779 \\ f(x)3137 \\ f(x)
2780 \\ else continue;3138 \\ else
3139 \\ continue;
2781 \\}3140 \\}
2782 \\3141 \\
2783 );3142 );
...@@ -2948,7 +3307,7 @@ test "zig fmt: nosuspend" {...@@ -2948,7 +3307,7 @@ test "zig fmt: nosuspend" {
29483307
2949test "zig fmt: Block after if" {3308test "zig fmt: Block after if" {
2950 try testCanonical(3309 try testCanonical(
2951 \\test "Block after if" {3310 \\test {
2952 \\ if (true) {3311 \\ if (true) {
2953 \\ const a = 0;3312 \\ const a = 0;
2954 \\ }3313 \\ }
...@@ -2961,7 +3320,7 @@ test "zig fmt: Block after if" {...@@ -2961,7 +3320,7 @@ test "zig fmt: Block after if" {
2961 );3320 );
2962}3321}
29633322
2964test "zig fmt: use" {3323test "zig fmt: usingnamespace" {
2965 try testCanonical(3324 try testCanonical(
2966 \\usingnamespace @import("std");3325 \\usingnamespace @import("std");
2967 \\pub usingnamespace @import("std");3326 \\pub usingnamespace @import("std");
...@@ -3025,10 +3384,7 @@ test "zig fmt: inline asm parameter alignment" {...@@ -3025,10 +3384,7 @@ test "zig fmt: inline asm parameter alignment" {
3025 \\ asm volatile (3384 \\ asm volatile (
3026 \\ \\ foo3385 \\ \\ foo
3027 \\ \\ bar3386 \\ \\ bar
3028 \\ :3387 \\ ::: "", "");
3029 \\ :
3030 \\ : "", ""
3031 \\ );
3032 \\ asm volatile (3388 \\ asm volatile (
3033 \\ \\ foo3389 \\ \\ foo
3034 \\ \\ bar3390 \\ \\ bar
...@@ -3087,16 +3443,12 @@ test "zig fmt: file ends with struct field" {...@@ -3087,16 +3443,12 @@ test "zig fmt: file ends with struct field" {
3087}3443}
30883444
3089test "zig fmt: comment after empty comment" {3445test "zig fmt: comment after empty comment" {
3090 try testTransform(3446 try testCanonical(
3091 \\const x = true; //3447 \\const x = true; //
3092 \\//3448 \\//
3093 \\//3449 \\//
3094 \\//a3450 \\//a
3095 \\3451 \\
3096 ,
3097 \\const x = true;
3098 \\//a
3099 \\
3100 );3452 );
3101}3453}
31023454
...@@ -3113,7 +3465,8 @@ test "zig fmt: line comment in array" {...@@ -3113,7 +3465,8 @@ test "zig fmt: line comment in array" {
3113 ,3465 ,
3114 \\test "a" {3466 \\test "a" {
3115 \\ var arr = [_]u32{3467 \\ var arr = [_]u32{
3116 \\ 0, // 1,3468 \\ 0,
3469 \\ // 1,
3117 \\ // 2,3470 \\ // 2,
3118 \\ };3471 \\ };
3119 \\}3472 \\}
...@@ -3141,7 +3494,8 @@ test "zig fmt: comment after params" {...@@ -3141,7 +3494,8 @@ test "zig fmt: comment after params" {
3141 \\3494 \\
3142 ,3495 ,
3143 \\fn a(3496 \\fn a(
3144 \\ b: u32, // c: u32,3497 \\ b: u32,
3498 \\ // c: u32,
3145 \\ // d: u32,3499 \\ // d: u32,
3146 \\) void {}3500 \\) void {}
3147 \\3501 \\
...@@ -3174,13 +3528,17 @@ test "zig fmt: comment in array initializer/access" {...@@ -3174,13 +3528,17 @@ test "zig fmt: comment in array initializer/access" {
3174 \\ var c = b[ //aa3528 \\ var c = b[ //aa
3175 \\ 03529 \\ 0
3176 \\ ];3530 \\ ];
3177 \\ var d = [_3531 \\ var d = [
3532 \\ _
3178 \\ //aa3533 \\ //aa
3534 \\ :
3535 \\ 0
3179 \\ ]x{ //aa3536 \\ ]x{ //aa
3180 \\ //bb3537 \\ //bb
3181 \\ 9,3538 \\ 9,
3182 \\ };3539 \\ };
3183 \\ var e = d[03540 \\ var e = d[
3541 \\ 0
3184 \\ //aa3542 \\ //aa
3185 \\ ];3543 \\ ];
3186 \\}3544 \\}
...@@ -3199,7 +3557,8 @@ test "zig fmt: comments at several places in struct init" {...@@ -3199,7 +3557,8 @@ test "zig fmt: comments at several places in struct init" {
3199 ,3557 ,
3200 \\var bar = Bar{3558 \\var bar = Bar{
3201 \\ .x = 10, // test3559 \\ .x = 10, // test
3202 \\ .y = "test", // test3560 \\ .y = "test",
3561 \\ // test
3203 \\};3562 \\};
3204 \\3563 \\
3205 );3564 );
...@@ -3214,7 +3573,7 @@ test "zig fmt: comments at several places in struct init" {...@@ -3214,7 +3573,7 @@ test "zig fmt: comments at several places in struct init" {
3214 );3573 );
3215}3574}
32163575
3217test "zig fmt: top level doc comments" {3576test "zig fmt: container doc comments" {
3218 try testCanonical(3577 try testCanonical(
3219 \\//! tld 13578 \\//! tld 1
3220 \\//! tld 23579 \\//! tld 2
...@@ -3235,25 +3594,25 @@ test "zig fmt: top level doc comments" {...@@ -3235,25 +3594,25 @@ test "zig fmt: top level doc comments" {
3235 \\ //! B tld 23594 \\ //! B tld 2
3236 \\ //! B tld 33595 \\ //! B tld 3
3237 \\3596 \\
3238 \\ /// b doc3597 \\ /// B doc
3239 \\ b: u32,3598 \\ b: u32,
3240 \\};3599 \\};
3241 \\3600 \\
3242 \\/// C doc3601 \\/// C doc
3243 \\const C = struct {3602 \\const C = union(enum) { // comment
3244 \\ //! C tld 13603 \\ //! C tld 1
3245 \\ //! C tld 23604 \\ //! C tld 2
3246 \\ //! C tld 33605 \\ //! C tld 3
3606 \\};
3247 \\3607 \\
3248 \\ /// c1 doc3608 \\/// D doc
3249 \\ c1: u32,3609 \\const D = union(Foo) {
3250 \\3610 \\ //! D tld 1
3251 \\ //! C tld 43611 \\ //! D tld 2
3252 \\ //! C tld 53612 \\ //! D tld 3
3253 \\ //! C tld 6
3254 \\3613 \\
3255 \\ /// c2 doc3614 \\ /// D doc
3256 \\ c2: u32,3615 \\ b: u32,
3257 \\};3616 \\};
3258 \\3617 \\
3259 );3618 );
...@@ -3275,8 +3634,31 @@ test "zig fmt: extern without container keyword returns error" {...@@ -3275,8 +3634,31 @@ test "zig fmt: extern without container keyword returns error" {
3275 \\const container = extern {};3634 \\const container = extern {};
3276 \\3635 \\
3277 , &[_]Error{3636 , &[_]Error{
3278 .ExpectedExpr,3637 .expected_container,
3279 .ExpectedVarDeclOrFn,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,
3280 });3662 });
3281}3663}
32823664
...@@ -3350,26 +3732,6 @@ test "zig fmt: hexadeciaml float literals with underscore separators" {...@@ -3350,26 +3732,6 @@ test "zig fmt: hexadeciaml float literals with underscore separators" {
3350 );3732 );
3351}3733}
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
3373test "zig fmt: C var args" {3735test "zig fmt: C var args" {
3374 try testCanonical(3736 try testCanonical(
3375 \\pub extern "c" fn printf(format: [*:0]const u8, ...) c_int;3737 \\pub extern "c" fn printf(format: [*:0]const u8, ...) c_int;
...@@ -3458,6 +3820,54 @@ test "zig fmt: test comments in field access chain" {...@@ -3458,6 +3820,54 @@ test "zig fmt: test comments in field access chain" {
3458 );3820 );
3459}3821}
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
3461test "zig fmt: Indent comma correctly after multiline string literals in arg list (trailing comma)" {3871test "zig fmt: Indent comma correctly after multiline string literals in arg list (trailing comma)" {
3462 try testCanonical(3872 try testCanonical(
3463 \\fn foo() void {3873 \\fn foo() void {
...@@ -3495,8 +3905,7 @@ test "zig fmt: Control flow statement as body of blockless if" {...@@ -3495,8 +3905,7 @@ test "zig fmt: Control flow statement as body of blockless if" {
3495 \\3905 \\
3496 \\ const zoom_node = if (focused_node == layout_first) while (it.next()) |node| {3906 \\ const zoom_node = if (focused_node == layout_first) while (it.next()) |node| {
3497 \\ if (!node.view.pending.float and !node.view.pending.fullscreen) break node;3907 \\ if (!node.view.pending.float and !node.view.pending.fullscreen) break node;
3498 \\ } else null else3908 \\ } else null else focused_node;
3499 \\ focused_node;
3500 \\3909 \\
3501 \\ const zoom_node = if (focused_node == layout_first)3910 \\ const zoom_node = if (focused_node == layout_first)
3502 \\ if (it.next()) {3911 \\ if (it.next()) {
...@@ -3513,14 +3922,13 @@ test "zig fmt: Control flow statement as body of blockless if" {...@@ -3513,14 +3922,13 @@ test "zig fmt: Control flow statement as body of blockless if" {
3513 \\3922 \\
3514 \\ const zoom_node = if (focused_node == layout_first) switch (nodes) {3923 \\ const zoom_node = if (focused_node == layout_first) switch (nodes) {
3515 \\ 0 => 0,3924 \\ 0 => 0,
3516 \\ } else3925 \\ } else focused_node;
3517 \\ focused_node;
3518 \\}3926 \\}
3519 \\3927 \\
3520 );3928 );
3521}3929}
35223930
3523test "zig fmt: " {3931test "zig fmt: regression test for #5722" {
3524 try testCanonical(3932 try testCanonical(
3525 \\pub fn sendViewTags(self: Self) void {3933 \\pub fn sendViewTags(self: Self) void {
3526 \\ var it = ViewStack(View).iterator(self.output.views.first, std.math.maxInt(u32));3934 \\ 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...@@ -3580,8 +3988,8 @@ test "zig fmt: multiline string literals should play nice with array initializer
3580 \\ 0,3988 \\ 0,
3581 \\ }}}}}}}};3989 \\ }}}}}}}};
3582 \\ myFunc(.{3990 \\ myFunc(.{
3583 \\ "aaaaaaa", "bbbbbb", "ccccc",3991 \\ "aaaaaaa", "bbbbbb", "ccccc",
3584 \\ "dddd", ("eee"), ("fff"),3992 \\ "dddd", ("eee"), ("fff"),
3585 \\ ("gggg"),3993 \\ ("gggg"),
3586 \\ // Line comment3994 \\ // Line comment
3587 \\ \\Multiline String Literals can be quite long3995 \\ \\Multiline String Literals can be quite long
...@@ -3610,9 +4018,11 @@ test "zig fmt: multiline string literals should play nice with array initializer...@@ -3610,9 +4018,11 @@ test "zig fmt: multiline string literals should play nice with array initializer
3610 \\ (4018 \\ (
3611 \\ \\ xxx4019 \\ \\ xxx
3612 \\ ),4020 \\ ),
3613 \\ "xxx", "xxx",4021 \\ "xxx",
4022 \\ "xxx",
3614 \\ },4023 \\ },
3615 \\ .{ "xxxxxxx", "xxx", "xxx", "xxx" }, .{ "xxxxxxx", "xxx", "xxx", "xxx" },4024 \\ .{ "xxxxxxx", "xxx", "xxx", "xxx" },
4025 \\ .{ "xxxxxxx", "xxx", "xxx", "xxx" },
3616 \\ "aaaaaaa", "bbbbbb", "ccccc", // -4026 \\ "aaaaaaa", "bbbbbb", "ccccc", // -
3617 \\ "dddd", ("eee"), ("fff"),4027 \\ "dddd", ("eee"), ("fff"),
3618 \\ .{4028 \\ .{
...@@ -3620,7 +4030,8 @@ test "zig fmt: multiline string literals should play nice with array initializer...@@ -3620,7 +4030,8 @@ test "zig fmt: multiline string literals should play nice with array initializer
3620 \\ (4030 \\ (
3621 \\ \\ xxx4031 \\ \\ xxx
3622 \\ ),4032 \\ ),
3623 \\ "xxxxxxxxxxxxxx", "xxx",4033 \\ "xxxxxxxxxxxxxx",
4034 \\ "xxx",
3624 \\ },4035 \\ },
3625 \\ .{4036 \\ .{
3626 \\ (4037 \\ (
...@@ -3636,10 +4047,10 @@ test "zig fmt: multiline string literals should play nice with array initializer...@@ -3636,10 +4047,10 @@ test "zig fmt: multiline string literals should play nice with array initializer
3636 );4047 );
3637}4048}
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" {
3640 try testCanonical(4051 try testCanonical(
3641 \\pub fn makeMemUndefined(qzz: []u8) i1 {4052 \\pub fn makeMemUndefined(qzz: []u8) i1 {
3642 \\ cases.add( // fixed bug #20324053 \\ cases.add( // fixed bug foo
3643 \\ "compile diagnostic string for top level decl type",4054 \\ "compile diagnostic string for top level decl type",
3644 \\ \\export fn entry() void {4055 \\ \\export fn entry() void {
3645 \\ \\ var foo: u32 = @This(){};4056 \\ \\ var foo: u32 = @This(){};
...@@ -3657,72 +4068,474 @@ test "zig fmt: use of comments and Multiline string literals may force the param...@@ -3657,72 +4068,474 @@ test "zig fmt: use of comments and Multiline string literals may force the param
3657 \\ .MakeMemUndefined, @ptrToInt(qzz.ptr), qzz.len, 0, 0, 0));4068 \\ .MakeMemUndefined, @ptrToInt(qzz.ptr), qzz.len, 0, 0, 0));
3658 \\}4069 \\}
3659 \\4070 \\
3660 \\// This looks like garbage don't do this4071 \\// This looks like garbage don't do this
3661 \\const rparen = tree.prevToken(4072 \\const rparen = tree.prevToken(
3662 \\// the first token for the annotation expressions is the left4073 \\// the first token for the annotation expressions is the left
3663 \\// parenthesis, hence the need for two prevToken4074 \\// parenthesis, hence the need for two prevToken
3664 \\ if (fn_proto.getAlignExpr()) |align_expr|4075 \\if (fn_proto.getAlignExpr()) |align_expr|
3665 \\ tree.prevToken(tree.prevToken(align_expr.firstToken()))4076 \\ tree.prevToken(tree.prevToken(align_expr.firstToken()))
3666 \\else if (fn_proto.getSectionExpr()) |section_expr|4077 \\else if (fn_proto.getSectionExpr()) |section_expr|
3667 \\ tree.prevToken(tree.prevToken(section_expr.firstToken()))4078 \\ tree.prevToken(tree.prevToken(section_expr.firstToken()))
3668 \\else if (fn_proto.getCallconvExpr()) |callconv_expr|4079 \\else if (fn_proto.getCallconvExpr()) |callconv_expr|
3669 \\ tree.prevToken(tree.prevToken(callconv_expr.firstToken()))4080 \\ tree.prevToken(tree.prevToken(callconv_expr.firstToken()))
3670 \\else switch (fn_proto.return_type) {4081 \\else switch (fn_proto.return_type) {
3671 \\ .Explicit => |node| node.firstToken(),4082 \\ .Explicit => |node| node.firstToken(),
3672 \\ .InferErrorSet => |node| tree.prevToken(node.firstToken()),4083 \\ .InferErrorSet => |node| tree.prevToken(node.firstToken()),
3673 \\ .Invalid => unreachable,4084 \\ .Invalid => unreachable,
3674 \\});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;
3675 \\4232 \\
3676 );4233 );
3677}4234}
36784235
3679test "zig fmt: single argument trailing commas in @builtins()" {4236test "zig fmt: respect extra newline between switch items" {
3680 try testCanonical(4237 try testCanonical(
3681 \\pub fn foo(qzz: []u8) i1 {4238 \\const a = switch (b) {
3682 \\ @panic(4239 \\ .c => {},
3683 \\ foo,4240 \\
3684 \\ );4241 \\ .d,
3685 \\ panic(4242 \\ .e,
3686 \\ foo,4243 \\ => f,
3687 \\ );4244 \\};
3688 \\ @panic(
3689 \\ foo,
3690 \\ bar,
3691 \\ );
3692 \\}
3693 \\4245 \\
3694 );4246 );
3695}4247}
36964248
3697test "zig fmt: trailing comma should force multiline 1 column" {4249test "zig fmt: insert trailing comma if there are comments between switch values" {
3698 try testTransform(4250 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 \\};
3700 \\4262 \\
3701 ,4263 ,
3702 \\pub const UUID_NULL: uuid_t = [16]u8{4264 \\const a = switch (b) {
3703 \\ 0,4265 \\ .c => {},
3704 \\ 0,4266 \\
3705 \\ 0,4267 \\ .d, // foobar
3706 \\ 0,4268 \\ .e,
4269 \\ => f,
4270 \\
4271 \\ .g,
4272 \\ .h,
4273 \\ // comment
4274 \\ => i,
3707 \\};4275 \\};
3708 \\4276 \\
3709 );4277 );
3710}4278}
37114279
3712test "zig fmt: function params should align nicely" {4280test "zig fmt: error for invalid bit range" {
3713 try testCanonical(4281 try testError(
3714 \\pub fn foo() void {4282 \\var x: []align(0:0:0)u8 = bar;
3715 \\ cases.addRuntimeSafety("slicing operator with sentinel",4283 , &[_]Error{
3716 \\ \\const std = @import("std");4284 .invalid_bit_range,
3717 \\ ++ check_panic_msg ++4285 });
3718 \\ \\pub fn main() void {4286}
3719 \\ \\ var buf = [4]u8{'a','b','c',0};4287
3720 \\ \\ const slice = buf[0..:0];4288test "zig fmt: error for invalid align" {
3721 \\ \\}4289 try testError(
3722 \\ );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;
3723 \\}4311 \\}
3724 \\4312 , &[_]Error{
3725 );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 });
3726}4539}
37274540
3728const std = @import("std");4541const std = @import("std");
...@@ -3736,12 +4549,12 @@ var fixed_buffer_mem: [100 * 1024]u8 = undefined;...@@ -3736,12 +4549,12 @@ var fixed_buffer_mem: [100 * 1024]u8 = undefined;
3736fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *bool) ![]u8 {4549fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *bool) ![]u8 {
3737 const stderr = io.getStdErr().writer();4550 const stderr = io.getStdErr().writer();
37384551
3739 const tree = try std.zig.parse(allocator, source);4552 var tree = try std.zig.parse(allocator, source);
3740 defer tree.deinit();4553 defer tree.deinit(allocator);
37414554
3742 for (tree.errors) |*parse_error| {4555 for (tree.errors) |parse_error| {
3743 const token = tree.token_locs[parse_error.loc()];4556 const token_start = tree.tokens.items(.start)[parse_error.token];
3744 const loc = tree.tokenLocation(0, parse_error.loc());4557 const loc = tree.tokenLocation(0, parse_error.token);
3745 try stderr.print("(memory buffer):{d}:{d}: error: ", .{ loc.line + 1, loc.column + 1 });4558 try stderr.print("(memory buffer):{d}:{d}: error: ", .{ loc.line + 1, loc.column + 1 });
3746 try tree.renderError(parse_error, stderr);4559 try tree.renderError(parse_error, stderr);
3747 try stderr.print("\n{s}\n", .{source[loc.line_start..loc.line_end]});4560 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...@@ -3750,13 +4563,7 @@ fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *b
3750 while (i < loc.column) : (i += 1) {4563 while (i < loc.column) : (i += 1) {
3751 try stderr.writeAll(" ");4564 try stderr.writeAll(" ");
3752 }4565 }
3753 }4566 try stderr.writeAll("^");
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 }
3760 }4567 }
3761 try stderr.writeAll("\n");4568 try stderr.writeAll("\n");
3762 }4569 }
...@@ -3764,12 +4571,9 @@ fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *b...@@ -3764,12 +4571,9 @@ fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *b
3764 return error.ParseError;4571 return error.ParseError;
3765 }4572 }
37664573
3767 var buffer = std.ArrayList(u8).init(allocator);4574 const formatted = try tree.render(allocator);
3768 errdefer buffer.deinit();4575 anything_changed.* = !mem.eql(u8, formatted, source);
37694576 return formatted;
3770 const writer = buffer.writer();
3771 anything_changed.* = try std.zig.render(allocator, writer, tree);
3772 return buffer.toOwnedSlice();
3773}4577}
3774fn testTransform(source: []const u8, expected_source: []const u8) !void {4578fn testTransform(source: []const u8, expected_source: []const u8) !void {
3775 const needed_alloc_count = x: {4579 const needed_alloc_count = x: {
...@@ -3822,14 +4626,14 @@ fn testCanonical(source: []const u8) !void {...@@ -3822,14 +4626,14 @@ fn testCanonical(source: []const u8) !void {
3822 return testTransform(source, source);4626 return testTransform(source, source);
3823}4627}
38244628
3825const Error = std.meta.Tag(std.zig.ast.Error);4629const Error = std.zig.ast.Error.Tag;
38264630
3827fn testError(source: []const u8, expected_errors: []const Error) !void {4631fn testError(source: []const u8, expected_errors: []const Error) !void {
3828 const tree = try std.zig.parse(std.testing.allocator, source);4632 var tree = try std.zig.parse(std.testing.allocator, source);
3829 defer tree.deinit();4633 defer tree.deinit(std.testing.allocator);
38304634
3831 std.testing.expect(tree.errors.len == expected_errors.len);4635 std.testing.expect(tree.errors.len == expected_errors.len);
3832 for (expected_errors) |expected, i| {4636 for (expected_errors) |expected, i| {
3833 std.testing.expect(expected == tree.errors[i]);4637 std.testing.expectEqual(expected, tree.errors[i].tag);
3834 }4638 }
3835}4639}
lib/std/zig/render.zig+2385-2340
...@@ -6,6 +6,7 @@...@@ -6,6 +6,7 @@
6const std = @import("../std.zig");6const std = @import("../std.zig");
7const assert = std.debug.assert;7const assert = std.debug.assert;
8const mem = std.mem;8const mem = std.mem;
9const Allocator = std.mem.Allocator;
9const meta = std.meta;10const meta = std.meta;
10const ast = std.zig.ast;11const ast = std.zig.ast;
11const Token = std.zig.Token;12const Token = std.zig.Token;
...@@ -13,2657 +14,2555 @@ const Token = std.zig.Token;...@@ -13,2657 +14,2555 @@ const Token = std.zig.Token;
13const indent_delta = 4;14const indent_delta = 4;
14const asm_indent_delta = 2;15const asm_indent_delta = 2;
1516
16pub const Error = error{17pub const Error = ast.Tree.RenderError;
17 /// Ran out of memory allocating call stack frames to complete rendering.
18 OutOfMemory,
19};
2018
21/// Returns whether anything changed19const Ais = AutoIndentingStream(std.ArrayList(u8).Writer);
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);
2520
26 var change_detection_stream = std.io.changeDetectionStream(tree.source, stream);21pub fn renderTree(buffer: *std.ArrayList(u8), tree: ast.Tree) Error!void {
27 var auto_indenting_stream = std.io.autoIndentingStream(indent_delta, change_detection_stream.writer());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();33 if (tree.tokens.items(.tag)[0] == .container_doc_comment) {
32}34 try renderContainerDocComments(ais, tree, 0);
35 }
3336
34fn renderRoot(37 try renderMembers(buffer.allocator, ais, tree, tree.rootDecls());
35 allocator: *mem.Allocator,38
36 ais: anytype,39 if (ais.disabled_offset) |disabled_offset| {
37 tree: *ast.Tree,40 try writeFixingWhitespace(ais.underlying_writer, tree.source[disabled_offset..]);
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 }
50 }41 }
42}
5143
52 var decl_i: ast.NodeIndex = 0;44/// Render all members in the given slice, keeping empty lines where appropriate
53 const root_decls = tree.root_node.decls();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;54fn renderMember(gpa: *Allocator, ais: *Ais, tree: ast.Tree, decl: ast.Node.Index, space: Space) Error!void {
56 while (true) {55 const token_tags = tree.tokens.items(.tag);
57 var decl = root_decls[decl_i];56 const main_tokens = tree.nodes.items(.main_token);
5857 const datas = tree.nodes.items(.data);
59 // This loop does the following:58 try renderDocComments(ais, tree, tree.firstToken(decl));
60 //59 switch (tree.nodes.items(.tag)[decl]) {
61 // - Iterates through line/doc comment tokens that precedes the current60 .fn_decl => {
62 // decl.61 // Some examples:
63 // - Figures out the first token index (`copy_start_token_index`) which62 // pub extern "foo" fn ...
64 // hasn't been copied to the output stream yet.63 // export fn ...
65 // - Detects `zig fmt: (off|on)` in the line comment tokens, and64 const fn_proto = datas[decl].lhs;
66 // determines whether the current decl should be reformatted or not.65 const fn_token = main_tokens[fn_proto];
67 //66 // Go back to the first token we should render here.
68 var token_index = decl.firstToken();67 var i = fn_token;
69 var fmt_active = true;68 while (i > 0) {
70 var found_fmt_directive = false;69 i -= 1;
7170 switch (token_tags[i]) {
72 var copy_start_token_index = token_index;71 .keyword_extern,
7372 .keyword_export,
74 while (token_index != 0) {73 .keyword_pub,
75 token_index -= 1;74 .string_literal,
76 const token_id = tree.token_ids[token_index];75 .keyword_inline,
77 switch (token_id) {76 .keyword_noinline,
78 .LineComment => {},77 => continue,
79 .DocComment => {78
80 copy_start_token_index = token_index;79 else => {
81 continue;80 i += 1;
82 },81 break;
83 else => break,82 },
83 }
84 }84 }
8585 while (i < fn_token) : (i += 1) {
86 const token_loc = tree.token_locs[token_index];86 if (token_tags[i] == .keyword_inline) {
87 if (mem.eql(u8, mem.trim(u8, tree.tokenSliceLoc(token_loc)[2..], " "), "zig fmt: off")) {87 // TODO remove this special case when 0.9.0 is released.
88 if (!found_fmt_directive) {88 // See the commit that introduced this comment for more details.
89 fmt_active = false;89 continue;
90 found_fmt_directive = true;
91 }90 }
92 } else if (mem.eql(u8, mem.trim(u8, tree.tokenSliceLoc(token_loc)[2..], " "), "zig fmt: on")) {91 try renderToken(ais, tree, i, .space);
93 if (!found_fmt_directive) {92 }
94 fmt_active = true;93 assert(datas[decl].rhs != 0);
95 found_fmt_directive = true;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 },
96 }121 }
97 }122 }
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) {130 .@"usingnamespace" => {
101 // Reformatting is disabled for the current decl and possibly some131 const main_token = main_tokens[decl];
102 // more decls that follow.132 const expr = datas[decl].lhs;
103 // Find the next `decl` for which reformatting is re-enabled.133 if (main_token > 0 and token_tags[main_token - 1] == .keyword_pub) {
104 token_index = decl.firstToken();134 try renderToken(ais, tree, main_token - 1, .space); // pub
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 }
132 }135 }
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. Copy141 .global_var_decl => return renderVarDecl(gpa, ais, tree, tree.globalVarDecl(decl)),
135 // the input tokens before the `decl` that haven't been copied yet.142 .local_var_decl => return renderVarDecl(gpa, ais, tree, tree.localVarDecl(decl)),
136 var copy_end_token_index = decl.firstToken();143 .simple_var_decl => return renderVarDecl(gpa, ais, tree, tree.simpleVarDecl(decl)),
137 token_index = copy_end_token_index;144 .aligned_var_decl => return renderVarDecl(gpa, ais, tree, tree.alignedVarDecl(decl)),
138 while (token_index != 0) {145
139 token_index -= 1;146 .test_decl => {
140 const token_id = tree.token_ids[token_index];147 const test_token = main_tokens[decl];
141 switch (token_id) {148 try renderToken(ais, tree, test_token, .space);
142 .LineComment => {},149 if (token_tags[test_token + 1] == .string_literal) {
143 .DocComment => {150 try renderToken(ais, tree, test_token + 1, .space);
144 copy_end_token_index = token_index;
145 continue;
146 },
147 else => break,
148 }
149 }151 }
152 try renderExpression(gpa, ais, tree, datas[decl].rhs, space);
153 },
150154
151 const start = tree.token_locs[copy_start_token_index].start;155 .container_field_init => return renderContainerField(gpa, ais, tree, tree.containerFieldInit(decl), space),
152 const end = tree.token_locs[copy_end_token_index].start;156 .container_field_align => return renderContainerField(gpa, ais, tree, tree.containerFieldAlign(decl), space),
153 try copyFixingWhitespace(ais, tree.source[start..end]);157 .container_field => return renderContainerField(gpa, ais, tree, tree.containerField(decl), space),
154 }158 .@"comptime" => return renderExpression(gpa, ais, tree, decl, space),
155159
156 try renderTopLevelDecl(allocator, ais, tree, decl);160 .root => unreachable,
157 decl_i += 1;161 else => unreachable,
158 if (decl_i >= root_decls.len) return;
159 try renderExtraNewline(tree, ais, root_decls[decl_i]);
160 }162 }
161}163}
162164
163fn renderExtraNewline(tree: *ast.Tree, ais: anytype, node: *ast.Node) @TypeOf(ais.*).Error!void {165/// Render all expressions in the slice, keeping empty lines where appropriate
164 return renderExtraNewlineToken(tree, ais, node.firstToken());166fn renderExpressions(gpa: *Allocator, ais: *Ais, tree: ast.Tree, expressions: []const ast.Node.Index, space: Space) Error!void {
165}167 if (expressions.len == 0) return;
166168 try renderExpression(gpa, ais, tree, expressions[0], space);
167fn renderExtraNewlineToken(169 for (expressions[1..]) |expression| {
168 tree: *ast.Tree,170 try renderExtraNewline(ais, tree, expression);
169 ais: anytype,171 try renderExpression(gpa, ais, tree, expression, space);
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();
185 }172 }
186}173}
187174
188fn renderTopLevelDecl(allocator: *mem.Allocator, ais: anytype, tree: *ast.Tree, decl: *ast.Node) (@TypeOf(ais.*).Error || Error)!void {175fn renderExpression(gpa: *Allocator, ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Space) Error!void {
189 try renderContainerDecl(allocator, ais, tree, decl, .Newline);176 const token_tags = tree.tokens.items(.tag);
190}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 {198 var i = datas[node].lhs;
193 switch (decl.tag) {199 while (i <= datas[node].rhs) : (i += 1) try renderToken(ais, tree, i, .newline);
194 .FnProto => {
195 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
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| {203 switch (space) {
200 try renderExpression(allocator, ais, tree, decl, .Space);204 .none, .space, .newline, .skip => {},
201 try renderExpression(allocator, ais, tree, body_node, space);205 .semicolon => if (token_tags[i] == .semicolon) try renderToken(ais, tree, i, .newline),
202 } else {206 .comma => if (token_tags[i] == .comma) try renderToken(ais, tree, i, .newline),
203 try renderExpression(allocator, ais, tree, decl, .None);207 .comma_space => if (token_tags[i] == .comma) try renderToken(ais, tree, i, .space),
204 try renderToken(tree, ais, tree.nextToken(decl.lastToken()), space);
205 }208 }
206 },209 },
207210
208 .Use => {211 .error_value => {
209 const use_decl = @fieldParentPtr(ast.Node.Use, "base", decl);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| {219 .block_two,
212 try renderToken(tree, ais, visib_token, .Space); // pub220 .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);
213 }229 }
214 try renderToken(tree, ais, use_decl.use_token, .Space); // usingnamespace230 },
215 try renderExpression(allocator, ais, tree, use_decl.expr, .None);231 .block,
216 try renderToken(tree, ais, use_decl.semicolon_token, space); // ;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);
217 },236 },
218237
219 .VarDecl => {238 .@"errdefer" => {
220 const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", decl);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());243 try renderToken(ais, tree, defer_token, .space);
223 try renderVarDecl(allocator, ais, tree, var_decl);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);
224 },250 },
225251
226 .TestDecl => {252 .@"defer" => {
227 const test_decl = @fieldParentPtr(ast.Node.TestDecl, "base", decl);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);265 .@"suspend" => {
230 try renderToken(tree, ais, test_decl.test_token, .Space);266 const suspend_token = main_tokens[node];
231 if (test_decl.name) |name|267 const body = datas[node].lhs;
232 try renderExpression(allocator, ais, tree, name, .Space);268 if (body != 0) {
233 try renderExpression(allocator, ais, tree, test_decl.body_node, space);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 }
234 },274 },
235275
236 .ContainerField => {276 .@"catch" => {
237 const field = @fieldParentPtr(ast.Node.ContainerField, "base", decl);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);280 const same_line = tree.tokensOnSameLine(main_token, fallback_first);
240 if (field.comptime_token) |t| {281 const after_op_space = if (same_line) Space.space else Space.newline;
241 try renderToken(tree, ais, t, .Space); // comptime
242 }
243282
244 const src_has_trailing_comma = blk: {283 try renderExpression(gpa, ais, tree, datas[node].lhs, .space); // target
245 const maybe_comma = tree.nextToken(field.lastToken());
246 break :blk tree.token_ids[maybe_comma] == .Comma;
247 };
248284
249 // The trailing comma is emitted at the end, but if it's not present285 if (token_tags[fallback_first - 1] == .pipe) {
250 // we still have to respect the specified `space` parameter286 try renderToken(ais, tree, main_token, .space); // catch keyword
251 const last_token_space: Space = if (src_has_trailing_comma) .None else space;287 try renderToken(ais, tree, main_token + 1, .none); // pipe
252288 try renderToken(ais, tree, main_token + 2, .none); // payload identifier
253 if (field.type_expr == null and field.value_expr == null) {289 try renderToken(ais, tree, main_token + 3, after_op_space); // pipe
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
275 } else {290 } else {
276 try renderToken(tree, ais, field.name_token, .None); // name291 assert(token_tags[fallback_first - 1] == .keyword_catch);
277 try renderToken(tree, ais, tree.nextToken(field.name_token), .Space); // :292 try renderToken(ais, tree, main_token, after_op_space); // catch keyword
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
293 }293 }
294294
295 if (src_has_trailing_comma) {295 ais.pushIndentOneShot();
296 const comma = tree.nextToken(field.lastToken());296 try renderExpression(gpa, ais, tree, datas[node].rhs, space); // fallback
297 try renderToken(tree, ais, comma, space);
298 }
299 },297 },
300298
301 .Comptime => {299 .field_access => {
302 assert(!decl.requireSemiColon());300 const main_token = main_tokens[node];
303 try renderExpression(allocator, ais, tree, decl, space);301 const field_access = datas[node];
304 },
305302
306 .DocComment => {303 try renderExpression(gpa, ais, tree, field_access.lhs, .none);
307 const comment = @fieldParentPtr(ast.Node.DocComment, "base", decl);304
308 const kind = tree.token_ids[comment.first_line];305 // Allow a line break between the lhs and the dot if the lhs and rhs
309 try renderToken(tree, ais, comment.first_line, .Newline);306 // are on different lines.
310 var tok_i = comment.first_line + 1;307 const lhs_last_token = tree.lastToken(field_access.lhs);
311 while (true) : (tok_i += 1) {308 const same_line = tree.tokensOnSameLine(lhs_last_token, main_token + 1);
312 const tok_id = tree.token_ids[tok_i];309 if (!same_line) {
313 if (tok_id == kind) {310 if (!hasComment(tree, lhs_last_token, main_token)) try ais.insertNewline();
314 try renderToken(tree, ais, tok_i, .Newline);311 ais.pushIndentOneShot();
315 } else if (tok_id == .LineComment) {
316 continue;
317 } else {
318 break;
319 }
320 }312 }
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);
321 },326 },
322 else => unreachable,
323 }
324}
325327
326fn renderExpression(328 .error_union,
327 allocator: *mem.Allocator,329 .switch_range,
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,
344 => {330 => {
345 const casted_node = base.cast(ast.Node.OneToken).?;331 const infix = datas[node];
346 return renderToken(tree, ais, casted_node.token, space);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);
347 },335 },
348336
349 .AnyType => {337 .add,
350 const any_type = base.castTag(.AnyType).?;338 .add_wrap,
351 if (mem.eql(u8, tree.tokenSlice(any_type.token), "var")) {339 .array_cat,
352 // TODO remove in next release cycle340 .array_mult,
353 try ais.writer().writeAll("anytype");341 .assign,
354 if (space == .Comma) try ais.writer().writeAll(",\n");342 .assign_bit_and,
355 return;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();
356 }386 }
357 return renderToken(tree, ais, any_type.token, space);387 ais.pushIndentOneShot();
388 return renderExpression(gpa, ais, tree, infix.rhs, space);
358 },389 },
359390
360 .Block, .LabeledBlock => {391 .bit_not,
361 const block: struct {392 .bool_not,
362 label: ?ast.TokenIndex,393 .negation,
363 statements: []*ast.Node,394 .negation_wrap,
364 lbrace: ast.TokenIndex,395 .optional_type,
365 rbrace: ast.TokenIndex,396 .address_of,
366 } = b: {397 => {
367 if (base.castTag(.Block)) |block| {398 try renderToken(ais, tree, main_tokens[node], .none);
368 break :b .{399 return renderExpression(gpa, ais, tree, datas[node].lhs, space);
369 .label = null,400 },
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 };
385401
386 if (block.label) |label| {402 .@"try",
387 try renderToken(tree, ais, label, Space.None);403 .@"resume",
388 try renderToken(tree, ais, tree.nextToken(label), Space.Space);404 .@"await",
389 }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) {410 .array_type => return renderArrayType(gpa, ais, tree, tree.arrayType(node), space),
392 ais.pushIndentNextLine();411 .array_type_sentinel => return renderArrayType(gpa, ais, tree, tree.arrayTypeSentinel(node), space),
393 defer ais.popIndent();
394 try renderToken(tree, ais, block.lbrace, Space.None);
395 } else {
396 ais.pushIndentNextLine();
397 defer ais.popIndent();
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| {418 .array_init_one, .array_init_one_comma => {
402 try renderStatement(allocator, ais, tree, statement);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) {453 .call,
405 try renderExtraNewline(tree, ais, block.statements[i + 1]);454 .call_comma,
406 }455 .async_call,
407 }456 .async_call_comma,
408 }457 => return renderCall(gpa, ais, tree, tree.callFull(node), space),
409 return renderToken(tree, ais, block.rbrace, 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); // ]
410 },471 },
411472
412 .Defer => {473 .slice_open => return renderSlice(gpa, ais, tree, tree.sliceOpen(node), space),
413 const defer_node = @fieldParentPtr(ast.Node.Defer, "base", base);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);477 .deref => {
416 if (defer_node.payload) |payload| {478 try renderExpression(gpa, ais, tree, datas[node].lhs, .none);
417 try renderExpression(allocator, ais, tree, payload, Space.Space);479 return renderToken(ais, tree, main_tokens[node], space);
418 }
419 return renderExpression(allocator, ais, tree, defer_node.expr, space);
420 },480 },
421 .Comptime => {
422 const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", base);
423481
424 try renderToken(tree, ais, comptime_node.comptime_token, Space.Space);482 .unwrap_optional => {
425 return renderExpression(allocator, ais, tree, comptime_node.expr, space);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);
426 },486 },
427 .Nosuspend => {487
428 const nosuspend_node = @fieldParentPtr(ast.Node.Nosuspend, "base", base);488 .@"break" => {
429 if (mem.eql(u8, tree.tokenSlice(nosuspend_node.nosuspend_token), "noasync")) {489 const main_token = main_tokens[node];
430 // TODO: remove this490 const label_token = datas[node].lhs;
431 try ais.writer().writeAll("nosuspend ");491 const target = datas[node].rhs;
432 } else {492 if (label_token == 0 and target == 0) {
433 try renderToken(tree, ais, nosuspend_node.nosuspend_token, Space.Space);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);
434 }506 }
435 return renderExpression(allocator, ais, tree, nosuspend_node.expr, space);
436 },507 },
437508
438 .Suspend => {509 .@"continue" => {
439 const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base);510 const main_token = main_tokens[node];
440511 const label = datas[node].lhs;
441 if (suspend_node.body) |body| {512 if (label != 0) {
442 try renderToken(tree, ais, suspend_node.suspend_token, Space.Space);513 try renderToken(ais, tree, main_token, .space); // continue
443 return renderExpression(allocator, ais, tree, body, space);514 try renderToken(ais, tree, label - 1, .none); // :
515 return renderToken(ais, tree, label, space); // label
444 } else {516 } else {
445 return renderToken(tree, ais, suspend_node.suspend_token, space);517 return renderToken(ais, tree, main_token, space); // continue
446 }518 }
447 },519 },
448520
449 .Catch => {521 .@"return" => {
450 const infix_op_node = @fieldParentPtr(ast.Node.Catch, "base", base);522 if (datas[node].lhs != 0) {
451523 try renderToken(ais, tree, main_tokens[node], .space);
452 const op_space = Space.Space;524 try renderExpression(gpa, ais, tree, datas[node].lhs, space);
453 try renderExpression(allocator, ais, tree, infix_op_node.lhs, op_space);525 } else {
454526 try renderToken(ais, tree, main_tokens[node], space);
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);
464 }527 }
528 },
465529
530 .grouped_expression => {
531 try renderToken(ais, tree, main_tokens[node], .none); // lparen
466 ais.pushIndentOneShot();532 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
468 },535 },
469536
470 .Add,537 .container_decl,
471 .AddWrap,538 .container_decl_trailing,
472 .ArrayCat,539 => return renderContainerDecl(gpa, ais, tree, node, tree.containerDecl(node), space),
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);
514540
515 const op_space = switch (base.tag) {541 .container_decl_two, .container_decl_two_trailing => {
516 .Period, .ErrorUnion, .Range => Space.None,542 var buffer: [2]ast.Node.Index = undefined;
517 else => Space.Space,543 return renderContainerDecl(gpa, ais, tree, node, tree.containerDeclTwo(&buffer, node), space);
518 };544 },
519 try renderExpression(allocator, ais, tree, infix_op_node.lhs, op_space);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: {549 .tagged_union,
522 const loc = tree.tokenLocation(tree.token_locs[infix_op_node.op_token].end, tree.nextToken(infix_op_node.op_token));550 .tagged_union_trailing,
523 break :blk if (loc.line == 0) op_space else Space.Newline;551 => return renderContainerDecl(gpa, ais, tree, node, tree.taggedUnion(node), space),
524 };
525552
526 {553 .tagged_union_two, .tagged_union_two_trailing => {
527 ais.pushIndent();554 var buffer: [2]ast.Node.Index = undefined;
528 defer ais.popIndent();555 return renderContainerDecl(gpa, ais, tree, node, tree.taggedUnionTwo(&buffer, node), space);
529 try renderToken(tree, ais, infix_op_node.op_token, after_op_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);
530 }607 }
531 ais.pushIndentOneShot();
532 return renderExpression(allocator, ais, tree, infix_op_node.rhs, space);
533 },608 },
534609
535 .BitNot,610 .builtin_call_two, .builtin_call_two_comma => {
536 .BoolNot,611 if (datas[node].lhs == 0) {
537 .Negation,612 return renderBuiltinCall(gpa, ais, tree, main_tokens[node], &.{}, space);
538 .NegationWrap,613 } else if (datas[node].rhs == 0) {
539 .OptionalType,614 return renderBuiltinCall(gpa, ais, tree, main_tokens[node], &.{datas[node].lhs}, space);
540 .AddressOf,615 } else {
541 => {616 return renderBuiltinCall(gpa, ais, tree, main_tokens[node], &.{ datas[node].lhs, datas[node].rhs }, space);
542 const casted_node = @fieldParentPtr(ast.Node.SimplePrefixOp, "base", base);617 }
543 try renderToken(tree, ais, casted_node.op_token, Space.None);
544 return renderExpression(allocator, ais, tree, casted_node.rhs, space);
545 },618 },
546619 .builtin_call, .builtin_call_comma => {
547 .Try,620 const params = tree.extra_data[datas[node].lhs..datas[node].rhs];
548 .Resume,621 return renderBuiltinCall(gpa, ais, tree, main_tokens[node], params, space);
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);
554 },622 },
555623
556 .ArrayType => {624 .fn_proto_simple => {
557 const array_type = @fieldParentPtr(ast.Node.ArrayType, "base", base);625 var params: [1]ast.Node.Index = undefined;
558 return renderArrayType(626 return renderFnProto(gpa, ais, tree, tree.fnProtoSimple(&params, node), space);
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 );
568 },627 },
569 .ArrayTypeSentinel => {628 .fn_proto_multi => return renderFnProto(gpa, ais, tree, tree.fnProtoMulti(node), space),
570 const array_type = @fieldParentPtr(ast.Node.ArrayTypeSentinel, "base", base);629 .fn_proto_one => {
571 return renderArrayType(630 var params: [1]ast.Node.Index = undefined;
572 allocator,631 return renderFnProto(gpa, ais, tree, tree.fnProtoOne(&params, node), space);
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 );
581 },632 },
582633 .fn_proto => return renderFnProto(gpa, ais, tree, tree.fnProto(node), space),
583 .PtrType => {634
584 const ptr_type = @fieldParentPtr(ast.Node.PtrType, "base", base);635 .anyframe_type => {
585 const op_tok_id = tree.token_ids[ptr_type.op_token];636 const main_token = main_tokens[node];
586 switch (op_tok_id) {637 if (datas[node].rhs != 0) {
587 .Asterisk, .AsteriskAsterisk => try ais.writer().writeByte('*'),638 try renderToken(ais, tree, main_token, .none); // anyframe
588 .LBracket => if (tree.token_ids[ptr_type.op_token + 2] == .Identifier)639 try renderToken(ais, tree, main_token + 1, .none); // ->
589 try ais.writer().writeAll("[*c")640 return renderExpression(gpa, ais, tree, datas[node].rhs, space);
590 else641 } else {
591 try ais.writer().writeAll("[*"),642 return renderToken(ais, tree, main_token, space); // anyframe
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
641 }643 }
642 return renderExpression(allocator, ais, tree, ptr_type.rhs, space);
643 },644 },
644645
645 .SliceType => {646 .@"switch",
646 const slice_type = @fieldParentPtr(ast.Node.SliceType, "base", base);647 .switch_comma,
647 try renderToken(tree, ais, slice_type.op_token, Space.None); // [648 => {
648 if (slice_type.ptr_info.sentinel) |sentinel| {649 const switch_token = main_tokens[node];
649 const colon_token = tree.prevToken(sentinel.firstToken());650 const condition = datas[node].lhs;
650 try renderToken(tree, ais, colon_token, Space.None); // :651 const extra = tree.extraData(datas[node].rhs, ast.Node.SubRange);
651 try renderExpression(allocator, ais, tree, sentinel, Space.None);652 const cases = tree.extra_data[extra.start..extra.end];
652 try renderToken(tree, ais, tree.nextToken(sentinel.lastToken()), Space.None); // ]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
653 } else {663 } 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);
655 }666 }
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| {671 .switch_case_one => return renderSwitchCase(gpa, ais, tree, tree.switchCaseOne(node), space),
658 try renderToken(tree, ais, allowzero_token, Space.Space); // allowzero672 .switch_case => return renderSwitchCase(gpa, ais, tree, tree.switchCase(node), space),
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);
663673
664 try renderToken(tree, ais, align_token, Space.None); // align674 .while_simple => return renderWhile(gpa, ais, tree, tree.whileSimple(node), space),
665 try renderToken(tree, ais, lparen_token, Space.None); // (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| {683 .asm_simple => return renderAsm(gpa, ais, tree, tree.asmSimple(node), space),
670 const colon1 = tree.prevToken(bit_range.start.firstToken());684 .@"asm" => return renderAsm(gpa, ais, tree, tree.asmFull(node), space),
671 const colon2 = tree.prevToken(bit_range.end.firstToken());
672685
673 try renderToken(tree, ais, colon1, Space.None); // :686 .enum_literal => {
674 try renderExpression(allocator, ais, tree, bit_range.start, Space.None);687 try renderToken(ais, tree, main_tokens[node] - 1, .none); // .
675 try renderToken(tree, ais, colon2, Space.None); // :688 return renderToken(ais, tree, main_tokens[node], space); // name
676 try renderExpression(allocator, ais, tree, bit_range.end, Space.None);689 },
677690
678 const rparen_token = tree.nextToken(bit_range.end.lastToken());691 .fn_decl => unreachable,
679 try renderToken(tree, ais, rparen_token, Space.Space); // )692 .container_field => unreachable,
680 } else {693 .container_field_init => unreachable,
681 const rparen_token = tree.nextToken(align_info.node.lastToken());694 .container_field_align => unreachable,
682 try renderToken(tree, ais, rparen_token, Space.Space); // )695 .root => unreachable,
683 }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);
684 }747 }
685 if (slice_type.ptr_info.const_token) |const_token| {748 try renderToken(ais, tree, ptr_type.ast.main_token, .none); // asterisk
686 try renderToken(tree, ais, const_token, Space.Space);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
687 }761 }
688 if (slice_type.ptr_info.volatile_token) |volatile_token| {762 },
689 try renderToken(tree, ais, volatile_token, Space.Space);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
690 }778 }
691 return renderExpression(allocator, ais, tree, slice_type.rhs, space);
692 },779 },
780 }
693781
694 .ArrayInitializer, .ArrayInitializerDot => {782 if (ptr_type.allowzero_token) |allowzero_token| {
695 var rtoken: ast.TokenIndex = undefined;783 try renderToken(ais, tree, allowzero_token, .space);
696 var exprs: []*ast.Node = undefined;784 }
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 };
717785
718 switch (lhs) {786 if (ptr_type.ast.align_node != 0) {
719 .dot => |dot| try renderToken(tree, ais, dot, Space.None),787 const align_first = tree.firstToken(ptr_type.ast.align_node);
720 .node => |node| try renderExpression(allocator, ais, tree, node, Space.None),788 try renderToken(ais, tree, align_first - 2, .none); // align
721 }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) {803 if (ptr_type.const_token) |const_token| {
724 try renderToken(tree, ais, lbrace, Space.None);804 try renderToken(ais, tree, const_token, .space);
725 return renderToken(tree, ais, rtoken, space);805 }
726 }
727806
728 if (exprs.len == 1 and exprs[0].tag != .MultilineStringLiteral and tree.token_ids[exprs[0].*.lastToken() + 1] == .RBrace) {807 if (ptr_type.volatile_token) |volatile_token| {
729 const expr = exprs[0];808 try renderToken(ais, tree, volatile_token, .space);
809 }
730810
731 try renderToken(tree, ais, lbrace, Space.None);811 try renderExpression(gpa, ais, tree, ptr_type.ast.child_type, space);
732 try renderExpression(allocator, ais, tree, expr, Space.None);812}
733 return renderToken(tree, ais, rtoken, space);
734 }
735813
736 // scan to find row size814fn renderSlice(
737 if (rowSize(tree, exprs, rtoken) != null) {815 gpa: *Allocator,
738 {816 ais: *Ais,
739 ais.pushIndentNextLine();817 tree: ast.Tree,
740 defer ais.popIndent();818 slice: ast.full.Slice,
741 try renderToken(tree, ais, lbrace, Space.Newline);819 space: Space,
742820) Error!void {
743 var expr_index: usize = 0;821 const node_tags = tree.nodes.items(.tag);
744 while (rowSize(tree, exprs[expr_index..], rtoken)) |row_size| {822 const after_start_space_bool = nodeCausesSliceOpSpace(node_tags[slice.ast.start]) or
745 const row_exprs = exprs[expr_index..];823 if (slice.ast.end != 0) nodeCausesSliceOpSpace(node_tags[slice.ast.end]) else false;
746 // A place to store the width of each expression and its column's maximum824 const after_start_space = if (after_start_space_bool) Space.space else Space.none;
747 var widths = try allocator.alloc(usize, row_exprs.len + row_size);825 const after_dots_space = if (slice.ast.end != 0) after_start_space else Space.none;
748 defer allocator.free(widths);826
749 mem.set(usize, widths, 0);827 try renderExpression(gpa, ais, tree, slice.ast.sliced, .none);
750828 try renderToken(ais, tree, slice.ast.lbracket, .none); // lbracket
751 var expr_newlines = try allocator.alloc(bool, row_exprs.len);829
752 defer allocator.free(expr_newlines);830 const start_last = tree.lastToken(slice.ast.start);
753 mem.set(bool, expr_newlines, false);831 try renderExpression(gpa, ais, tree, slice.ast.start, after_start_space);
754832 try renderToken(ais, tree, start_last + 1, after_dots_space); // ellipsis2 ("..")
755 var expr_widths = widths[0 .. widths.len - row_size];833 if (slice.ast.end == 0) {
756 var column_widths = widths[widths.len - row_size ..];834 return renderToken(ais, tree, start_last + 2, space); // rbracket
757835 }
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 }
839836
840 // Render exprs in current section837 const end_last = tree.lastToken(slice.ast.end);
841 column_counter = 0;838 const after_end_space = if (slice.ast.sentinel != 0) Space.space else Space.none;
842 var last_col_index: usize = row_size - 1;839 try renderExpression(gpa, ais, tree, slice.ast.end, after_end_space);
843 for (section_exprs) |expr, i| {840 if (slice.ast.sentinel == 0) {
844 if (i + 1 < section_exprs.len) {841 return renderToken(ais, tree, end_last + 1, space); // rbracket
845 const next_expr = section_exprs[i + 1];842 }
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 }
880843
881 if (expr_index == exprs.len) {844 try renderToken(ais, tree, end_last + 1, .none); // colon
882 break;845 try renderExpression(gpa, ais, tree, slice.ast.sentinel, .none);
883 }846 try renderToken(ais, tree, tree.lastToken(slice.ast.sentinel) + 1, space); // rbracket
884 }847}
885 }
886848
887 return renderToken(tree, ais, rtoken, space);849fn renderAsmOutput(
888 }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 line879fn renderAsmInput(
891 try renderToken(tree, ais, lbrace, Space.Space);880 gpa: *Allocator,
892 for (exprs) |expr, i| {881 ais: *Ais,
893 if (i + 1 < exprs.len) {882 tree: ast.Tree,
894 const next_expr = exprs[i + 1];883 asm_input: ast.Node.Index,
895 try renderExpression(allocator, ais, tree, expr, Space.None);884 space: Space,
896 const comma = tree.nextToken(expr.*.lastToken());885) Error!void {
897 try renderToken(tree, ais, comma, Space.Space); // ,886 const node_tags = tree.nodes.items(.tag);
898 } else {887 const main_tokens = tree.nodes.items(.main_token);
899 try renderExpression(allocator, ais, tree, expr, Space.Space);888 const datas = tree.nodes.items(.data);
900 }889 assert(node_tags[asm_input] == .asm_input);
901 }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);901fn renderVarDecl(gpa: *Allocator, ais: *Ais, tree: ast.Tree, var_decl: ast.full.VarDecl) Error!void {
904 },902 if (var_decl.visib_token) |visib_token| {
903 try renderToken(ais, tree, visib_token, Space.space); // pub
904 }
905905
906 .StructInitializer, .StructInitializerDot => {906 if (var_decl.extern_export_token) |extern_export_token| {
907 var rtoken: ast.TokenIndex = undefined;907 try renderToken(ais, tree, extern_export_token, Space.space); // extern
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 };
924908
925 const lbrace = switch (lhs) {909 if (var_decl.lib_name) |lib_name| {
926 .dot => |dot| tree.nextToken(dot),910 try renderToken(ais, tree, lib_name, Space.space); // "lib"
927 .node => |node| tree.nextToken(node.lastToken()),911 }
928 };912 }
929913
930 if (field_inits.len == 0) {914 if (var_decl.threadlocal_token) |thread_local_token| {
931 switch (lhs) {915 try renderToken(ais, tree, thread_local_token, Space.space); // threadlocal
932 .dot => |dot| try renderToken(tree, ais, dot, Space.None),916 }
933 .node => |node| try renderExpression(allocator, ais, tree, node, Space.None),
934 }
935917
936 {918 if (var_decl.comptime_token) |comptime_token| {
937 ais.pushIndentNextLine();919 try renderToken(ais, tree, comptime_token, Space.space); // comptime
938 defer ais.popIndent();920 }
939 try renderToken(tree, ais, lbrace, Space.None);
940 }
941921
942 return renderToken(tree, ais, rtoken, space);922 try renderToken(ais, tree, var_decl.ast.mut_token, .space); // var
943 }
944923
945 const src_has_trailing_comma = blk: {924 const name_space = if (var_decl.ast.type_node == 0 and
946 const maybe_comma = tree.prevToken(rtoken);925 (var_decl.ast.align_node != 0 or
947 break :blk tree.token_ids[maybe_comma] == .Comma;926 var_decl.ast.section_node != 0 or
948 };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: {933 if (var_decl.ast.type_node != 0) {
951 const loc = tree.tokenLocation(tree.token_locs[lbrace].end, rtoken);934 try renderToken(ais, tree, var_decl.ast.mut_token + 2, Space.space); // :
952 break :blk loc.line == 0;935 if (var_decl.ast.align_node != 0 or var_decl.ast.section_node != 0 or
953 };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: {946 if (var_decl.ast.align_node != 0) {
956 // render field expressions until a LF is found947 const lparen = tree.firstToken(var_decl.ast.align_node) - 1;
957 for (field_inits) |field_init| {948 const align_kw = lparen - 1;
958 var find_stream = std.io.findByteWriter('\n', std.io.null_writer);949 const rparen = tree.lastToken(var_decl.ast.align_node) + 1;
959 var auto_indenting_stream = std.io.autoIndentingStream(indent_delta, find_stream.writer());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);961 if (var_decl.ast.section_node != 0) {
962 if (find_stream.byte_found) break :blk false;962 const lparen = tree.firstToken(var_decl.ast.section_node) - 1;
963 }963 const section_kw = lparen - 1;
964 break :blk true;964 const rparen = tree.lastToken(var_decl.ast.section_node) + 1;
965 };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: {976 assert(var_decl.ast.init_node != 0);
968 if (field_inits[0].cast(ast.Node.FieldInitializer)) |field_init| {977 const eq_token = tree.firstToken(var_decl.ast.init_node) - 1;
969 switch (field_init.expr.tag) {978 const eq_space: Space = if (tree.tokensOnSameLine(eq_token, eq_token + 1)) .space else .newline;
970 .StructInitializer,979 {
971 .StructInitializerDot,980 ais.pushIndent();
972 => break :blk,981 try renderToken(ais, tree, eq_token, eq_space); // =
973 else => {},982 ais.popIndent();
974 }983 }
975 }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 multiline988fn renderIf(gpa: *Allocator, ais: *Ais, tree: ast.Tree, if_node: ast.full.If, space: Space) Error!void {
978 if (!expr_outputs_one_line or src_has_trailing_comma) {989 return renderWhile(gpa, ais, tree, .{
979 break :blk;990 .ast = .{
980 }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) {1005/// Note that this function is additionally used to render if and for expressions, with
983 .dot => |dot| try renderToken(tree, ais, dot, Space.None),1006/// respective values set to null.
984 .node => |node| try renderExpression(allocator, ais, tree, node, Space.None),1007fn renderWhile(gpa: *Allocator, ais: *Ais, tree: ast.Tree, while_node: ast.full.While, space: Space) Error!void {
985 }1008 const node_tags = tree.nodes.items(.tag);
986 try renderToken(tree, ais, lbrace, Space.Space);1009 const token_tags = tree.tokens.items(.tag);
987 try renderExpression(allocator, ais, tree, field_inits[0], Space.Space);
988 return renderToken(tree, ais, rtoken, space);
989 }
9901010
991 if (!src_has_trailing_comma and src_same_line and expr_outputs_one_line) {1011 if (while_node.label_token) |label| {
992 // render all on one line, no trailing comma1012 try renderToken(ais, tree, label, .none); // label
993 switch (lhs) {1013 try renderToken(ais, tree, label + 1, .space); // :
994 .dot => |dot| try renderToken(tree, ais, dot, Space.None),1014 }
995 .node => |node| try renderExpression(allocator, ais, tree, node, Space.None),
996 }
997 try renderToken(tree, ais, lbrace, Space.Space);
9981015
999 for (field_inits) |field_init, i| {1016 if (while_node.inline_token) |inline_token| {
1000 if (i + 1 < field_inits.len) {1017 try renderToken(ais, tree, inline_token, .space); // inline
1001 try renderExpression(allocator, ais, tree, field_init, Space.None);1018 }
10021019
1003 const comma = tree.nextToken(field_init.lastToken());1020 try renderToken(ais, tree, while_node.ast.while_token, .space); // if
1004 try renderToken(tree, ais, comma, Space.Space);1021 try renderToken(ais, tree, while_node.ast.while_token + 1, .none); // lparen
1005 } else {1022 try renderExpression(gpa, ais, tree, while_node.ast.cond_expr, .none); // condition
1006 try renderExpression(allocator, ais, tree, field_init, Space.Space);1023
1007 }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;
1008 }1035 }
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;
1060 };1036 };
10611037 try renderToken(ais, tree, ident, .none); // identifier
1062 if (src_has_trailing_comma) {1038 const pipe = blk: {
1063 {1039 if (token_tags[ident + 1] == .comma) {
1064 ais.pushIndent();1040 try renderToken(ais, tree, ident + 1, .space); // ,
1065 defer ais.popIndent();1041 try renderToken(ais, tree, ident + 2, .none); // index
10661042 break :blk ident + 3;
1067 try renderToken(tree, ais, lparen, Space.Newline); // (1043 } else {
1068 const params = call.params();1044 break :blk ident + 1;
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); // {
1324 }1045 }
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;
1335 };1046 };
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();1083 const rparen = tree.lastToken(while_node.ast.cond_expr) + 1;
13381084 const last_then_token = tree.lastToken(while_node.ast.then_expr);
1339 // Check if the first declaration and the { are on the same line1085 const src_has_newline = !tree.tokensOnSameLine(rparen, last_then_token);
1340 const src_has_newline = !tree.tokensOnSameLine(1086
1341 container_decl.lbrace_token,1087 if (src_has_newline) {
1342 fields_and_decls[0].firstToken(),1088 if (while_node.payload_token) |payload_token| {
1343 );1089 try renderToken(ais, tree, payload_token - 2, .space); // rparen
13441090 try renderToken(ais, tree, payload_token - 1, .none); // |
1345 // We can only print all the elements in-line if all the1091 const ident = blk: {
1346 // declarations inside are fields1092 if (token_tags[payload_token] == .asterisk) {
1347 const src_has_only_fields = blk: {1093 try renderToken(ais, tree, payload_token, .none); // *
1348 for (fields_and_decls) |decl| {1094 break :blk payload_token + 1;
1349 if (decl.tag != .ContainerField) break :blk false;1095 } else {
1096 break :blk payload_token;
1350 }1097 }
1351 break :blk true;
1352 };1098 };
13531099 try renderToken(ais, tree, ident, .none); // identifier
1354 if (src_has_trailing_comma or !src_has_only_fields) {1100 const pipe = blk: {
1355 // One declaration per line1101 if (token_tags[ident + 1] == .comma) {
1356 ais.pushIndentNextLine();1102 try renderToken(ais, tree, ident + 1, .space); // ,
1357 defer ais.popIndent();1103 try renderToken(ais, tree, ident + 2, .none); // index
1358 try renderToken(tree, ais, container_decl.lbrace_token, .Newline); // {1104 break :blk ident + 3;
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;
1409 } else {1105 } else {
1410 break :blk;1106 break :blk ident + 1;
1411 }1107 }
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;
1424 };1108 };
14251109 const after_space: Space = if (while_node.ast.cont_expr != 0) .space else .newline;
1426 if (src_has_trailing_comma) {1110 try renderToken(ais, tree, pipe, after_space); // |
1427 {1111 } else {
1428 ais.pushIndent();1112 ais.pushIndent();
1429 defer ais.popIndent();1113 const after_space: Space = if (while_node.ast.cont_expr != 0) .space else .newline;
14301114 try renderToken(ais, tree, rparen, after_space); // rparen
1431 try renderToken(tree, ais, lbrace, Space.Newline); // {1115 ais.popIndent();
1432 const decls = err_set_decl.decls();1116 }
1433 for (decls) |node, i| {1117 if (while_node.ast.cont_expr != 0) {
1434 if (i + 1 < decls.len) {1118 const cont_rparen = tree.lastToken(while_node.ast.cont_expr) + 1;
1435 try renderExpression(allocator, ais, tree, node, Space.None);1119 const cont_lparen = tree.firstToken(while_node.ast.cont_expr) - 1;
1436 try renderToken(tree, ais, tree.nextToken(node.lastToken()), Space.Newline); // ,1120 try renderToken(ais, tree, cont_lparen - 1, .space); // :
14371121 try renderToken(ais, tree, cont_lparen, .none); // lparen
1438 try renderExtraNewline(tree, ais, decls[i + 1]);1122 try renderExpression(gpa, ais, tree, while_node.ast.cont_expr, .none);
1439 } else {1123 try renderToken(ais, tree, cont_rparen, .newline); // rparen
1440 try renderExpression(allocator, ais, tree, node, Space.Comma);1124 }
1441 }1125 if (while_node.ast.else_expr != 0) {
1442 }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); // |
1443 }1136 }
14441137 return renderExpression(gpa, ais, tree, while_node.ast.else_expr, space);
1445 return renderToken(tree, ais, err_set_decl.rbrace_token, space); // }
1446 } else {1138 } else {
1447 try renderToken(tree, ais, lbrace, Space.Space); // {1139 if (while_node.error_token) |error_token| {
14481140 try renderToken(ais, tree, while_node.else_token, .space); // else
1449 const decls = err_set_decl.decls();1141 try renderToken(ais, tree, error_token - 1, .none); // |
1450 for (decls) |node, i| {1142 try renderToken(ais, tree, error_token, .none); // identifier
1451 if (i + 1 < decls.len) {1143 try renderToken(ais, tree, error_token + 1, .space); // |
1452 try renderExpression(allocator, ais, tree, node, Space.None);1144 } else {
14531145 try renderToken(ais, tree, while_node.else_token, .newline); // else
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 {}");
1505 }1146 }
1147 try renderExpressionIndented(gpa, ais, tree, while_node.ast.else_expr, space);
1148 return;
1506 }1149 }
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); // @name1156 // Render everything on a single line.
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);
15301157
1531 if (i + 1 < params.len) {1158 if (while_node.payload_token) |payload_token| {
1532 const comma_token = tree.nextToken(param_node.lastToken());1159 assert(payload_token - 2 == rparen);
1533 try renderToken(tree, ais, comma_token, Space.Space); // ,1160 try renderToken(ais, tree, payload_token - 2, .space); // )
1534 }1161 try renderToken(ais, tree, payload_token - 1, .none); // |
1535 }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;
1536 } else {1166 } else {
1537 // one param per line1167 break :blk payload_token;
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 }
1545 }1168 }
15461169 };
1547 return renderToken(tree, ais, builtin_call.rparen_token, space); // )1170 try renderToken(ais, tree, ident, .none); // identifier
1548 },1171 const pipe = blk: {
15491172 if (token_tags[ident + 1] == .comma) {
1550 .FnProto => {1173 try renderToken(ais, tree, ident + 1, .space); // ,
1551 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", base);1174 try renderToken(ais, tree, ident + 2, .none); // index
15521175 break :blk ident + 3;
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 }
1616 } else {1176 } else {
1617 // one param per line1177 break :blk ident + 1;
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 }
1628 }1178 }
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); // )1185 if (while_node.ast.cont_expr != 0) {
16311186 const cont_rparen = tree.lastToken(while_node.ast.cont_expr) + 1;
1632 if (fn_proto.getAlignExpr()) |align_expr| {1187 const cont_lparen = tree.firstToken(while_node.ast.cont_expr) - 1;
1633 const align_rparen = tree.nextToken(align_expr.lastToken());1188 try renderToken(ais, tree, cont_lparen - 1, .space); // :
1634 const align_lparen = tree.prevToken(align_expr.firstToken());1189 try renderToken(ais, tree, cont_lparen, .none); // lparen
1635 const align_kw = tree.prevToken(align_lparen);1190 try renderExpression(gpa, ais, tree, while_node.ast.cont_expr, .none);
16361191 try renderToken(ais, tree, cont_rparen, .space); // rparen
1637 try renderToken(tree, ais, align_kw, Space.None); // align1192 }
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 }
16531193
1654 if (fn_proto.getCallconvExpr()) |callconv_expr| {1194 if (while_node.ast.else_expr != 0) {
1655 const callconv_rparen = tree.nextToken(callconv_expr.lastToken());1195 try renderExpression(gpa, ais, tree, while_node.ast.then_expr, .space);
1656 const callconv_lparen = tree.prevToken(callconv_expr.firstToken());1196 try renderToken(ais, tree, while_node.else_token, .space); // else
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 }
16701197
1671 switch (fn_proto.return_type) {1198 if (while_node.error_token) |error_token| {
1672 .Explicit => |node| {1199 try renderToken(ais, tree, error_token - 1, .none); // |
1673 return renderExpression(allocator, ais, tree, node, space);1200 try renderToken(ais, tree, error_token, .none); // identifier
1674 },1201 try renderToken(ais, tree, error_token + 1, .space); // |
1675 .InferErrorSet => |node| {1202 }
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 },
16821203
1683 .AnyFrameType => {1204 return renderExpression(gpa, ais, tree, while_node.ast.else_expr, space);
1684 const anyframe_type = @fieldParentPtr(ast.Node.AnyFrameType, "base", base);1205 } else {
1206 return renderExpression(gpa, ais, tree, while_node.ast.then_expr, space);
1207 }
1208}
16851209
1686 if (anyframe_type.result) |result| {1210fn renderContainerField(
1687 try renderToken(tree, ais, anyframe_type.anyframe_token, Space.None); // anyframe1211 gpa: *Allocator,
1688 try renderToken(tree, ais, result.arrow_token, Space.None); // ->1212 ais: *Ais,
1689 return renderExpression(allocator, ais, tree, result.return_type, space);1213 tree: ast.Tree,
1690 } else {1214 field: ast.full.ContainerField,
1691 return renderToken(tree, ais, anyframe_type.anyframe_token, space); // anyframe1215 space: Space,
1692 }1216) Error!void {
1693 },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 nodes1246 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 => {1264fn renderBuiltinCall(
1698 const switch_node = @fieldParentPtr(ast.Node.Switch, "base", base);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); // switch1274 try renderToken(ais, tree, builtin_token, .none); // @name
1701 try renderToken(tree, ais, tree.nextToken(switch_node.switch_token), Space.None); // (
17021275
1703 const rparen = tree.nextToken(switch_node.expr.lastToken());1276 if (params.len == 0) {
1704 const lbrace = tree.nextToken(rparen);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) {1281 const last_param = params[params.len - 1];
1707 try renderExpression(allocator, ais, tree, switch_node.expr, Space.None);1282 const after_last_param_token = tree.lastToken(last_param) + 1;
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 }
17121283
1713 try renderExpression(allocator, ais, tree, switch_node.expr, Space.None);1284 if (token_tags[after_last_param_token] != .comma) {
1714 try renderToken(tree, ais, rparen, Space.Space); // )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))
1716 {1292 {
1717 ais.pushIndentNextLine();1293 ais.pushIndentOneShot();
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 }
1729 }1294 }
1295 try renderExpression(gpa, ais, tree, param_node, .none);
17301296
1731 return renderToken(tree, ais, switch_node.rbrace, space); // }1297 if (i + 1 < params.len) {
1732 },1298 const comma_token = tree.lastToken(param_node) + 1;
17331299 try renderToken(ais, tree, comma_token, .space); // ,
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 }
1770 }1300 }
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); // =>1313 return renderToken(ais, tree, after_last_param_token + 1, space); // )
17731314 }
1774 if (switch_case.payload) |payload| {1315}
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 }
17971316
1798 if (same_line) {1317fn renderFnProto(gpa: *Allocator, ais: *Ais, tree: ast.Tree, fn_proto: ast.full.FnProto, space: Space) Error!void {
1799 return renderExpression(allocator, ais, tree, else_node.body, space);1318 const token_tags = tree.tokens.items(.tag);
1800 } else {1319 const token_starts = tree.tokens.items(.start);
1801 ais.pushIndent();1320
1802 defer ais.popIndent();1321 const is_inline = fn_proto.ast.fn_token > 0 and
1803 return renderExpression(allocator, ais, tree, else_node.body, space);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;
1804 }1347 }
1805 },1348 }
18061349 if (fn_proto.ast.section_expr != 0) {
1807 .While => {1350 const tok = tree.firstToken(fn_proto.ast.section_expr) - 3;
1808 const while_node = @fieldParentPtr(ast.Node.While, "base", base);1351 const start = token_starts[tok];
18091352 if (start < smallest_start) {
1810 if (while_node.label) |label| {1353 rparen = tok;
1811 try renderToken(tree, ais, label, Space.None); // label1354 smallest_start = start;
1812 try renderToken(tree, ais, tree.nextToken(label), Space.Space); // :
1813 }1355 }
18141356 }
1815 if (while_node.inline_token) |inline_token| {1357 if (fn_proto.ast.callconv_expr != 0) {
1816 try renderToken(tree, ais, inline_token, Space.Space); // inline1358 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;
1817 }1363 }
18181364 }
1819 try renderToken(tree, ais, while_node.while_token, Space.Space); // while1365 break :blk rparen;
1820 try renderToken(tree, ais, tree.nextToken(while_node.while_token), Space.None); // (1366 };
1821 try renderExpression(allocator, ais, tree, while_node.condition, Space.None);1367 assert(token_tags[rparen] == .r_paren);
18221368
1823 const cond_rparen = tree.nextToken(while_node.condition.lastToken());1369 // The params list is a sparse set that does *not* include anytype or ... parameters.
18241370
1825 const body_is_block = nodeIsBlock(while_node.body);1371 const trailing_comma = token_tags[rparen - 1] == .comma;
18261372 if (!trailing_comma and !hasComment(tree, lparen, rparen)) {
1827 var block_start_space: Space = undefined;1373 // Render all on one line, no trailing comma.
1828 var after_body_space: Space = undefined;1374 try renderToken(ais, tree, lparen, .none); // (
18291375
1830 if (body_is_block) {1376 var param_i: usize = 0;
1831 block_start_space = Space.BlockStart;1377 var last_param_token = lparen;
1832 after_body_space = if (while_node.@"else" == null) space else Space.SpaceOrOutdent;1378 while (true) {
1833 } else if (tree.tokensOnSameLine(cond_rparen, while_node.body.lastToken())) {1379 last_param_token += 1;
1834 block_start_space = Space.Space;1380 switch (token_tags[last_param_token]) {
1835 after_body_space = if (while_node.@"else" == null) space else Space.Space;1381 .doc_comment => {
1836 } else {1382 try renderToken(ais, tree, last_param_token, .newline);
1837 block_start_space = Space.Newline;1383 continue;
1838 after_body_space = if (while_node.@"else" == null) space else Space.Newline;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.
1839 }1404 }
18401405 if (token_tags[last_param_token] == .identifier and
1406 token_tags[last_param_token + 1] == .colon)
1841 {1407 {
1842 const rparen_space = if (while_node.payload != null or while_node.continue_expr != null) Space.Space else block_start_space;1408 try renderToken(ais, tree, last_param_token, .none); // name
1843 try renderToken(tree, ais, cond_rparen, rparen_space); // )1409 last_param_token += 1;
1844 }1410 try renderToken(ais, tree, last_param_token, .space); // :
18451411 last_param_token += 1;
1846 if (while_node.payload) |payload| {1412 }
1847 const payload_space = if (while_node.continue_expr != null) Space.Space else block_start_space;1413 if (token_tags[last_param_token] == .keyword_anytype) {
1848 try renderExpression(allocator, ais, tree, payload, payload_space);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,
1849 }1453 }
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| {1475 try renderToken(ais, tree, rparen, .space); // )
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); // (
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); // )1481 try renderToken(ais, tree, align_lparen - 1, .none); // align
1862 }1482 try renderToken(ais, tree, align_lparen, .none); // (
18631483 try renderExpression(gpa, ais, tree, fn_proto.ast.align_expr, .none);
1864 {1484 try renderToken(ais, tree, align_rparen, .space); // )
1865 if (!body_is_block) ais.pushIndent();1485 }
1866 defer if (!body_is_block) ais.popIndent();
1867 try renderExpression(allocator, ais, tree, while_node.body, after_body_space);
1868 }
18691486
1870 if (while_node.@"else") |@"else"| {1487 if (fn_proto.ast.section_expr != 0) {
1871 return renderExpression(allocator, ais, tree, &@"else".base, space);1488 const section_lparen = tree.firstToken(fn_proto.ast.section_expr) - 1;
1872 }1489 const section_rparen = tree.lastToken(fn_proto.ast.section_expr) + 1;
1873 },
18741490
1875 .For => {1491 try renderToken(ais, tree, section_lparen - 1, .none); // section
1876 const for_node = @fieldParentPtr(ast.Node.For, "base", base);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| {1497 if (fn_proto.ast.callconv_expr != 0) {
1879 try renderToken(tree, ais, label, Space.None); // label1498 const callconv_lparen = tree.firstToken(fn_proto.ast.callconv_expr) - 1;
1880 try renderToken(tree, ais, tree.nextToken(label), Space.Space); // :1499 const callconv_rparen = tree.lastToken(fn_proto.ast.callconv_expr) + 1;
1881 }
18821500
1883 if (for_node.inline_token) |inline_token| {1501 try renderToken(ais, tree, callconv_lparen - 1, .none); // callconv
1884 try renderToken(tree, ais, inline_token, Space.Space); // inline1502 try renderToken(ais, tree, callconv_lparen, .none); // (
1885 }1503 try renderExpression(gpa, ais, tree, fn_proto.ast.callconv_expr, .none);
18861504 try renderToken(ais, tree, callconv_rparen, .space); // )
1887 try renderToken(tree, ais, for_node.for_token, Space.Space); // for1505 } else if (is_inline) {
1888 try renderToken(tree, ais, tree.nextToken(for_node.for_token), Space.None); // (1506 try ais.writer().writeAll("callconv(.Inline) ");
1889 try renderExpression(allocator, ais, tree, for_node.array_expr, Space.None);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();1515fn renderSwitchCase(
1894 const src_one_line_to_body = !body_is_block and tree.tokensOnSameLine(rparen, for_node.body.firstToken());1516 gpa: *Allocator,
1895 const body_on_same_line = body_is_block or src_one_line_to_body;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;1546 if (switch_case.payload_token) |payload_token| {
1900 try renderExpression(allocator, ais, tree, for_node.payload, space_after_payload); // |x|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: {1558 try renderExpression(gpa, ais, tree, switch_case.ast.target_expr, space);
1903 if (for_node.@"else") |@"else"| {1559}
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 };
19141560
1915 {1561fn renderBlock(
1916 if (!body_on_same_line) ais.pushIndent();1562 gpa: *Allocator,
1917 defer if (!body_on_same_line) ais.popIndent();1563 ais: *Ais,
1918 try renderExpression(allocator, ais, tree, for_node.body, space_after_body); // { body }1564 tree: ast.Tree,
1919 }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"| {1581 ais.pushIndentNextLine();
1922 return renderExpression(allocator, ais, tree, &@"else".base, space); // else1582 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),
1923 }1594 }
1924 },1595 }
19251596 }
1926 .If => {1597 ais.popIndent();
1927 const if_node = @fieldParentPtr(ast.Node.If, "base", base);
19281598
1929 const lparen = tree.nextToken(if_node.if_token);1599 try renderToken(ais, tree, tree.lastToken(block_node), space); // rbrace
1930 const rparen = tree.nextToken(if_node.condition.lastToken());1600}
19311601
1932 try renderToken(tree, ais, if_node.if_token, Space.Space); // if1602fn renderStructInit(
1933 try renderToken(tree, ais, lparen, Space.None); // (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); // condition1623 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;1644 ais.popIndent();
1938 const body_is_block = nodeIsBlock(if_node.body);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) {1658 return renderToken(ais, tree, rbrace, space);
1941 try renderExtraNewline(tree, ais, if_node.body);1659}
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); // )
19451660
1946 if (if_node.payload) |payload| {1661// TODO: handle comments between elements
1947 try renderExpression(allocator, ais, tree, payload, Space.BlockStart); // |x|1662fn renderArrayInit(
1948 }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"| {1678 if (array_init.ast.elements.len == 0) {
1951 try renderExpression(allocator, ais, tree, if_node.body, Space.SpaceOrOutdent);1679 ais.pushIndentNextLine();
1952 return renderExpression(allocator, ais, tree, &@"else".base, space);1680 try renderToken(ais, tree, array_init.ast.lbrace, .none); // lbrace
1953 } else {1681 ais.popIndent();
1954 return renderExpression(allocator, ais, tree, if_node.body, space);1682 return renderToken(ais, tree, array_init.ast.lbrace + 1, space); // rbrace
1955 }1683 }
1956 }
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) {1703 const contains_newlines = !tree.tokensOnSameLine(array_init.ast.lbrace, rbrace);
1961 const after_rparen_space = if (if_node.payload == null) Space.Newline else Space.Space;
19621704
1963 {1705 if (!trailing_comma and !contains_newlines) {
1964 ais.pushIndent();1706 // Render all on one line, no trailing comma.
1965 defer ais.popIndent();1707 if (array_init.ast.elements.len == 1) {
1966 try renderToken(tree, ais, rparen, after_rparen_space); // )1708 // If there is only one element, we don't use spaces
1967 }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| {1720 ais.pushIndentNextLine();
1970 try renderExpression(allocator, ais, tree, payload, Space.Newline);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);
1971 }1752 }
19721753
1973 if (if_node.@"else") |@"else"| {1754 const maybe_comma = expr_last_token + 1;
1974 const else_is_block = nodeIsBlock(@"else".body);1755 if (token_tags[maybe_comma] == .comma) {
19751756 if (hasSameLineComment(tree, maybe_comma))
1976 {1757 break :sec_end i - this_line_size.? + 1;
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);
2006 }1758 }
2007 }1759 }
1760 break :sec_end row_exprs.len;
1761 };
1762 expr_index += section_end;
20081763
2009 // Single line if statement1764 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| {1769 var auto_indenting_stream = Ais{
2014 try renderExpression(allocator, ais, tree, payload, Space.Space);1770 .indent_delta = indent_delta,
2015 }1771 .underlying_writer = sub_expr_buffer.writer(),
1772 };
20161773
2017 if (if_node.@"else") |@"else"| {1774 // Calculate size of columns in current section
2018 try renderExpression(allocator, ais, tree, if_node.body, Space.Space);1775 var column_counter: usize = 0;
2019 try renderToken(tree, ais, @"else".else_token, Space.Space);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| {1815 // Render exprs in current section.
2022 try renderExpression(allocator, ais, tree, payload, Space.Space);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;
2023 }1840 }
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);
2026 } else {1845 } else {
2027 return renderExpression(allocator, ais, tree, if_node.body, space);1846 try renderExpression(gpa, ais, tree, expr, .comma); // ,
2028 }1847 }
2029 },1848 }
20301849
2031 .Asm => {1850 if (expr_index == array_init.ast.elements.len)
2032 const asm_node = @fieldParentPtr(ast.Node.Asm, "base", base);1851 break;
1852 }
20331853
2034 try renderToken(tree, ais, asm_node.asm_token, Space.Space); // asm1854 ais.popIndent();
1855 return renderToken(ais, tree, rbrace, space); // rbrace
1856}
20351857
2036 if (asm_node.volatile_token) |volatile_token| {1858fn renderContainerDecl(
2037 try renderToken(tree, ais, volatile_token, Space.Space); // volatile1859 gpa: *Allocator,
2038 try renderToken(tree, ais, tree.nextToken(volatile_token), Space.None); // (1860 ais: *Ais,
2039 } else {1861 tree: ast.Tree,
2040 try renderToken(tree, ais, tree.nextToken(asm_node.asm_token), Space.None); // (1862 container_decl_node: ast.Node.Index,
2041 }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: {1869 if (container_decl.layout_token) |layout_token| {
2044 ais.pushIndent();1870 try renderToken(ais, tree, layout_token, .space);
2045 defer ais.popIndent();1871 }
20461872
2047 if (asm_node.outputs.len == 0 and asm_node.inputs.len == 0 and asm_node.clobbers.len == 0) {1873 var lbrace: ast.TokenIndex = undefined;
2048 try renderExpression(allocator, ais, tree, asm_node.template, Space.None);1874 if (container_decl.ast.enum_token) |enum_token| {
2049 break :asmblk;1875 try renderToken(ais, tree, container_decl.ast.main_token, .none); // union
2050 }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);1914 const src_has_trailing_comma = token_tags[rbrace - 1] == .comma;
2055 defer ais.setIndentDelta(indent_delta);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: {1937 return renderToken(ais, tree, rbrace, space); // rbrace
2060 try renderToken(tree, ais, colon1, Space.Newline); // :1938}
20611939
2062 break :blk tree.nextToken(colon1);1940fn renderAsm(
2063 } else blk: {1941 gpa: *Allocator,
2064 try renderToken(tree, ais, colon1, Space.Space); // :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();1949 try renderToken(ais, tree, asm_node.ast.asm_token, .space); // asm
2067 defer ais.popIndent();
20681950
2069 for (asm_node.outputs) |*asm_output, i| {1951 if (asm_node.volatile_token) |volatile_token| {
2070 if (i + 1 < asm_node.outputs.len) {1952 try renderToken(ais, tree, volatile_token, .space); // volatile
2071 const next_asm_output = asm_node.outputs[i + 1];1953 try renderToken(ais, tree, volatile_token + 1, .none); // lparen
2072 try renderAsmOutput(allocator, ais, tree, asm_output, Space.None);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());1958 if (asm_node.ast.items.len == 0) {
2075 try renderToken(tree, ais, comma, Space.Newline); // ,1959 ais.pushIndent();
2076 try renderExtraNewlineToken(tree, ais, next_asm_output.firstToken());1960 if (asm_node.first_clobber) |first_clobber| {
2077 } else if (asm_node.inputs.len == 0 and asm_node.clobbers.len == 0) {1961 // asm ("foo" ::: "a", "b")
2078 try renderAsmOutput(allocator, ais, tree, asm_output, Space.Newline);1962 // asm ("foo" ::: "a", "b",)
2079 break :asmblk;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);
2080 } else {1982 } else {
2081 try renderAsmOutput(allocator, ais, tree, asm_output, Space.Newline);1983 try renderToken(ais, tree, tok_i, .space);
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 };
2087 }1984 }
2088 }1985 },
2089 unreachable;1986 else => unreachable,
2090 };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: {1997 ais.pushIndent();
2093 try renderToken(tree, ais, colon2, Space.Newline); // :1998 try renderExpression(gpa, ais, tree, asm_node.ast.template, .newline);
2094 break :blk tree.nextToken(colon2);1999 ais.setIndentDelta(asm_indent_delta);
2095 } else blk: {2000 const colon1 = tree.lastToken(asm_node.ast.template) + 1;
2096 try renderToken(tree, ais, colon2, Space.Space); // :2001
2097 ais.pushIndent();2002 const colon2 = if (asm_node.outputs.len == 0) colon2: {
2098 defer ais.popIndent();2003 try renderToken(ais, tree, colon1, .newline); // :
2099 for (asm_node.inputs) |*asm_input, i| {2004 break :colon2 colon1 + 1;
2100 if (i + 1 < asm_node.inputs.len) {2005 } else colon2: {
2101 const next_asm_input = &asm_node.inputs[i + 1];2006 try renderToken(ais, tree, colon1, .space); // :
2102 try renderAsmInput(allocator, ais, tree, asm_input, Space.None);2007
21032008 ais.pushIndent();
2104 const comma = tree.prevToken(next_asm_input.firstToken());2009 for (asm_node.outputs) |asm_output, i| {
2105 try renderToken(tree, ais, comma, Space.Newline); // ,2010 if (i + 1 < asm_node.outputs.len) {
2106 try renderExtraNewlineToken(tree, ais, next_asm_input.firstToken());2011 const next_asm_output = asm_node.outputs[i + 1];
2107 } else if (asm_node.clobbers.len == 0) {2012 try renderAsmOutput(gpa, ais, tree, asm_output, .none);
2108 try renderAsmInput(allocator, ais, tree, asm_input, Space.Newline);2013
2109 break :asmblk;2014 const comma = tree.firstToken(next_asm_output) - 1;
2110 } else {2015 try renderToken(ais, tree, comma, .newline); // ,
2111 try renderAsmInput(allocator, ais, tree, asm_input, Space.Newline);2016 try renderExtraNewlineToken(ais, tree, tree.firstToken(next_asm_output));
2112 const comma_or_colon = tree.nextToken(asm_input.lastToken());2017 } else if (asm_node.inputs.len == 0 and asm_node.first_clobber == null) {
2113 break :blk switch (tree.token_ids[comma_or_colon]) {2018 try renderAsmOutput(gpa, ais, tree, asm_output, .newline);
2114 .Comma => tree.nextToken(comma_or_colon),2019 ais.popIndent();
2115 else => comma_or_colon,2020 ais.setIndentDelta(indent_delta);
2116 };2021 ais.popIndent();
2117 }2022 return renderToken(ais, tree, asm_node.ast.rparen, space); // rparen
2118 }2023 } else {
2119 unreachable;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,
2120 };2030 };
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 }
2135 }2031 }
2032 } else unreachable;
2033 };
21362034
2137 return renderToken(tree, ais, asm_node.rparen, space);2035 const colon3 = if (asm_node.inputs.len == 0) colon3: {
2138 },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 => {2068 try renderToken(ais, tree, colon3, .space); // :
2141 const enum_literal = @fieldParentPtr(ast.Node.EnumLiteral, "base", base);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); // .2089fn renderCall(
2144 return renderToken(tree, ais, enum_literal.name, space); // name2090 gpa: *Allocator,
2145 },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,2099 if (call.async_token) |async_token| {
2148 .Root,2100 try renderToken(ais, tree, async_token, .space);
2149 .VarDecl,2101 }
2150 .Use,2102 try renderExpression(gpa, ais, tree, call.ast.fn_expr, .none);
2151 .TestDecl,2103
2152 => unreachable,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); // )
2153 }2111 }
2154}
21552112
2156fn renderArrayType(2113 const last_param = params[params.len - 1];
2157 allocator: *mem.Allocator,2114 const after_last_param_tok = tree.lastToken(last_param) + 1;
2158 ais: anytype,2115 if (token_tags[after_last_param_tok] == .comma) {
2159 tree: *ast.Tree,2116 ais.pushIndentNextLine();
2160 lbracket: ast.TokenIndex,2117 try renderToken(ais, tree, lparen, .newline); // (
2161 rhs: *ast.Node,2118 for (params) |param_node, i| {
2162 len_expr: *ast.Node,2119 if (i + 1 < params.len) {
2163 opt_sentinel: ?*ast.Node,2120 try renderExpression(gpa, ais, tree, param_node, .none);
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());
21702121
2171 const starts_with_comment = tree.token_ids[lbracket + 1] == .LineComment;2122 // Unindent the comma for multiline string literals.
2172 const ends_with_comment = tree.token_ids[rbracket - 1] == .LineComment;2123 const is_multiline_string =
2173 const new_space = if (ends_with_comment) Space.Newline else Space.None;2124 token_tags[tree.firstToken(param_node)] == .multiline_string_literal_line;
2174 {2125 if (is_multiline_string) ais.popIndent();
2175 const do_indent = (starts_with_comment or ends_with_comment);
2176 if (do_indent) ais.pushIndent();
2177 defer if (do_indent) ais.popIndent();
21782126
2179 try renderToken(tree, ais, lbracket, Space.None); // [2127 const comma = tree.lastToken(param_node) + 1;
2180 try renderExpression(allocator, ais, tree, len_expr, new_space);2128 try renderToken(ais, tree, comma, .newline); // ,
21812129
2182 if (starts_with_comment) {2130 if (is_multiline_string) ais.pushIndent();
2183 try ais.maybeInsertNewline();2131
2132 try renderExtraNewline(ais, tree, params[i + 1]);
2133 } else {
2134 try renderExpression(gpa, ais, tree, param_node, .comma);
2135 }
2184 }2136 }
2185 if (opt_sentinel) |sentinel| {2137 ais.popIndent();
2186 const colon_token = tree.prevToken(sentinel.firstToken());2138 return renderToken(ais, tree, after_last_param_tok + 1, space); // )
2187 try renderToken(tree, ais, colon_token, Space.None); // :2139 }
2188 try renderExpression(allocator, ais, tree, sentinel, Space.None);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();
2189 }2149 }
2190 if (starts_with_comment) {2150 try renderExpression(gpa, ais, tree, param_node, .none);
2191 try ais.maybeInsertNewline();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);
2192 }2158 }
2193 }2159 }
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); // )
2197}2162}
21982163
2199fn renderAsmOutput(2164/// Renders the given expression indented, popping the indent before rendering
2200 allocator: *mem.Allocator,2165/// any following line comments
2201 ais: anytype,2166fn renderExpressionIndented(gpa: *Allocator, ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Space) Error!void {
2202 tree: *ast.Tree,2167 const token_starts = tree.tokens.items(.start);
2203 asm_output: *const ast.Node.Asm.Output,2168 const token_tags = tree.tokens.items(.tag);
2204 space: Space,2169
2205) (@TypeOf(ais.*).Error || Error)!void {2170 ais.pushIndent();
2206 try ais.writer().writeAll("[");2171
2207 try renderExpression(allocator, ais, tree, asm_output.symbolic_name, Space.None);2172 var last_token = tree.lastToken(node);
2208 try ais.writer().writeAll("] ");2173 const punctuation = switch (space) {
2209 try renderExpression(allocator, ais, tree, asm_output.constraint, Space.None);2174 .none, .space, .newline, .skip => false,
2210 try ais.writer().writeAll(" (");2175 .comma => true,
22112176 .comma_space => token_tags[last_token + 1] == .comma,
2212 switch (asm_output.kind) {2177 .semicolon => token_tags[last_token + 1] == .semicolon,
2213 .Variable => |variable_name| {2178 };
2214 try renderExpression(allocator, ais, tree, &variable_name.base, Space.None);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;
2215 },2195 },
2216 .Return => |return_type| {2196 .semicolon => if (token_tags[last_token + 1] == .semicolon) {
2217 try ais.writer().writeAll("-> ");2197 try renderToken(ais, tree, last_token + 1, .skip);
2218 try renderExpression(allocator, ais, tree, return_type, Space.None);2198 last_token += 1;
2219 },2199 },
2220 }2200 }
22212201
2222 return renderToken(tree, ais, asm_output.lastToken(), space); // )2202 ais.popIndent();
2223}
22242203
2225fn renderAsmInput(2204 if (space == .skip) return;
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}
22402205
2241fn renderVarDecl(2206 const comment_start = token_starts[last_token] + tokenSliceForRender(tree, last_token).len;
2242 allocator: *mem.Allocator,2207 const comment = try renderComments(ais, tree, comment_start, token_starts[last_token + 1]);
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 }
22502208
2251 if (var_decl.getExternExportToken()) |extern_export_token| {2209 if (!comment) switch (space) {
2252 try renderToken(tree, ais, extern_export_token, Space.Space); // extern2210 .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| {2222/// Render an expression, and the comma that follows it, if it is present in the source.
2255 try renderExpression(allocator, ais, tree, lib_name, Space.Space); // "lib"2223fn renderExpressionComma(gpa: *Allocator, ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Space) Error!void {
2256 }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);
2257 }2231 }
2232}
22582233
2259 if (var_decl.getComptimeToken()) |comptime_token| {2234fn renderTokenComma(ais: *Ais, tree: ast.Tree, token: ast.TokenIndex, space: Space) Error!void {
2260 try renderToken(tree, ais, comptime_token, Space.Space); // comptime2235 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);
2261 }2242 }
2243}
22622244
2263 if (var_decl.getThreadLocalToken()) |thread_local_token| {2245const Space = enum {
2264 try renderToken(tree, ais, thread_local_token, Space.Space); // threadlocal2246 /// Output the token lexeme only.
2265 }2247 none,
2266 try renderToken(tree, ais, var_decl.mut_token, Space.Space); // var2248 /// 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 and2266fn renderToken(ais: *Ais, tree: ast.Tree, token_index: ast.TokenIndex, space: Space) Error!void {
2269 (var_decl.getAlignNode() != null or2267 const token_tags = tree.tokens.items(.tag);
2270 var_decl.getSectionNode() != null or2268 const token_starts = tree.tokens.items(.start);
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 };
23132269
2314 {2270 const token_start = token_starts[token_index];
2315 ais.pushIndent();2271 const lexeme = tokenSliceForRender(tree, token_index);
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 }
23222272
2323 try renderToken(tree, ais, var_decl.semicolon_token, Space.Newline);2273 try ais.writer().writeAll(lexeme);
2324}
23252274
2326fn renderParamDecl(2275 if (space == .skip) return;
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);
23342276
2335 if (param_decl.comptime_token) |comptime_token| {2277 if (space == .comma and token_tags[token_index + 1] != .comma) {
2336 try renderToken(tree, ais, comptime_token, Space.Space);2278 try ais.writer().writeByte(',');
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),
2347 }2279 }
2348}
23492280
2350fn renderStatement(2281 const comment = try renderComments(ais, tree, token_start + lexeme.len, token_starts[token_index + 1]);
2351 allocator: *mem.Allocator,2282 switch (space) {
2352 ais: anytype,2283 .none => {},
2353 tree: *ast.Tree,2284 .space => if (!comment) try ais.writer().writeByte(' '),
2354 base: *ast.Node,2285 .newline => if (!comment) try ais.insertNewline(),
2355) (@TypeOf(ais.*).Error || Error)!void {2286
2356 switch (base.tag) {2287 .comma => if (token_tags[token_index + 1] == .comma) {
2357 .VarDecl => {2288 try renderToken(ais, tree, token_index + 1, .newline);
2358 const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base);2289 } else if (!comment) {
2359 try renderVarDecl(allocator, ais, tree, var_decl);2290 try ais.insertNewline();
2360 },2291 },
2361 else => {
2362 if (base.requireSemiColon()) {
2363 try renderExpression(allocator, ais, tree, base, Space.None);
23642292
2365 const semicolon_index = tree.nextToken(base.lastToken());2293 .comma_space => if (token_tags[token_index + 1] == .comma) {
2366 assert(tree.token_ids[semicolon_index] == .Semicolon);2294 try renderToken(ais, tree, token_index + 1, .space);
2367 try renderToken(tree, ais, semicolon_index, Space.Newline);2295 } else if (!comment) {
2368 } else {2296 try ais.writer().writeByte(' ');
2369 try renderExpression(allocator, ais, tree, base, Space.Newline);2297 },
2370 }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();
2371 },2303 },
2304
2305 .skip => unreachable,
2372 }2306 }
2373}2307}
23742308
2375const Space = enum {2309/// Returns true if there exists a comment between the start of token
2376 None,2310/// `start_token` and the start of token `end_token`. This is used to determine
2377 Newline,2311/// if e.g. a fn_proto should be wrapped and have a trailing comma inserted
2378 Comma,2312/// even if there is none in the source.
2379 Space,2313fn hasComment(tree: ast.Tree, start_token: ast.TokenIndex, end_token: ast.TokenIndex) bool {
2380 SpaceOrOutdent,2314 const token_starts = tree.tokens.items(.start);
2381 NoNewline,
2382 NoComment,
2383 BlockStart,
2384};
23852315
2386fn renderTokenOffset(2316 const start = token_starts[start_token];
2387 tree: *ast.Tree,2317 const end = token_starts[end_token];
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 }
23982318
2399 var token_loc = tree.token_locs[token_index];2319 return mem.indexOf(u8, tree.source[start..end], "//") != null;
2400 try ais.writer().writeAll(mem.trimRight(u8, tree.tokenSliceLoc(token_loc)[token_skip_bytes..], " "));2320}
24012321
2402 if (space == Space.NoComment)2322/// Assumes that start is the first byte past the previous token and
2403 return;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];2329 // If there is no newline, the comment ends with EOF
2406 var next_token_loc = tree.token_locs[token_index + 1];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) {2333 const untrimmed_comment = tree.source[comment_start .. newline orelse tree.source.len];
2409 .Comma => return renderToken(tree, ais, token_index + 1, Space.Newline),2334 const trimmed_comment = mem.trimRight(u8, untrimmed_comment, &std.ascii.spaces);
2410 .LineComment => {2335
2411 try ais.writer().writeAll(", ");2336 // Don't leave any whitespace at the start of the file
2412 return renderToken(tree, ais, token_index + 1, Space.Newline);2337 if (index != 0) {
2413 },2338 if (index == start and mem.containsAtLeast(u8, tree.source[index..comment_start], 2, "\n")) {
2414 else => {2339 // Leave up to one empty line before the first comment
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(",");
2422 try ais.insertNewline();2340 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(' ');
2424 }2350 }
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];
2436 }2351 }
2437 }
24382352
2439 if (next_token_id != .LineComment) {2353 try ais.writer().print("{s}\n", .{trimmed_comment});
2440 switch (space) {2354 index = 1 + (newline orelse return true);
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 }
24592355
2460 while (true) {2356 if (ais.disabled_offset) |disabled_offset| {
2461 const comment_is_empty = mem.trimRight(u8, tree.tokenSliceLoc(next_token_loc), " ").len == 2;2357 if (mem.eql(u8, trimmed_comment, "// zig fmt: on")) {
2462 if (comment_is_empty) {2358 // write the source for which formatting was disabled directly
2463 switch (space) {2359 // to the underlying writer, fixing up invaild whitespace
2464 Space.Newline => {2360 try writeFixingWhitespace(ais.underlying_writer, tree.source[disabled_offset..index]);
2465 offset += 1;2361 ais.disabled_offset = null;
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,
2475 }2362 }
2476 } else {2363 } else if (mem.eql(u8, trimmed_comment, "// zig fmt: off")) {
2477 break;2364 ais.disabled_offset = index;
2478 }2365 }
2479 }2366 }
24802367
2481 var loc = tree.tokenLocationLoc(token_loc.end, next_token_loc);2368 if (index != start and mem.containsAtLeast(u8, tree.source[index - 1 .. end], 2, "\n")) {
2482 if (loc.line == 0) {2369 try ais.insertNewline();
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);
2510 }2370 }
25112371
2512 while (true) {2372 return index != start;
2513 // translate-c doesn't generate correct newlines2373}
2514 // in generated code (loc.line == 0) so treat that case2374
2515 // as though there was meant to be a newline between the tokens2375fn renderExtraNewline(ais: *Ais, tree: ast.Tree, node: ast.Node.Index) Error!void {
2516 var newline_count = if (loc.line <= 1) @as(u8, 1) else @as(u8, 2);2376 return renderExtraNewlineToken(ais, tree, tree.firstToken(node));
2517 while (newline_count > 0) : (newline_count -= 1) try ais.insertNewline();2377}
2518 try ais.writer().writeAll(mem.trimRight(u8, tree.tokenSliceLoc(next_token_loc), " "));2378
25192379/// Check if there is an empty line immediately before the given token. If so, render it.
2520 offset += 1;2380fn renderExtraNewlineToken(ais: *Ais, tree: ast.Tree, token_index: ast.TokenIndex) Error!void {
2521 token_loc = next_token_loc;2381 const token_starts = tree.tokens.items(.start);
2522 next_token_loc = tree.token_locs[token_index + offset];2382 const token_start = token_starts[token_index];
2523 next_token_id = tree.token_ids[token_index + offset];2383 if (token_start == 0) return;
2524 if (next_token_id != .LineComment) {2384 const prev_token_end = if (token_index == 0)
2525 switch (space) {2385 0
2526 .Newline => {2386 else
2527 if (next_token_id == .MultilineStringLiteralLine) {2387 token_starts[token_index - 1] + tokenSliceForRender(tree, token_index - 1).len;
2528 return;2388
2529 } else {2389 // If there is a comment present, it will handle the empty line
2530 try ais.insertNewline();2390 if (mem.indexOf(u8, tree.source[prev_token_end..token_start], "//") != null) return;
2531 return;2391
2532 }2392 // Iterate backwards to the end of the previous token, stopping if a
2533 },2393 // non-whitespace character is encountered or two newlines have been found.
2534 .None, .Space, .SpaceOrOutdent => {2394 var i = token_start - 1;
2535 try ais.insertNewline();2395 var newlines: u2 = 0;
2536 },2396 while (std.ascii.isSpace(tree.source[i])) : (i -= 1) {
2537 .NoNewline => {},2397 if (tree.source[i] == '\n') newlines += 1;
2538 .NoComment, .Comma, .BlockStart => unreachable,2398 if (newlines == 2) return ais.insertNewline();
2539 }2399 if (i == prev_token_end) break;
2540 return;
2541 }
2542 loc = tree.tokenLocationLoc(token_loc.end, next_token_loc);
2543 }2400 }
2544}2401}
25452402
2546fn renderToken(2403/// end_token is the token one past the last doc comment token. This function
2547 tree: *ast.Tree,2404/// searches backwards from there.
2548 ais: anytype,2405fn renderDocComments(ais: *Ais, tree: ast.Tree, end_token: ast.TokenIndex) Error!void {
2549 token_index: ast.TokenIndex,2406 // Search backwards for the first doc comment.
2550 space: Space,2407 const token_tags = tree.tokens.items(.tag);
2551) (@TypeOf(ais.*).Error || Error)!void {2408 if (end_token == 0) return;
2552 return renderTokenOffset(tree, ais, token_index, space, 0);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 }
2553}2423}
25542424
2555fn renderDocComments(2425/// start_token is first container doc comment token.
2556 tree: *ast.Tree,2426fn renderContainerDocComments(ais: *Ais, tree: ast.Tree, start_token: ast.TokenIndex) Error!void {
2557 ais: anytype,2427 const token_tags = tree.tokens.items(.tag);
2558 node: anytype,2428 var tok = start_token;
2559 doc_comments: ?*ast.Node.DocComment,2429 while (token_tags[tok] == .container_doc_comment) : (tok += 1) {
2560) (@TypeOf(ais.*).Error || Error)!void {2430 try renderToken(ais, tree, tok, .newline);
2561 const comment = doc_comments orelse return;2431 }
2562 return renderDocCommentsToken(tree, ais, comment, node.firstToken());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 }
2563}2438}
25642439
2565fn renderDocCommentsToken(2440fn tokenSliceForRender(tree: ast.Tree, token_index: ast.TokenIndex) []const u8 {
2566 tree: *ast.Tree,2441 var ret = tree.tokenSlice(token_index);
2567 ais: anytype,2442 if (tree.tokens.items(.tag)[token_index] == .multiline_string_literal_line) {
2568 comment: *ast.Node.DocComment,2443 assert(ret[ret.len - 1] == '\n');
2569 first_token: ast.TokenIndex,2444 ret.len -= 1;
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 }
2585 }2445 }
2446 return ret;
2586}2447}
25872448
2588fn nodeIsBlock(base: *const ast.Node) bool {2449fn hasSameLineComment(tree: ast.Tree, token_index: ast.TokenIndex) bool {
2589 return switch (base.tag) {2450 const token_starts = tree.tokens.items(.start);
2590 .Block,2451 const between_source = tree.source[token_starts[token_index]..token_starts[token_index + 1]];
2591 .LabeledBlock,2452 for (between_source) |byte| switch (byte) {
2592 .If,2453 '\n' => return false,
2593 .For,2454 '/' => return true,
2594 .While,2455 else => continue,
2595 .Switch,2456 };
2596 => true,2457 return false;
2597 else => 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,
2598 };2469 };
2470 return false;
2599}2471}
26002472
2601fn nodeCausesSliceOpSpace(base: *ast.Node) bool {2473fn writeFixingWhitespace(writer: std.ArrayList(u8).Writer, slice: []const u8) Error!void {
2602 return switch (base.tag) {2474 for (slice) |byte| switch (byte) {
2603 .Catch,2475 '\t' => try writer.writeAll(" " ** 4),
2604 .Add,2476 '\r' => {},
2605 .AddWrap,2477 else => try writer.writeByte(byte),
2606 .ArrayCat,2478 };
2607 .ArrayMult,2479}
2608 .Assign,2480
2609 .AssignBitAnd,2481fn nodeIsBlock(tag: ast.Node.Tag) bool {
2610 .AssignBitOr,2482 return switch (tag) {
2611 .AssignBitShiftLeft,2483 .block,
2612 .AssignBitShiftRight,2484 .block_semicolon,
2613 .AssignBitXor,2485 .block_two,
2614 .AssignDiv,2486 .block_two_semicolon,
2615 .AssignSub,2487 .@"if",
2616 .AssignSubWrap,2488 .if_simple,
2617 .AssignMod,2489 .@"for",
2618 .AssignAdd,2490 .for_simple,
2619 .AssignAddWrap,2491 .@"while",
2620 .AssignMul,2492 .while_simple,
2621 .AssignMulWrap,2493 .while_cont,
2622 .BangEqual,2494 .@"switch",
2623 .BitAnd,2495 .switch_comma,
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,
2645 => true,2496 => true,
2497 else => false,
2498 };
2499}
26462500
2501fn nodeIsIf(tag: ast.Node.Tag) bool {
2502 return switch (tag) {
2503 .@"if", .if_simple => true,
2647 else => false,2504 else => false,
2648 };2505 };
2649}2506}
26502507
2651fn copyFixingWhitespace(ais: anytype, slice: []const u8) @TypeOf(ais.*).Error!void {2508fn nodeCausesSliceOpSpace(tag: ast.Node.Tag) bool {
2652 for (slice) |byte| switch (byte) {2509 return switch (tag) {
2653 '\t' => try ais.writer().writeAll(" "),2510 .@"catch",
2654 '\r' => {},2511 .add,
2655 else => try ais.writer().writeByte(byte),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,
2656 };2554 };
2657}2555}
26582556
2659// Returns the number of nodes in `expr` that are on the same line as `rtoken`,2557// Returns the number of nodes in `expr` that are on the same line as `rtoken`,
2660// or null if they all are on the same line.2558// or null if they all are on the same line.
2661fn rowSize(tree: *ast.Tree, exprs: []*ast.Node, rtoken: ast.TokenIndex) ?usize {2559fn rowSize(tree: ast.Tree, exprs: []const ast.Node.Index, rtoken: ast.TokenIndex) ?usize {
2662 const first_token = exprs[0].firstToken();2560 const token_tags = tree.tokens.items(.tag);
2663 const first_loc = tree.tokenLocation(tree.token_locs[first_token].start, rtoken);2561
2664 if (first_loc.line == 0) {2562 const first_token = tree.firstToken(exprs[0]);
2665 const maybe_comma = tree.prevToken(rtoken);2563 if (tree.tokensOnSameLine(first_token, rtoken)) {
2666 if (tree.token_ids[maybe_comma] == .Comma)2564 const maybe_comma = rtoken - 1;
2565 if (token_tags[maybe_comma] == .comma)
2667 return 1;2566 return 1;
2668 return null; // no newlines2567 return null; // no newlines
2669 }2568 }
...@@ -2671,9 +2570,8 @@ fn rowSize(tree: *ast.Tree, exprs: []*ast.Node, rtoken: ast.TokenIndex) ?usize {...@@ -2671,9 +2570,8 @@ fn rowSize(tree: *ast.Tree, exprs: []*ast.Node, rtoken: ast.TokenIndex) ?usize {
2671 var count: usize = 1;2570 var count: usize = 1;
2672 for (exprs) |expr, i| {2571 for (exprs) |expr, i| {
2673 if (i + 1 < exprs.len) {2572 if (i + 1 < exprs.len) {
2674 const expr_last_token = expr.lastToken() + 1;2573 const expr_last_token = tree.lastToken(expr) + 1;
2675 const loc = tree.tokenLocation(tree.token_locs[expr_last_token].start, exprs[i + 1].firstToken());2574 if (!tree.tokensOnSameLine(expr_last_token, tree.firstToken(exprs[i + 1]))) return count;
2676 if (loc.line != 0) return count;
2677 count += 1;2575 count += 1;
2678 } else {2576 } else {
2679 return count;2577 return count;
...@@ -2681,3 +2579,150 @@ fn rowSize(tree: *ast.Tree, exprs: []*ast.Node, rtoken: ast.TokenIndex) ?usize {...@@ -2681,3 +2579,150 @@ fn rowSize(tree: *ast.Tree, exprs: []*ast.Node, rtoken: ast.TokenIndex) ?usize {
2681 }2579 }
2682 unreachable;2580 unreachable;
2683}2581}
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");...@@ -7,7 +7,7 @@ const std = @import("../std.zig");
7const mem = std.mem;7const mem = std.mem;
88
9pub const Token = struct {9pub const Token = struct {
10 id: Id,10 tag: Tag,
11 loc: Loc,11 loc: Loc,
1212
13 pub const Loc = struct {13 pub const Loc = struct {
...@@ -15,315 +15,315 @@ pub const Token = struct {...@@ -15,315 +15,315 @@ pub const Token = struct {
15 end: usize,15 end: usize,
16 };16 };
1717
18 pub const keywords = std.ComptimeStringMap(Id, .{18 pub const keywords = std.ComptimeStringMap(Tag, .{
19 .{ "align", .Keyword_align },19 .{ "align", .keyword_align },
20 .{ "allowzero", .Keyword_allowzero },20 .{ "allowzero", .keyword_allowzero },
21 .{ "and", .Keyword_and },21 .{ "and", .keyword_and },
22 .{ "anyframe", .Keyword_anyframe },22 .{ "anyframe", .keyword_anyframe },
23 .{ "anytype", .Keyword_anytype },23 .{ "anytype", .keyword_anytype },
24 .{ "asm", .Keyword_asm },24 .{ "asm", .keyword_asm },
25 .{ "async", .Keyword_async },25 .{ "async", .keyword_async },
26 .{ "await", .Keyword_await },26 .{ "await", .keyword_await },
27 .{ "break", .Keyword_break },27 .{ "break", .keyword_break },
28 .{ "callconv", .Keyword_callconv },28 .{ "callconv", .keyword_callconv },
29 .{ "catch", .Keyword_catch },29 .{ "catch", .keyword_catch },
30 .{ "comptime", .Keyword_comptime },30 .{ "comptime", .keyword_comptime },
31 .{ "const", .Keyword_const },31 .{ "const", .keyword_const },
32 .{ "continue", .Keyword_continue },32 .{ "continue", .keyword_continue },
33 .{ "defer", .Keyword_defer },33 .{ "defer", .keyword_defer },
34 .{ "else", .Keyword_else },34 .{ "else", .keyword_else },
35 .{ "enum", .Keyword_enum },35 .{ "enum", .keyword_enum },
36 .{ "errdefer", .Keyword_errdefer },36 .{ "errdefer", .keyword_errdefer },
37 .{ "error", .Keyword_error },37 .{ "error", .keyword_error },
38 .{ "export", .Keyword_export },38 .{ "export", .keyword_export },
39 .{ "extern", .Keyword_extern },39 .{ "extern", .keyword_extern },
40 .{ "false", .Keyword_false },40 .{ "false", .keyword_false },
41 .{ "fn", .Keyword_fn },41 .{ "fn", .keyword_fn },
42 .{ "for", .Keyword_for },42 .{ "for", .keyword_for },
43 .{ "if", .Keyword_if },43 .{ "if", .keyword_if },
44 .{ "inline", .Keyword_inline },44 .{ "inline", .keyword_inline },
45 .{ "noalias", .Keyword_noalias },45 .{ "noalias", .keyword_noalias },
46 .{ "noasync", .Keyword_nosuspend }, // TODO: remove this46 .{ "noinline", .keyword_noinline },
47 .{ "noinline", .Keyword_noinline },47 .{ "nosuspend", .keyword_nosuspend },
48 .{ "nosuspend", .Keyword_nosuspend },48 .{ "null", .keyword_null },
49 .{ "null", .Keyword_null },49 .{ "opaque", .keyword_opaque },
50 .{ "opaque", .Keyword_opaque },50 .{ "or", .keyword_or },
51 .{ "or", .Keyword_or },51 .{ "orelse", .keyword_orelse },
52 .{ "orelse", .Keyword_orelse },52 .{ "packed", .keyword_packed },
53 .{ "packed", .Keyword_packed },53 .{ "pub", .keyword_pub },
54 .{ "pub", .Keyword_pub },54 .{ "resume", .keyword_resume },
55 .{ "resume", .Keyword_resume },55 .{ "return", .keyword_return },
56 .{ "return", .Keyword_return },56 .{ "linksection", .keyword_linksection },
57 .{ "linksection", .Keyword_linksection },57 .{ "struct", .keyword_struct },
58 .{ "struct", .Keyword_struct },58 .{ "suspend", .keyword_suspend },
59 .{ "suspend", .Keyword_suspend },59 .{ "switch", .keyword_switch },
60 .{ "switch", .Keyword_switch },60 .{ "test", .keyword_test },
61 .{ "test", .Keyword_test },61 .{ "threadlocal", .keyword_threadlocal },
62 .{ "threadlocal", .Keyword_threadlocal },62 .{ "true", .keyword_true },
63 .{ "true", .Keyword_true },63 .{ "try", .keyword_try },
64 .{ "try", .Keyword_try },64 .{ "undefined", .keyword_undefined },
65 .{ "undefined", .Keyword_undefined },65 .{ "union", .keyword_union },
66 .{ "union", .Keyword_union },66 .{ "unreachable", .keyword_unreachable },
67 .{ "unreachable", .Keyword_unreachable },67 .{ "usingnamespace", .keyword_usingnamespace },
68 .{ "usingnamespace", .Keyword_usingnamespace },68 .{ "var", .keyword_var },
69 .{ "var", .Keyword_var },69 .{ "volatile", .keyword_volatile },
70 .{ "volatile", .Keyword_volatile },70 .{ "while", .keyword_while },
71 .{ "while", .Keyword_while },
72 });71 });
7372
74 pub fn getKeyword(bytes: []const u8) ?Id {73 pub fn getKeyword(bytes: []const u8) ?Tag {
75 return keywords.get(bytes);74 return keywords.get(bytes);
76 }75 }
7776
78 pub const Id = enum {77 pub const Tag = enum {
79 Invalid,78 invalid,
80 Invalid_ampersands,79 invalid_ampersands,
81 Invalid_periodasterisks,80 invalid_periodasterisks,
82 Identifier,81 identifier,
83 StringLiteral,82 string_literal,
84 MultilineStringLiteralLine,83 multiline_string_literal_line,
85 CharLiteral,84 char_literal,
86 Eof,85 eof,
87 Builtin,86 builtin,
88 Bang,87 bang,
89 Pipe,88 pipe,
90 PipePipe,89 pipe_pipe,
91 PipeEqual,90 pipe_equal,
92 Equal,91 equal,
93 EqualEqual,92 equal_equal,
94 EqualAngleBracketRight,93 equal_angle_bracket_right,
95 BangEqual,94 bang_equal,
96 LParen,95 l_paren,
97 RParen,96 r_paren,
98 Semicolon,97 semicolon,
99 Percent,98 percent,
100 PercentEqual,99 percent_equal,
101 LBrace,100 l_brace,
102 RBrace,101 r_brace,
103 LBracket,102 l_bracket,
104 RBracket,103 r_bracket,
105 Period,104 period,
106 PeriodAsterisk,105 period_asterisk,
107 Ellipsis2,106 ellipsis2,
108 Ellipsis3,107 ellipsis3,
109 Caret,108 caret,
110 CaretEqual,109 caret_equal,
111 Plus,110 plus,
112 PlusPlus,111 plus_plus,
113 PlusEqual,112 plus_equal,
114 PlusPercent,113 plus_percent,
115 PlusPercentEqual,114 plus_percent_equal,
116 Minus,115 minus,
117 MinusEqual,116 minus_equal,
118 MinusPercent,117 minus_percent,
119 MinusPercentEqual,118 minus_percent_equal,
120 Asterisk,119 asterisk,
121 AsteriskEqual,120 asterisk_equal,
122 AsteriskAsterisk,121 asterisk_asterisk,
123 AsteriskPercent,122 asterisk_percent,
124 AsteriskPercentEqual,123 asterisk_percent_equal,
125 Arrow,124 arrow,
126 Colon,125 colon,
127 Slash,126 slash,
128 SlashEqual,127 slash_equal,
129 Comma,128 comma,
130 Ampersand,129 ampersand,
131 AmpersandEqual,130 ampersand_equal,
132 QuestionMark,131 question_mark,
133 AngleBracketLeft,132 angle_bracket_left,
134 AngleBracketLeftEqual,133 angle_bracket_left_equal,
135 AngleBracketAngleBracketLeft,134 angle_bracket_angle_bracket_left,
136 AngleBracketAngleBracketLeftEqual,135 angle_bracket_angle_bracket_left_equal,
137 AngleBracketRight,136 angle_bracket_right,
138 AngleBracketRightEqual,137 angle_bracket_right_equal,
139 AngleBracketAngleBracketRight,138 angle_bracket_angle_bracket_right,
140 AngleBracketAngleBracketRightEqual,139 angle_bracket_angle_bracket_right_equal,
141 Tilde,140 tilde,
142 IntegerLiteral,141 integer_literal,
143 FloatLiteral,142 float_literal,
144 LineComment,143 doc_comment,
145 DocComment,144 container_doc_comment,
146 ContainerDocComment,145 keyword_align,
147 ShebangLine,146 keyword_allowzero,
148 Keyword_align,147 keyword_and,
149 Keyword_allowzero,148 keyword_anyframe,
150 Keyword_and,149 keyword_anytype,
151 Keyword_anyframe,150 keyword_asm,
152 Keyword_anytype,151 keyword_async,
153 Keyword_asm,152 keyword_await,
154 Keyword_async,153 keyword_break,
155 Keyword_await,154 keyword_callconv,
156 Keyword_break,155 keyword_catch,
157 Keyword_callconv,156 keyword_comptime,
158 Keyword_catch,157 keyword_const,
159 Keyword_comptime,158 keyword_continue,
160 Keyword_const,159 keyword_defer,
161 Keyword_continue,160 keyword_else,
162 Keyword_defer,161 keyword_enum,
163 Keyword_else,162 keyword_errdefer,
164 Keyword_enum,163 keyword_error,
165 Keyword_errdefer,164 keyword_export,
166 Keyword_error,165 keyword_extern,
167 Keyword_export,166 keyword_false,
168 Keyword_extern,167 keyword_fn,
169 Keyword_false,168 keyword_for,
170 Keyword_fn,169 keyword_if,
171 Keyword_for,170 keyword_inline,
172 Keyword_if,171 keyword_noalias,
173 Keyword_inline,172 keyword_noinline,
174 Keyword_noalias,173 keyword_nosuspend,
175 Keyword_noinline,174 keyword_null,
176 Keyword_nosuspend,175 keyword_opaque,
177 Keyword_null,176 keyword_or,
178 Keyword_opaque,177 keyword_orelse,
179 Keyword_or,178 keyword_packed,
180 Keyword_orelse,179 keyword_pub,
181 Keyword_packed,180 keyword_resume,
182 Keyword_pub,181 keyword_return,
183 Keyword_resume,182 keyword_linksection,
184 Keyword_return,183 keyword_struct,
185 Keyword_linksection,184 keyword_suspend,
186 Keyword_struct,185 keyword_switch,
187 Keyword_suspend,186 keyword_test,
188 Keyword_switch,187 keyword_threadlocal,
189 Keyword_test,188 keyword_true,
190 Keyword_threadlocal,189 keyword_try,
191 Keyword_true,190 keyword_undefined,
192 Keyword_try,191 keyword_union,
193 Keyword_undefined,192 keyword_unreachable,
194 Keyword_union,193 keyword_usingnamespace,
195 Keyword_unreachable,194 keyword_var,
196 Keyword_usingnamespace,195 keyword_volatile,
197 Keyword_var,196 keyword_while,
198 Keyword_volatile,197
199 Keyword_while,198 pub fn lexeme(tag: Tag) ?[]const u8 {
200199 return switch (tag) {
201 pub fn symbol(id: Id) []const u8 {200 .invalid,
202 return switch (id) {201 .identifier,
203 .Invalid => "Invalid",202 .string_literal,
204 .Invalid_ampersands => "&&",203 .multiline_string_literal_line,
205 .Invalid_periodasterisks => ".**",204 .char_literal,
206 .Identifier => "Identifier",205 .eof,
207 .StringLiteral => "StringLiteral",206 .builtin,
208 .MultilineStringLiteralLine => "MultilineStringLiteralLine",207 .integer_literal,
209 .CharLiteral => "CharLiteral",208 .float_literal,
210 .Eof => "Eof",209 .doc_comment,
211 .Builtin => "Builtin",210 .container_doc_comment,
212 .IntegerLiteral => "IntegerLiteral",211 => null,
213 .FloatLiteral => "FloatLiteral",212
214 .LineComment => "LineComment",213 .invalid_ampersands => "&&",
215 .DocComment => "DocComment",214 .invalid_periodasterisks => ".**",
216 .ContainerDocComment => "ContainerDocComment",215 .bang => "!",
217 .ShebangLine => "ShebangLine",216 .pipe => "|",
218217 .pipe_pipe => "||",
219 .Bang => "!",218 .pipe_equal => "|=",
220 .Pipe => "|",219 .equal => "=",
221 .PipePipe => "||",220 .equal_equal => "==",
222 .PipeEqual => "|=",221 .equal_angle_bracket_right => "=>",
223 .Equal => "=",222 .bang_equal => "!=",
224 .EqualEqual => "==",223 .l_paren => "(",
225 .EqualAngleBracketRight => "=>",224 .r_paren => ")",
226 .BangEqual => "!=",225 .semicolon => ";",
227 .LParen => "(",226 .percent => "%",
228 .RParen => ")",227 .percent_equal => "%=",
229 .Semicolon => ";",228 .l_brace => "{",
230 .Percent => "%",229 .r_brace => "}",
231 .PercentEqual => "%=",230 .l_bracket => "[",
232 .LBrace => "{",231 .r_bracket => "]",
233 .RBrace => "}",232 .period => ".",
234 .LBracket => "[",233 .period_asterisk => ".*",
235 .RBracket => "]",234 .ellipsis2 => "..",
236 .Period => ".",235 .ellipsis3 => "...",
237 .PeriodAsterisk => ".*",236 .caret => "^",
238 .Ellipsis2 => "..",237 .caret_equal => "^=",
239 .Ellipsis3 => "...",238 .plus => "+",
240 .Caret => "^",239 .plus_plus => "++",
241 .CaretEqual => "^=",240 .plus_equal => "+=",
242 .Plus => "+",241 .plus_percent => "+%",
243 .PlusPlus => "++",242 .plus_percent_equal => "+%=",
244 .PlusEqual => "+=",243 .minus => "-",
245 .PlusPercent => "+%",244 .minus_equal => "-=",
246 .PlusPercentEqual => "+%=",245 .minus_percent => "-%",
247 .Minus => "-",246 .minus_percent_equal => "-%=",
248 .MinusEqual => "-=",247 .asterisk => "*",
249 .MinusPercent => "-%",248 .asterisk_equal => "*=",
250 .MinusPercentEqual => "-%=",249 .asterisk_asterisk => "**",
251 .Asterisk => "*",250 .asterisk_percent => "*%",
252 .AsteriskEqual => "*=",251 .asterisk_percent_equal => "*%=",
253 .AsteriskAsterisk => "**",252 .arrow => "->",
254 .AsteriskPercent => "*%",253 .colon => ":",
255 .AsteriskPercentEqual => "*%=",254 .slash => "/",
256 .Arrow => "->",255 .slash_equal => "/=",
257 .Colon => ":",256 .comma => ",",
258 .Slash => "/",257 .ampersand => "&",
259 .SlashEqual => "/=",258 .ampersand_equal => "&=",
260 .Comma => ",",259 .question_mark => "?",
261 .Ampersand => "&",260 .angle_bracket_left => "<",
262 .AmpersandEqual => "&=",261 .angle_bracket_left_equal => "<=",
263 .QuestionMark => "?",262 .angle_bracket_angle_bracket_left => "<<",
264 .AngleBracketLeft => "<",263 .angle_bracket_angle_bracket_left_equal => "<<=",
265 .AngleBracketLeftEqual => "<=",264 .angle_bracket_right => ">",
266 .AngleBracketAngleBracketLeft => "<<",265 .angle_bracket_right_equal => ">=",
267 .AngleBracketAngleBracketLeftEqual => "<<=",266 .angle_bracket_angle_bracket_right => ">>",
268 .AngleBracketRight => ">",267 .angle_bracket_angle_bracket_right_equal => ">>=",
269 .AngleBracketRightEqual => ">=",268 .tilde => "~",
270 .AngleBracketAngleBracketRight => ">>",269 .keyword_align => "align",
271 .AngleBracketAngleBracketRightEqual => ">>=",270 .keyword_allowzero => "allowzero",
272 .Tilde => "~",271 .keyword_and => "and",
273 .Keyword_align => "align",272 .keyword_anyframe => "anyframe",
274 .Keyword_allowzero => "allowzero",273 .keyword_anytype => "anytype",
275 .Keyword_and => "and",274 .keyword_asm => "asm",
276 .Keyword_anyframe => "anyframe",275 .keyword_async => "async",
277 .Keyword_anytype => "anytype",276 .keyword_await => "await",
278 .Keyword_asm => "asm",277 .keyword_break => "break",
279 .Keyword_async => "async",278 .keyword_callconv => "callconv",
280 .Keyword_await => "await",279 .keyword_catch => "catch",
281 .Keyword_break => "break",280 .keyword_comptime => "comptime",
282 .Keyword_callconv => "callconv",281 .keyword_const => "const",
283 .Keyword_catch => "catch",282 .keyword_continue => "continue",
284 .Keyword_comptime => "comptime",283 .keyword_defer => "defer",
285 .Keyword_const => "const",284 .keyword_else => "else",
286 .Keyword_continue => "continue",285 .keyword_enum => "enum",
287 .Keyword_defer => "defer",286 .keyword_errdefer => "errdefer",
288 .Keyword_else => "else",287 .keyword_error => "error",
289 .Keyword_enum => "enum",288 .keyword_export => "export",
290 .Keyword_errdefer => "errdefer",289 .keyword_extern => "extern",
291 .Keyword_error => "error",290 .keyword_false => "false",
292 .Keyword_export => "export",291 .keyword_fn => "fn",
293 .Keyword_extern => "extern",292 .keyword_for => "for",
294 .Keyword_false => "false",293 .keyword_if => "if",
295 .Keyword_fn => "fn",294 .keyword_inline => "inline",
296 .Keyword_for => "for",295 .keyword_noalias => "noalias",
297 .Keyword_if => "if",296 .keyword_noinline => "noinline",
298 .Keyword_inline => "inline",297 .keyword_nosuspend => "nosuspend",
299 .Keyword_noalias => "noalias",298 .keyword_null => "null",
300 .Keyword_noinline => "noinline",299 .keyword_opaque => "opaque",
301 .Keyword_nosuspend => "nosuspend",300 .keyword_or => "or",
302 .Keyword_null => "null",301 .keyword_orelse => "orelse",
303 .Keyword_opaque => "opaque",302 .keyword_packed => "packed",
304 .Keyword_or => "or",303 .keyword_pub => "pub",
305 .Keyword_orelse => "orelse",304 .keyword_resume => "resume",
306 .Keyword_packed => "packed",305 .keyword_return => "return",
307 .Keyword_pub => "pub",306 .keyword_linksection => "linksection",
308 .Keyword_resume => "resume",307 .keyword_struct => "struct",
309 .Keyword_return => "return",308 .keyword_suspend => "suspend",
310 .Keyword_linksection => "linksection",309 .keyword_switch => "switch",
311 .Keyword_struct => "struct",310 .keyword_test => "test",
312 .Keyword_suspend => "suspend",311 .keyword_threadlocal => "threadlocal",
313 .Keyword_switch => "switch",312 .keyword_true => "true",
314 .Keyword_test => "test",313 .keyword_try => "try",
315 .Keyword_threadlocal => "threadlocal",314 .keyword_undefined => "undefined",
316 .Keyword_true => "true",315 .keyword_union => "union",
317 .Keyword_try => "try",316 .keyword_unreachable => "unreachable",
318 .Keyword_undefined => "undefined",317 .keyword_usingnamespace => "usingnamespace",
319 .Keyword_union => "union",318 .keyword_var => "var",
320 .Keyword_unreachable => "unreachable",319 .keyword_volatile => "volatile",
321 .Keyword_usingnamespace => "usingnamespace",320 .keyword_while => "while",
322 .Keyword_var => "var",
323 .Keyword_volatile => "volatile",
324 .Keyword_while => "while",
325 };321 };
326 }322 }
323
324 pub fn symbol(tag: Tag) []const u8 {
325 return tag.lexeme() orelse @tagName(tag);
326 }
327 };327 };
328};328};
329329
...@@ -334,7 +334,7 @@ pub const Tokenizer = struct {...@@ -334,7 +334,7 @@ pub const Tokenizer = struct {
334334
335 /// For debugging purposes335 /// For debugging purposes
336 pub fn dump(self: *Tokenizer, token: *const Token) void {336 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] });
338 }338 }
339339
340 pub fn init(buffer: []const u8) Tokenizer {340 pub fn init(buffer: []const u8) Tokenizer {
...@@ -421,7 +421,7 @@ pub const Tokenizer = struct {...@@ -421,7 +421,7 @@ pub const Tokenizer = struct {
421 const start_index = self.index;421 const start_index = self.index;
422 var state: State = .start;422 var state: State = .start;
423 var result = Token{423 var result = Token{
424 .id = .Eof,424 .tag = .eof,
425 .loc = .{425 .loc = .{
426 .start = self.index,426 .start = self.index,
427 .end = undefined,427 .end = undefined,
...@@ -438,14 +438,14 @@ pub const Tokenizer = struct {...@@ -438,14 +438,14 @@ pub const Tokenizer = struct {
438 },438 },
439 '"' => {439 '"' => {
440 state = .string_literal;440 state = .string_literal;
441 result.id = .StringLiteral;441 result.tag = .string_literal;
442 },442 },
443 '\'' => {443 '\'' => {
444 state = .char_literal;444 state = .char_literal;
445 },445 },
446 'a'...'z', 'A'...'Z', '_' => {446 'a'...'z', 'A'...'Z', '_' => {
447 state = .identifier;447 state = .identifier;
448 result.id = .Identifier;448 result.tag = .identifier;
449 },449 },
450 '@' => {450 '@' => {
451 state = .saw_at_sign;451 state = .saw_at_sign;
...@@ -460,42 +460,42 @@ pub const Tokenizer = struct {...@@ -460,42 +460,42 @@ pub const Tokenizer = struct {
460 state = .pipe;460 state = .pipe;
461 },461 },
462 '(' => {462 '(' => {
463 result.id = .LParen;463 result.tag = .l_paren;
464 self.index += 1;464 self.index += 1;
465 break;465 break;
466 },466 },
467 ')' => {467 ')' => {
468 result.id = .RParen;468 result.tag = .r_paren;
469 self.index += 1;469 self.index += 1;
470 break;470 break;
471 },471 },
472 '[' => {472 '[' => {
473 result.id = .LBracket;473 result.tag = .l_bracket;
474 self.index += 1;474 self.index += 1;
475 break;475 break;
476 },476 },
477 ']' => {477 ']' => {
478 result.id = .RBracket;478 result.tag = .r_bracket;
479 self.index += 1;479 self.index += 1;
480 break;480 break;
481 },481 },
482 ';' => {482 ';' => {
483 result.id = .Semicolon;483 result.tag = .semicolon;
484 self.index += 1;484 self.index += 1;
485 break;485 break;
486 },486 },
487 ',' => {487 ',' => {
488 result.id = .Comma;488 result.tag = .comma;
489 self.index += 1;489 self.index += 1;
490 break;490 break;
491 },491 },
492 '?' => {492 '?' => {
493 result.id = .QuestionMark;493 result.tag = .question_mark;
494 self.index += 1;494 self.index += 1;
495 break;495 break;
496 },496 },
497 ':' => {497 ':' => {
498 result.id = .Colon;498 result.tag = .colon;
499 self.index += 1;499 self.index += 1;
500 break;500 break;
501 },501 },
...@@ -519,20 +519,20 @@ pub const Tokenizer = struct {...@@ -519,20 +519,20 @@ pub const Tokenizer = struct {
519 },519 },
520 '\\' => {520 '\\' => {
521 state = .backslash;521 state = .backslash;
522 result.id = .MultilineStringLiteralLine;522 result.tag = .multiline_string_literal_line;
523 },523 },
524 '{' => {524 '{' => {
525 result.id = .LBrace;525 result.tag = .l_brace;
526 self.index += 1;526 self.index += 1;
527 break;527 break;
528 },528 },
529 '}' => {529 '}' => {
530 result.id = .RBrace;530 result.tag = .r_brace;
531 self.index += 1;531 self.index += 1;
532 break;532 break;
533 },533 },
534 '~' => {534 '~' => {
535 result.id = .Tilde;535 result.tag = .tilde;
536 self.index += 1;536 self.index += 1;
537 break;537 break;
538 },538 },
...@@ -550,14 +550,14 @@ pub const Tokenizer = struct {...@@ -550,14 +550,14 @@ pub const Tokenizer = struct {
550 },550 },
551 '0' => {551 '0' => {
552 state = .zero;552 state = .zero;
553 result.id = .IntegerLiteral;553 result.tag = .integer_literal;
554 },554 },
555 '1'...'9' => {555 '1'...'9' => {
556 state = .int_literal_dec;556 state = .int_literal_dec;
557 result.id = .IntegerLiteral;557 result.tag = .integer_literal;
558 },558 },
559 else => {559 else => {
560 result.id = .Invalid;560 result.tag = .invalid;
561 self.index += 1;561 self.index += 1;
562 break;562 break;
563 },563 },
...@@ -565,42 +565,42 @@ pub const Tokenizer = struct {...@@ -565,42 +565,42 @@ pub const Tokenizer = struct {
565565
566 .saw_at_sign => switch (c) {566 .saw_at_sign => switch (c) {
567 '"' => {567 '"' => {
568 result.id = .Identifier;568 result.tag = .identifier;
569 state = .string_literal;569 state = .string_literal;
570 },570 },
571 else => {571 else => {
572 // reinterpret as a builtin572 // reinterpret as a builtin
573 self.index -= 1;573 self.index -= 1;
574 state = .builtin;574 state = .builtin;
575 result.id = .Builtin;575 result.tag = .builtin;
576 },576 },
577 },577 },
578578
579 .ampersand => switch (c) {579 .ampersand => switch (c) {
580 '&' => {580 '&' => {
581 result.id = .Invalid_ampersands;581 result.tag = .invalid_ampersands;
582 self.index += 1;582 self.index += 1;
583 break;583 break;
584 },584 },
585 '=' => {585 '=' => {
586 result.id = .AmpersandEqual;586 result.tag = .ampersand_equal;
587 self.index += 1;587 self.index += 1;
588 break;588 break;
589 },589 },
590 else => {590 else => {
591 result.id = .Ampersand;591 result.tag = .ampersand;
592 break;592 break;
593 },593 },
594 },594 },
595595
596 .asterisk => switch (c) {596 .asterisk => switch (c) {
597 '=' => {597 '=' => {
598 result.id = .AsteriskEqual;598 result.tag = .asterisk_equal;
599 self.index += 1;599 self.index += 1;
600 break;600 break;
601 },601 },
602 '*' => {602 '*' => {
603 result.id = .AsteriskAsterisk;603 result.tag = .asterisk_asterisk;
604 self.index += 1;604 self.index += 1;
605 break;605 break;
606 },606 },
...@@ -608,43 +608,43 @@ pub const Tokenizer = struct {...@@ -608,43 +608,43 @@ pub const Tokenizer = struct {
608 state = .asterisk_percent;608 state = .asterisk_percent;
609 },609 },
610 else => {610 else => {
611 result.id = .Asterisk;611 result.tag = .asterisk;
612 break;612 break;
613 },613 },
614 },614 },
615615
616 .asterisk_percent => switch (c) {616 .asterisk_percent => switch (c) {
617 '=' => {617 '=' => {
618 result.id = .AsteriskPercentEqual;618 result.tag = .asterisk_percent_equal;
619 self.index += 1;619 self.index += 1;
620 break;620 break;
621 },621 },
622 else => {622 else => {
623 result.id = .AsteriskPercent;623 result.tag = .asterisk_percent;
624 break;624 break;
625 },625 },
626 },626 },
627627
628 .percent => switch (c) {628 .percent => switch (c) {
629 '=' => {629 '=' => {
630 result.id = .PercentEqual;630 result.tag = .percent_equal;
631 self.index += 1;631 self.index += 1;
632 break;632 break;
633 },633 },
634 else => {634 else => {
635 result.id = .Percent;635 result.tag = .percent;
636 break;636 break;
637 },637 },
638 },638 },
639639
640 .plus => switch (c) {640 .plus => switch (c) {
641 '=' => {641 '=' => {
642 result.id = .PlusEqual;642 result.tag = .plus_equal;
643 self.index += 1;643 self.index += 1;
644 break;644 break;
645 },645 },
646 '+' => {646 '+' => {
647 result.id = .PlusPlus;647 result.tag = .plus_plus;
648 self.index += 1;648 self.index += 1;
649 break;649 break;
650 },650 },
...@@ -652,31 +652,31 @@ pub const Tokenizer = struct {...@@ -652,31 +652,31 @@ pub const Tokenizer = struct {
652 state = .plus_percent;652 state = .plus_percent;
653 },653 },
654 else => {654 else => {
655 result.id = .Plus;655 result.tag = .plus;
656 break;656 break;
657 },657 },
658 },658 },
659659
660 .plus_percent => switch (c) {660 .plus_percent => switch (c) {
661 '=' => {661 '=' => {
662 result.id = .PlusPercentEqual;662 result.tag = .plus_percent_equal;
663 self.index += 1;663 self.index += 1;
664 break;664 break;
665 },665 },
666 else => {666 else => {
667 result.id = .PlusPercent;667 result.tag = .plus_percent;
668 break;668 break;
669 },669 },
670 },670 },
671671
672 .caret => switch (c) {672 .caret => switch (c) {
673 '=' => {673 '=' => {
674 result.id = .CaretEqual;674 result.tag = .caret_equal;
675 self.index += 1;675 self.index += 1;
676 break;676 break;
677 },677 },
678 else => {678 else => {
679 result.id = .Caret;679 result.tag = .caret;
680 break;680 break;
681 },681 },
682 },682 },
...@@ -684,8 +684,8 @@ pub const Tokenizer = struct {...@@ -684,8 +684,8 @@ pub const Tokenizer = struct {
684 .identifier => switch (c) {684 .identifier => switch (c) {
685 'a'...'z', 'A'...'Z', '_', '0'...'9' => {},685 'a'...'z', 'A'...'Z', '_', '0'...'9' => {},
686 else => {686 else => {
687 if (Token.getKeyword(self.buffer[result.loc.start..self.index])) |id| {687 if (Token.getKeyword(self.buffer[result.loc.start..self.index])) |tag| {
688 result.id = id;688 result.tag = tag;
689 }689 }
690 break;690 break;
691 },691 },
...@@ -724,7 +724,7 @@ pub const Tokenizer = struct {...@@ -724,7 +724,7 @@ pub const Tokenizer = struct {
724 state = .char_literal_backslash;724 state = .char_literal_backslash;
725 },725 },
726 '\'', 0x80...0xbf, 0xf8...0xff => {726 '\'', 0x80...0xbf, 0xf8...0xff => {
727 result.id = .Invalid;727 result.tag = .invalid;
728 break;728 break;
729 },729 },
730 0xc0...0xdf => { // 110xxxxx730 0xc0...0xdf => { // 110xxxxx
...@@ -746,7 +746,7 @@ pub const Tokenizer = struct {...@@ -746,7 +746,7 @@ pub const Tokenizer = struct {
746746
747 .char_literal_backslash => switch (c) {747 .char_literal_backslash => switch (c) {
748 '\n' => {748 '\n' => {
749 result.id = .Invalid;749 result.tag = .invalid;
750 break;750 break;
751 },751 },
752 'x' => {752 'x' => {
...@@ -769,7 +769,7 @@ pub const Tokenizer = struct {...@@ -769,7 +769,7 @@ pub const Tokenizer = struct {
769 }769 }
770 },770 },
771 else => {771 else => {
772 result.id = .Invalid;772 result.tag = .invalid;
773 break;773 break;
774 },774 },
775 },775 },
...@@ -780,7 +780,7 @@ pub const Tokenizer = struct {...@@ -780,7 +780,7 @@ pub const Tokenizer = struct {
780 seen_escape_digits = 0;780 seen_escape_digits = 0;
781 },781 },
782 else => {782 else => {
783 result.id = .Invalid;783 result.tag = .invalid;
784 state = .char_literal_unicode_invalid;784 state = .char_literal_unicode_invalid;
785 },785 },
786 },786 },
...@@ -791,14 +791,14 @@ pub const Tokenizer = struct {...@@ -791,14 +791,14 @@ pub const Tokenizer = struct {
791 },791 },
792 '}' => {792 '}' => {
793 if (seen_escape_digits == 0) {793 if (seen_escape_digits == 0) {
794 result.id = .Invalid;794 result.tag = .invalid;
795 state = .char_literal_unicode_invalid;795 state = .char_literal_unicode_invalid;
796 } else {796 } else {
797 state = .char_literal_end;797 state = .char_literal_end;
798 }798 }
799 },799 },
800 else => {800 else => {
801 result.id = .Invalid;801 result.tag = .invalid;
802 state = .char_literal_unicode_invalid;802 state = .char_literal_unicode_invalid;
803 },803 },
804 },804 },
...@@ -813,12 +813,12 @@ pub const Tokenizer = struct {...@@ -813,12 +813,12 @@ pub const Tokenizer = struct {
813813
814 .char_literal_end => switch (c) {814 .char_literal_end => switch (c) {
815 '\'' => {815 '\'' => {
816 result.id = .CharLiteral;816 result.tag = .char_literal;
817 self.index += 1;817 self.index += 1;
818 break;818 break;
819 },819 },
820 else => {820 else => {
821 result.id = .Invalid;821 result.tag = .invalid;
822 break;822 break;
823 },823 },
824 },824 },
...@@ -831,7 +831,7 @@ pub const Tokenizer = struct {...@@ -831,7 +831,7 @@ pub const Tokenizer = struct {
831 }831 }
832 },832 },
833 else => {833 else => {
834 result.id = .Invalid;834 result.tag = .invalid;
835 break;835 break;
836 },836 },
837 },837 },
...@@ -847,58 +847,58 @@ pub const Tokenizer = struct {...@@ -847,58 +847,58 @@ pub const Tokenizer = struct {
847847
848 .bang => switch (c) {848 .bang => switch (c) {
849 '=' => {849 '=' => {
850 result.id = .BangEqual;850 result.tag = .bang_equal;
851 self.index += 1;851 self.index += 1;
852 break;852 break;
853 },853 },
854 else => {854 else => {
855 result.id = .Bang;855 result.tag = .bang;
856 break;856 break;
857 },857 },
858 },858 },
859859
860 .pipe => switch (c) {860 .pipe => switch (c) {
861 '=' => {861 '=' => {
862 result.id = .PipeEqual;862 result.tag = .pipe_equal;
863 self.index += 1;863 self.index += 1;
864 break;864 break;
865 },865 },
866 '|' => {866 '|' => {
867 result.id = .PipePipe;867 result.tag = .pipe_pipe;
868 self.index += 1;868 self.index += 1;
869 break;869 break;
870 },870 },
871 else => {871 else => {
872 result.id = .Pipe;872 result.tag = .pipe;
873 break;873 break;
874 },874 },
875 },875 },
876876
877 .equal => switch (c) {877 .equal => switch (c) {
878 '=' => {878 '=' => {
879 result.id = .EqualEqual;879 result.tag = .equal_equal;
880 self.index += 1;880 self.index += 1;
881 break;881 break;
882 },882 },
883 '>' => {883 '>' => {
884 result.id = .EqualAngleBracketRight;884 result.tag = .equal_angle_bracket_right;
885 self.index += 1;885 self.index += 1;
886 break;886 break;
887 },887 },
888 else => {888 else => {
889 result.id = .Equal;889 result.tag = .equal;
890 break;890 break;
891 },891 },
892 },892 },
893893
894 .minus => switch (c) {894 .minus => switch (c) {
895 '>' => {895 '>' => {
896 result.id = .Arrow;896 result.tag = .arrow;
897 self.index += 1;897 self.index += 1;
898 break;898 break;
899 },899 },
900 '=' => {900 '=' => {
901 result.id = .MinusEqual;901 result.tag = .minus_equal;
902 self.index += 1;902 self.index += 1;
903 break;903 break;
904 },904 },
...@@ -906,19 +906,19 @@ pub const Tokenizer = struct {...@@ -906,19 +906,19 @@ pub const Tokenizer = struct {
906 state = .minus_percent;906 state = .minus_percent;
907 },907 },
908 else => {908 else => {
909 result.id = .Minus;909 result.tag = .minus;
910 break;910 break;
911 },911 },
912 },912 },
913913
914 .minus_percent => switch (c) {914 .minus_percent => switch (c) {
915 '=' => {915 '=' => {
916 result.id = .MinusPercentEqual;916 result.tag = .minus_percent_equal;
917 self.index += 1;917 self.index += 1;
918 break;918 break;
919 },919 },
920 else => {920 else => {
921 result.id = .MinusPercent;921 result.tag = .minus_percent;
922 break;922 break;
923 },923 },
924 },924 },
...@@ -928,24 +928,24 @@ pub const Tokenizer = struct {...@@ -928,24 +928,24 @@ pub const Tokenizer = struct {
928 state = .angle_bracket_angle_bracket_left;928 state = .angle_bracket_angle_bracket_left;
929 },929 },
930 '=' => {930 '=' => {
931 result.id = .AngleBracketLeftEqual;931 result.tag = .angle_bracket_left_equal;
932 self.index += 1;932 self.index += 1;
933 break;933 break;
934 },934 },
935 else => {935 else => {
936 result.id = .AngleBracketLeft;936 result.tag = .angle_bracket_left;
937 break;937 break;
938 },938 },
939 },939 },
940940
941 .angle_bracket_angle_bracket_left => switch (c) {941 .angle_bracket_angle_bracket_left => switch (c) {
942 '=' => {942 '=' => {
943 result.id = .AngleBracketAngleBracketLeftEqual;943 result.tag = .angle_bracket_angle_bracket_left_equal;
944 self.index += 1;944 self.index += 1;
945 break;945 break;
946 },946 },
947 else => {947 else => {
948 result.id = .AngleBracketAngleBracketLeft;948 result.tag = .angle_bracket_angle_bracket_left;
949 break;949 break;
950 },950 },
951 },951 },
...@@ -955,24 +955,24 @@ pub const Tokenizer = struct {...@@ -955,24 +955,24 @@ pub const Tokenizer = struct {
955 state = .angle_bracket_angle_bracket_right;955 state = .angle_bracket_angle_bracket_right;
956 },956 },
957 '=' => {957 '=' => {
958 result.id = .AngleBracketRightEqual;958 result.tag = .angle_bracket_right_equal;
959 self.index += 1;959 self.index += 1;
960 break;960 break;
961 },961 },
962 else => {962 else => {
963 result.id = .AngleBracketRight;963 result.tag = .angle_bracket_right;
964 break;964 break;
965 },965 },
966 },966 },
967967
968 .angle_bracket_angle_bracket_right => switch (c) {968 .angle_bracket_angle_bracket_right => switch (c) {
969 '=' => {969 '=' => {
970 result.id = .AngleBracketAngleBracketRightEqual;970 result.tag = .angle_bracket_angle_bracket_right_equal;
971 self.index += 1;971 self.index += 1;
972 break;972 break;
973 },973 },
974 else => {974 else => {
975 result.id = .AngleBracketAngleBracketRight;975 result.tag = .angle_bracket_angle_bracket_right;
976 break;976 break;
977 },977 },
978 },978 },
...@@ -985,30 +985,30 @@ pub const Tokenizer = struct {...@@ -985,30 +985,30 @@ pub const Tokenizer = struct {
985 state = .period_asterisk;985 state = .period_asterisk;
986 },986 },
987 else => {987 else => {
988 result.id = .Period;988 result.tag = .period;
989 break;989 break;
990 },990 },
991 },991 },
992992
993 .period_2 => switch (c) {993 .period_2 => switch (c) {
994 '.' => {994 '.' => {
995 result.id = .Ellipsis3;995 result.tag = .ellipsis3;
996 self.index += 1;996 self.index += 1;
997 break;997 break;
998 },998 },
999 else => {999 else => {
1000 result.id = .Ellipsis2;1000 result.tag = .ellipsis2;
1001 break;1001 break;
1002 },1002 },
1003 },1003 },
10041004
1005 .period_asterisk => switch (c) {1005 .period_asterisk => switch (c) {
1006 '*' => {1006 '*' => {
1007 result.id = .Invalid_periodasterisks;1007 result.tag = .invalid_periodasterisks;
1008 break;1008 break;
1009 },1009 },
1010 else => {1010 else => {
1011 result.id = .PeriodAsterisk;1011 result.tag = .period_asterisk;
1012 break;1012 break;
1013 },1013 },
1014 },1014 },
...@@ -1016,15 +1016,14 @@ pub const Tokenizer = struct {...@@ -1016,15 +1016,14 @@ pub const Tokenizer = struct {
1016 .slash => switch (c) {1016 .slash => switch (c) {
1017 '/' => {1017 '/' => {
1018 state = .line_comment_start;1018 state = .line_comment_start;
1019 result.id = .LineComment;
1020 },1019 },
1021 '=' => {1020 '=' => {
1022 result.id = .SlashEqual;1021 result.tag = .slash_equal;
1023 self.index += 1;1022 self.index += 1;
1024 break;1023 break;
1025 },1024 },
1026 else => {1025 else => {
1027 result.id = .Slash;1026 result.tag = .slash;
1028 break;1027 break;
1029 },1028 },
1030 },1029 },
...@@ -1033,10 +1032,13 @@ pub const Tokenizer = struct {...@@ -1033,10 +1032,13 @@ pub const Tokenizer = struct {
1033 state = .doc_comment_start;1032 state = .doc_comment_start;
1034 },1033 },
1035 '!' => {1034 '!' => {
1036 result.id = .ContainerDocComment;1035 result.tag = .container_doc_comment;
1037 state = .container_doc_comment;1036 state = .container_doc_comment;
1038 },1037 },
1039 '\n' => break,1038 '\n' => {
1039 state = .start;
1040 result.loc.start = self.index + 1;
1041 },
1040 '\t', '\r' => state = .line_comment,1042 '\t', '\r' => state = .line_comment,
1041 else => {1043 else => {
1042 state = .line_comment;1044 state = .line_comment;
...@@ -1048,20 +1050,28 @@ pub const Tokenizer = struct {...@@ -1048,20 +1050,28 @@ pub const Tokenizer = struct {
1048 state = .line_comment;1050 state = .line_comment;
1049 },1051 },
1050 '\n' => {1052 '\n' => {
1051 result.id = .DocComment;1053 result.tag = .doc_comment;
1052 break;1054 break;
1053 },1055 },
1054 '\t', '\r' => {1056 '\t', '\r' => {
1055 state = .doc_comment;1057 state = .doc_comment;
1056 result.id = .DocComment;1058 result.tag = .doc_comment;
1057 },1059 },
1058 else => {1060 else => {
1059 state = .doc_comment;1061 state = .doc_comment;
1060 result.id = .DocComment;1062 result.tag = .doc_comment;
1061 self.checkLiteralCharacter();1063 self.checkLiteralCharacter();
1062 },1064 },
1063 },1065 },
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) {
1065 '\n' => break,1075 '\n' => break,
1066 '\t', '\r' => {},1076 '\t', '\r' => {},
1067 else => self.checkLiteralCharacter(),1077 else => self.checkLiteralCharacter(),
...@@ -1083,7 +1093,7 @@ pub const Tokenizer = struct {...@@ -1083,7 +1093,7 @@ pub const Tokenizer = struct {
1083 },1093 },
1084 else => {1094 else => {
1085 if (isIdentifierChar(c)) {1095 if (isIdentifierChar(c)) {
1086 result.id = .Invalid;1096 result.tag = .invalid;
1087 }1097 }
1088 break;1098 break;
1089 },1099 },
...@@ -1093,7 +1103,7 @@ pub const Tokenizer = struct {...@@ -1093,7 +1103,7 @@ pub const Tokenizer = struct {
1093 state = .int_literal_bin;1103 state = .int_literal_bin;
1094 },1104 },
1095 else => {1105 else => {
1096 result.id = .Invalid;1106 result.tag = .invalid;
1097 break;1107 break;
1098 },1108 },
1099 },1109 },
...@@ -1104,7 +1114,7 @@ pub const Tokenizer = struct {...@@ -1104,7 +1114,7 @@ pub const Tokenizer = struct {
1104 '0'...'1' => {},1114 '0'...'1' => {},
1105 else => {1115 else => {
1106 if (isIdentifierChar(c)) {1116 if (isIdentifierChar(c)) {
1107 result.id = .Invalid;1117 result.tag = .invalid;
1108 }1118 }
1109 break;1119 break;
1110 },1120 },
...@@ -1114,7 +1124,7 @@ pub const Tokenizer = struct {...@@ -1114,7 +1124,7 @@ pub const Tokenizer = struct {
1114 state = .int_literal_oct;1124 state = .int_literal_oct;
1115 },1125 },
1116 else => {1126 else => {
1117 result.id = .Invalid;1127 result.tag = .invalid;
1118 break;1128 break;
1119 },1129 },
1120 },1130 },
...@@ -1125,7 +1135,7 @@ pub const Tokenizer = struct {...@@ -1125,7 +1135,7 @@ pub const Tokenizer = struct {
1125 '0'...'7' => {},1135 '0'...'7' => {},
1126 else => {1136 else => {
1127 if (isIdentifierChar(c)) {1137 if (isIdentifierChar(c)) {
1128 result.id = .Invalid;1138 result.tag = .invalid;
1129 }1139 }
1130 break;1140 break;
1131 },1141 },
...@@ -1135,7 +1145,7 @@ pub const Tokenizer = struct {...@@ -1135,7 +1145,7 @@ pub const Tokenizer = struct {
1135 state = .int_literal_dec;1145 state = .int_literal_dec;
1136 },1146 },
1137 else => {1147 else => {
1138 result.id = .Invalid;1148 result.tag = .invalid;
1139 break;1149 break;
1140 },1150 },
1141 },1151 },
...@@ -1145,16 +1155,16 @@ pub const Tokenizer = struct {...@@ -1145,16 +1155,16 @@ pub const Tokenizer = struct {
1145 },1155 },
1146 '.' => {1156 '.' => {
1147 state = .num_dot_dec;1157 state = .num_dot_dec;
1148 result.id = .FloatLiteral;1158 result.tag = .float_literal;
1149 },1159 },
1150 'e', 'E' => {1160 'e', 'E' => {
1151 state = .float_exponent_unsigned;1161 state = .float_exponent_unsigned;
1152 result.id = .FloatLiteral;1162 result.tag = .float_literal;
1153 },1163 },
1154 '0'...'9' => {},1164 '0'...'9' => {},
1155 else => {1165 else => {
1156 if (isIdentifierChar(c)) {1166 if (isIdentifierChar(c)) {
1157 result.id = .Invalid;1167 result.tag = .invalid;
1158 }1168 }
1159 break;1169 break;
1160 },1170 },
...@@ -1164,7 +1174,7 @@ pub const Tokenizer = struct {...@@ -1164,7 +1174,7 @@ pub const Tokenizer = struct {
1164 state = .int_literal_hex;1174 state = .int_literal_hex;
1165 },1175 },
1166 else => {1176 else => {
1167 result.id = .Invalid;1177 result.tag = .invalid;
1168 break;1178 break;
1169 },1179 },
1170 },1180 },
...@@ -1174,23 +1184,23 @@ pub const Tokenizer = struct {...@@ -1174,23 +1184,23 @@ pub const Tokenizer = struct {
1174 },1184 },
1175 '.' => {1185 '.' => {
1176 state = .num_dot_hex;1186 state = .num_dot_hex;
1177 result.id = .FloatLiteral;1187 result.tag = .float_literal;
1178 },1188 },
1179 'p', 'P' => {1189 'p', 'P' => {
1180 state = .float_exponent_unsigned;1190 state = .float_exponent_unsigned;
1181 result.id = .FloatLiteral;1191 result.tag = .float_literal;
1182 },1192 },
1183 '0'...'9', 'a'...'f', 'A'...'F' => {},1193 '0'...'9', 'a'...'f', 'A'...'F' => {},
1184 else => {1194 else => {
1185 if (isIdentifierChar(c)) {1195 if (isIdentifierChar(c)) {
1186 result.id = .Invalid;1196 result.tag = .invalid;
1187 }1197 }
1188 break;1198 break;
1189 },1199 },
1190 },1200 },
1191 .num_dot_dec => switch (c) {1201 .num_dot_dec => switch (c) {
1192 '.' => {1202 '.' => {
1193 result.id = .IntegerLiteral;1203 result.tag = .integer_literal;
1194 self.index -= 1;1204 self.index -= 1;
1195 state = .start;1205 state = .start;
1196 break;1206 break;
...@@ -1203,14 +1213,14 @@ pub const Tokenizer = struct {...@@ -1203,14 +1213,14 @@ pub const Tokenizer = struct {
1203 },1213 },
1204 else => {1214 else => {
1205 if (isIdentifierChar(c)) {1215 if (isIdentifierChar(c)) {
1206 result.id = .Invalid;1216 result.tag = .invalid;
1207 }1217 }
1208 break;1218 break;
1209 },1219 },
1210 },1220 },
1211 .num_dot_hex => switch (c) {1221 .num_dot_hex => switch (c) {
1212 '.' => {1222 '.' => {
1213 result.id = .IntegerLiteral;1223 result.tag = .integer_literal;
1214 self.index -= 1;1224 self.index -= 1;
1215 state = .start;1225 state = .start;
1216 break;1226 break;
...@@ -1219,12 +1229,12 @@ pub const Tokenizer = struct {...@@ -1219,12 +1229,12 @@ pub const Tokenizer = struct {
1219 state = .float_exponent_unsigned;1229 state = .float_exponent_unsigned;
1220 },1230 },
1221 '0'...'9', 'a'...'f', 'A'...'F' => {1231 '0'...'9', 'a'...'f', 'A'...'F' => {
1222 result.id = .FloatLiteral;1232 result.tag = .float_literal;
1223 state = .float_fraction_hex;1233 state = .float_fraction_hex;
1224 },1234 },
1225 else => {1235 else => {
1226 if (isIdentifierChar(c)) {1236 if (isIdentifierChar(c)) {
1227 result.id = .Invalid;1237 result.tag = .invalid;
1228 }1238 }
1229 break;1239 break;
1230 },1240 },
...@@ -1234,7 +1244,7 @@ pub const Tokenizer = struct {...@@ -1234,7 +1244,7 @@ pub const Tokenizer = struct {
1234 state = .float_fraction_dec;1244 state = .float_fraction_dec;
1235 },1245 },
1236 else => {1246 else => {
1237 result.id = .Invalid;1247 result.tag = .invalid;
1238 break;1248 break;
1239 },1249 },
1240 },1250 },
...@@ -1248,7 +1258,7 @@ pub const Tokenizer = struct {...@@ -1248,7 +1258,7 @@ pub const Tokenizer = struct {
1248 '0'...'9' => {},1258 '0'...'9' => {},
1249 else => {1259 else => {
1250 if (isIdentifierChar(c)) {1260 if (isIdentifierChar(c)) {
1251 result.id = .Invalid;1261 result.tag = .invalid;
1252 }1262 }
1253 break;1263 break;
1254 },1264 },
...@@ -1258,7 +1268,7 @@ pub const Tokenizer = struct {...@@ -1258,7 +1268,7 @@ pub const Tokenizer = struct {
1258 state = .float_fraction_hex;1268 state = .float_fraction_hex;
1259 },1269 },
1260 else => {1270 else => {
1261 result.id = .Invalid;1271 result.tag = .invalid;
1262 break;1272 break;
1263 },1273 },
1264 },1274 },
...@@ -1272,7 +1282,7 @@ pub const Tokenizer = struct {...@@ -1272,7 +1282,7 @@ pub const Tokenizer = struct {
1272 '0'...'9', 'a'...'f', 'A'...'F' => {},1282 '0'...'9', 'a'...'f', 'A'...'F' => {},
1273 else => {1283 else => {
1274 if (isIdentifierChar(c)) {1284 if (isIdentifierChar(c)) {
1275 result.id = .Invalid;1285 result.tag = .invalid;
1276 }1286 }
1277 break;1287 break;
1278 },1288 },
...@@ -1292,7 +1302,7 @@ pub const Tokenizer = struct {...@@ -1292,7 +1302,7 @@ pub const Tokenizer = struct {
1292 state = .float_exponent_num;1302 state = .float_exponent_num;
1293 },1303 },
1294 else => {1304 else => {
1295 result.id = .Invalid;1305 result.tag = .invalid;
1296 break;1306 break;
1297 },1307 },
1298 },1308 },
...@@ -1303,7 +1313,7 @@ pub const Tokenizer = struct {...@@ -1303,7 +1313,7 @@ pub const Tokenizer = struct {
1303 '0'...'9' => {},1313 '0'...'9' => {},
1304 else => {1314 else => {
1305 if (isIdentifierChar(c)) {1315 if (isIdentifierChar(c)) {
1306 result.id = .Invalid;1316 result.tag = .invalid;
1307 }1317 }
1308 break;1318 break;
1309 },1319 },
...@@ -1324,21 +1334,20 @@ pub const Tokenizer = struct {...@@ -1324,21 +1334,20 @@ pub const Tokenizer = struct {
1324 .string_literal, // find this error later1334 .string_literal, // find this error later
1325 .multiline_string_literal_line,1335 .multiline_string_literal_line,
1326 .builtin,1336 .builtin,
1337 .line_comment,
1338 .line_comment_start,
1327 => {},1339 => {},
13281340
1329 .identifier => {1341 .identifier => {
1330 if (Token.getKeyword(self.buffer[result.loc.start..self.index])) |id| {1342 if (Token.getKeyword(self.buffer[result.loc.start..self.index])) |tag| {
1331 result.id = id;1343 result.tag = tag;
1332 }1344 }
1333 },1345 },
1334 .line_comment, .line_comment_start => {
1335 result.id = .LineComment;
1336 },
1337 .doc_comment, .doc_comment_start => {1346 .doc_comment, .doc_comment_start => {
1338 result.id = .DocComment;1347 result.tag = .doc_comment;
1339 },1348 },
1340 .container_doc_comment => {1349 .container_doc_comment => {
1341 result.id = .ContainerDocComment;1350 result.tag = .container_doc_comment;
1342 },1351 },
13431352
1344 .int_literal_dec_no_underscore,1353 .int_literal_dec_no_underscore,
...@@ -1361,80 +1370,81 @@ pub const Tokenizer = struct {...@@ -1361,80 +1370,81 @@ pub const Tokenizer = struct {
1361 .char_literal_unicode,1370 .char_literal_unicode,
1362 .string_literal_backslash,1371 .string_literal_backslash,
1363 => {1372 => {
1364 result.id = .Invalid;1373 result.tag = .invalid;
1365 },1374 },
13661375
1367 .equal => {1376 .equal => {
1368 result.id = .Equal;1377 result.tag = .equal;
1369 },1378 },
1370 .bang => {1379 .bang => {
1371 result.id = .Bang;1380 result.tag = .bang;
1372 },1381 },
1373 .minus => {1382 .minus => {
1374 result.id = .Minus;1383 result.tag = .minus;
1375 },1384 },
1376 .slash => {1385 .slash => {
1377 result.id = .Slash;1386 result.tag = .slash;
1378 },1387 },
1379 .zero => {1388 .zero => {
1380 result.id = .IntegerLiteral;1389 result.tag = .integer_literal;
1381 },1390 },
1382 .ampersand => {1391 .ampersand => {
1383 result.id = .Ampersand;1392 result.tag = .ampersand;
1384 },1393 },
1385 .period => {1394 .period => {
1386 result.id = .Period;1395 result.tag = .period;
1387 },1396 },
1388 .period_2 => {1397 .period_2 => {
1389 result.id = .Ellipsis2;1398 result.tag = .ellipsis2;
1390 },1399 },
1391 .period_asterisk => {1400 .period_asterisk => {
1392 result.id = .PeriodAsterisk;1401 result.tag = .period_asterisk;
1393 },1402 },
1394 .pipe => {1403 .pipe => {
1395 result.id = .Pipe;1404 result.tag = .pipe;
1396 },1405 },
1397 .angle_bracket_angle_bracket_right => {1406 .angle_bracket_angle_bracket_right => {
1398 result.id = .AngleBracketAngleBracketRight;1407 result.tag = .angle_bracket_angle_bracket_right;
1399 },1408 },
1400 .angle_bracket_right => {1409 .angle_bracket_right => {
1401 result.id = .AngleBracketRight;1410 result.tag = .angle_bracket_right;
1402 },1411 },
1403 .angle_bracket_angle_bracket_left => {1412 .angle_bracket_angle_bracket_left => {
1404 result.id = .AngleBracketAngleBracketLeft;1413 result.tag = .angle_bracket_angle_bracket_left;
1405 },1414 },
1406 .angle_bracket_left => {1415 .angle_bracket_left => {
1407 result.id = .AngleBracketLeft;1416 result.tag = .angle_bracket_left;
1408 },1417 },
1409 .plus_percent => {1418 .plus_percent => {
1410 result.id = .PlusPercent;1419 result.tag = .plus_percent;
1411 },1420 },
1412 .plus => {1421 .plus => {
1413 result.id = .Plus;1422 result.tag = .plus;
1414 },1423 },
1415 .percent => {1424 .percent => {
1416 result.id = .Percent;1425 result.tag = .percent;
1417 },1426 },
1418 .caret => {1427 .caret => {
1419 result.id = .Caret;1428 result.tag = .caret;
1420 },1429 },
1421 .asterisk_percent => {1430 .asterisk_percent => {
1422 result.id = .AsteriskPercent;1431 result.tag = .asterisk_percent;
1423 },1432 },
1424 .asterisk => {1433 .asterisk => {
1425 result.id = .Asterisk;1434 result.tag = .asterisk;
1426 },1435 },
1427 .minus_percent => {1436 .minus_percent => {
1428 result.id = .MinusPercent;1437 result.tag = .minus_percent;
1429 },1438 },
1430 }1439 }
1431 }1440 }
14321441
1433 if (result.id == .Eof) {1442 if (result.tag == .eof) {
1434 if (self.pending_invalid_token) |token| {1443 if (self.pending_invalid_token) |token| {
1435 self.pending_invalid_token = null;1444 self.pending_invalid_token = null;
1436 return token;1445 return token;
1437 }1446 }
1447 result.loc.start = self.index;
1438 }1448 }
14391449
1440 result.loc.end = self.index;1450 result.loc.end = self.index;
...@@ -1446,7 +1456,7 @@ pub const Tokenizer = struct {...@@ -1446,7 +1456,7 @@ pub const Tokenizer = struct {
1446 const invalid_length = self.getInvalidCharacterLength();1456 const invalid_length = self.getInvalidCharacterLength();
1447 if (invalid_length == 0) return;1457 if (invalid_length == 0) return;
1448 self.pending_invalid_token = .{1458 self.pending_invalid_token = .{
1449 .id = .Invalid,1459 .tag = .invalid,
1450 .loc = .{1460 .loc = .{
1451 .start = self.index,1461 .start = self.index,
1452 .end = self.index + invalid_length,1462 .end = self.index + invalid_length,
...@@ -1493,220 +1503,218 @@ pub const Tokenizer = struct {...@@ -1493,220 +1503,218 @@ pub const Tokenizer = struct {
1493};1503};
14941504
1495test "tokenizer" {1505test "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 });
1497}1519}
14981520
1499test "tokenizer - unknown length pointer and then c pointer" {1521test "tokenizer - unknown length pointer and then c pointer" {
1500 testTokenize(1522 testTokenize(
1501 \\[*]u81523 \\[*]u8
1502 \\[*c]u81524 \\[*c]u8
1503 , &[_]Token.Id{1525 , &.{
1504 .LBracket,1526 .l_bracket,
1505 .Asterisk,1527 .asterisk,
1506 .RBracket,1528 .r_bracket,
1507 .Identifier,1529 .identifier,
1508 .LBracket,1530 .l_bracket,
1509 .Asterisk,1531 .asterisk,
1510 .Identifier,1532 .identifier,
1511 .RBracket,1533 .r_bracket,
1512 .Identifier,1534 .identifier,
1513 });1535 });
1514}1536}
15151537
1516test "tokenizer - code point literal with hex escape" {1538test "tokenizer - code point literal with hex escape" {
1517 testTokenize(1539 testTokenize(
1518 \\'\x1b'1540 \\'\x1b'
1519 , &[_]Token.Id{.CharLiteral});1541 , &.{.char_literal});
1520 testTokenize(1542 testTokenize(
1521 \\'\x1'1543 \\'\x1'
1522 , &[_]Token.Id{ .Invalid, .Invalid });1544 , &.{ .invalid, .invalid });
1523}1545}
15241546
1525test "tokenizer - code point literal with unicode escapes" {1547test "tokenizer - code point literal with unicode escapes" {
1526 // Valid unicode escapes1548 // Valid unicode escapes
1527 testTokenize(1549 testTokenize(
1528 \\'\u{3}'1550 \\'\u{3}'
1529 , &[_]Token.Id{.CharLiteral});1551 , &.{.char_literal});
1530 testTokenize(1552 testTokenize(
1531 \\'\u{01}'1553 \\'\u{01}'
1532 , &[_]Token.Id{.CharLiteral});1554 , &.{.char_literal});
1533 testTokenize(1555 testTokenize(
1534 \\'\u{2a}'1556 \\'\u{2a}'
1535 , &[_]Token.Id{.CharLiteral});1557 , &.{.char_literal});
1536 testTokenize(1558 testTokenize(
1537 \\'\u{3f9}'1559 \\'\u{3f9}'
1538 , &[_]Token.Id{.CharLiteral});1560 , &.{.char_literal});
1539 testTokenize(1561 testTokenize(
1540 \\'\u{6E09aBc1523}'1562 \\'\u{6E09aBc1523}'
1541 , &[_]Token.Id{.CharLiteral});1563 , &.{.char_literal});
1542 testTokenize(1564 testTokenize(
1543 \\"\u{440}"1565 \\"\u{440}"
1544 , &[_]Token.Id{.StringLiteral});1566 , &.{.string_literal});
15451567
1546 // Invalid unicode escapes1568 // Invalid unicode escapes
1547 testTokenize(1569 testTokenize(
1548 \\'\u'1570 \\'\u'
1549 , &[_]Token.Id{.Invalid});1571 , &.{.invalid});
1550 testTokenize(1572 testTokenize(
1551 \\'\u{{'1573 \\'\u{{'
1552 , &[_]Token.Id{ .Invalid, .Invalid });1574 , &.{ .invalid, .invalid });
1553 testTokenize(1575 testTokenize(
1554 \\'\u{}'1576 \\'\u{}'
1555 , &[_]Token.Id{ .Invalid, .Invalid });1577 , &.{ .invalid, .invalid });
1556 testTokenize(1578 testTokenize(
1557 \\'\u{s}'1579 \\'\u{s}'
1558 , &[_]Token.Id{ .Invalid, .Invalid });1580 , &.{ .invalid, .invalid });
1559 testTokenize(1581 testTokenize(
1560 \\'\u{2z}'1582 \\'\u{2z}'
1561 , &[_]Token.Id{ .Invalid, .Invalid });1583 , &.{ .invalid, .invalid });
1562 testTokenize(1584 testTokenize(
1563 \\'\u{4a'1585 \\'\u{4a'
1564 , &[_]Token.Id{.Invalid});1586 , &.{.invalid});
15651587
1566 // Test old-style unicode literals1588 // Test old-style unicode literals
1567 testTokenize(1589 testTokenize(
1568 \\'\u0333'1590 \\'\u0333'
1569 , &[_]Token.Id{ .Invalid, .Invalid });1591 , &.{ .invalid, .invalid });
1570 testTokenize(1592 testTokenize(
1571 \\'\U0333'1593 \\'\U0333'
1572 , &[_]Token.Id{ .Invalid, .IntegerLiteral, .Invalid });1594 , &.{ .invalid, .integer_literal, .invalid });
1573}1595}
15741596
1575test "tokenizer - code point literal with unicode code point" {1597test "tokenizer - code point literal with unicode code point" {
1576 testTokenize(1598 testTokenize(
1577 \\'💩'1599 \\'💩'
1578 , &[_]Token.Id{.CharLiteral});1600 , &.{.char_literal});
1579}1601}
15801602
1581test "tokenizer - float literal e exponent" {1603test "tokenizer - float literal e exponent" {
1582 testTokenize("a = 4.94065645841246544177e-324;\n", &[_]Token.Id{1604 testTokenize("a = 4.94065645841246544177e-324;\n", &.{
1583 .Identifier,1605 .identifier,
1584 .Equal,1606 .equal,
1585 .FloatLiteral,1607 .float_literal,
1586 .Semicolon,1608 .semicolon,
1587 });1609 });
1588}1610}
15891611
1590test "tokenizer - float literal p exponent" {1612test "tokenizer - float literal p exponent" {
1591 testTokenize("a = 0x1.a827999fcef32p+1022;\n", &[_]Token.Id{1613 testTokenize("a = 0x1.a827999fcef32p+1022;\n", &.{
1592 .Identifier,1614 .identifier,
1593 .Equal,1615 .equal,
1594 .FloatLiteral,1616 .float_literal,
1595 .Semicolon,1617 .semicolon,
1596 });1618 });
1597}1619}
15981620
1599test "tokenizer - chars" {1621test "tokenizer - chars" {
1600 testTokenize("'c'", &[_]Token.Id{.CharLiteral});1622 testTokenize("'c'", &.{.char_literal});
1601}1623}
16021624
1603test "tokenizer - invalid token characters" {1625test "tokenizer - invalid token characters" {
1604 testTokenize("#", &[_]Token.Id{.Invalid});1626 testTokenize("#", &.{.invalid});
1605 testTokenize("`", &[_]Token.Id{.Invalid});1627 testTokenize("`", &.{.invalid});
1606 testTokenize("'c", &[_]Token.Id{.Invalid});1628 testTokenize("'c", &.{.invalid});
1607 testTokenize("'", &[_]Token.Id{.Invalid});1629 testTokenize("'", &.{.invalid});
1608 testTokenize("''", &[_]Token.Id{ .Invalid, .Invalid });1630 testTokenize("''", &.{ .invalid, .invalid });
1609}1631}
16101632
1611test "tokenizer - invalid literal/comment characters" {1633test "tokenizer - invalid literal/comment characters" {
1612 testTokenize("\"\x00\"", &[_]Token.Id{1634 testTokenize("\"\x00\"", &.{
1613 .StringLiteral,1635 .string_literal,
1614 .Invalid,1636 .invalid,
1615 });1637 });
1616 testTokenize("//\x00", &[_]Token.Id{1638 testTokenize("//\x00", &.{
1617 .LineComment,1639 .invalid,
1618 .Invalid,
1619 });1640 });
1620 testTokenize("//\x1f", &[_]Token.Id{1641 testTokenize("//\x1f", &.{
1621 .LineComment,1642 .invalid,
1622 .Invalid,
1623 });1643 });
1624 testTokenize("//\x7f", &[_]Token.Id{1644 testTokenize("//\x7f", &.{
1625 .LineComment,1645 .invalid,
1626 .Invalid,
1627 });1646 });
1628}1647}
16291648
1630test "tokenizer - utf8" {1649test "tokenizer - utf8" {
1631 testTokenize("//\xc2\x80", &[_]Token.Id{.LineComment});1650 testTokenize("//\xc2\x80", &.{});
1632 testTokenize("//\xf4\x8f\xbf\xbf", &[_]Token.Id{.LineComment});1651 testTokenize("//\xf4\x8f\xbf\xbf", &.{});
1633}1652}
16341653
1635test "tokenizer - invalid utf8" {1654test "tokenizer - invalid utf8" {
1636 testTokenize("//\x80", &[_]Token.Id{1655 testTokenize("//\x80", &.{
1637 .LineComment,1656 .invalid,
1638 .Invalid,
1639 });1657 });
1640 testTokenize("//\xbf", &[_]Token.Id{1658 testTokenize("//\xbf", &.{
1641 .LineComment,1659 .invalid,
1642 .Invalid,
1643 });1660 });
1644 testTokenize("//\xf8", &[_]Token.Id{1661 testTokenize("//\xf8", &.{
1645 .LineComment,1662 .invalid,
1646 .Invalid,
1647 });1663 });
1648 testTokenize("//\xff", &[_]Token.Id{1664 testTokenize("//\xff", &.{
1649 .LineComment,1665 .invalid,
1650 .Invalid,
1651 });1666 });
1652 testTokenize("//\xc2\xc0", &[_]Token.Id{1667 testTokenize("//\xc2\xc0", &.{
1653 .LineComment,1668 .invalid,
1654 .Invalid,
1655 });1669 });
1656 testTokenize("//\xe0", &[_]Token.Id{1670 testTokenize("//\xe0", &.{
1657 .LineComment,1671 .invalid,
1658 .Invalid,
1659 });1672 });
1660 testTokenize("//\xf0", &[_]Token.Id{1673 testTokenize("//\xf0", &.{
1661 .LineComment,1674 .invalid,
1662 .Invalid,
1663 });1675 });
1664 testTokenize("//\xf0\x90\x80\xc0", &[_]Token.Id{1676 testTokenize("//\xf0\x90\x80\xc0", &.{
1665 .LineComment,1677 .invalid,
1666 .Invalid,
1667 });1678 });
1668}1679}
16691680
1670test "tokenizer - illegal unicode codepoints" {1681test "tokenizer - illegal unicode codepoints" {
1671 // unicode newline characters.U+0085, U+2028, U+20291682 // unicode newline characters.U+0085, U+2028, U+2029
1672 testTokenize("//\xc2\x84", &[_]Token.Id{.LineComment});1683 testTokenize("//\xc2\x84", &.{});
1673 testTokenize("//\xc2\x85", &[_]Token.Id{1684 testTokenize("//\xc2\x85", &.{
1674 .LineComment,1685 .invalid,
1675 .Invalid,
1676 });1686 });
1677 testTokenize("//\xc2\x86", &[_]Token.Id{.LineComment});1687 testTokenize("//\xc2\x86", &.{});
1678 testTokenize("//\xe2\x80\xa7", &[_]Token.Id{.LineComment});1688 testTokenize("//\xe2\x80\xa7", &.{});
1679 testTokenize("//\xe2\x80\xa8", &[_]Token.Id{1689 testTokenize("//\xe2\x80\xa8", &.{
1680 .LineComment,1690 .invalid,
1681 .Invalid,
1682 });1691 });
1683 testTokenize("//\xe2\x80\xa9", &[_]Token.Id{1692 testTokenize("//\xe2\x80\xa9", &.{
1684 .LineComment,1693 .invalid,
1685 .Invalid,
1686 });1694 });
1687 testTokenize("//\xe2\x80\xaa", &[_]Token.Id{.LineComment});1695 testTokenize("//\xe2\x80\xaa", &.{});
1688}1696}
16891697
1690test "tokenizer - string identifier and builtin fns" {1698test "tokenizer - string identifier and builtin fns" {
1691 testTokenize(1699 testTokenize(
1692 \\const @"if" = @import("std");1700 \\const @"if" = @import("std");
1693 , &[_]Token.Id{1701 , &.{
1694 .Keyword_const,1702 .keyword_const,
1695 .Identifier,1703 .identifier,
1696 .Equal,1704 .equal,
1697 .Builtin,1705 .builtin,
1698 .LParen,1706 .l_paren,
1699 .StringLiteral,1707 .string_literal,
1700 .RParen,1708 .r_paren,
1701 .Semicolon,1709 .semicolon,
1702 });1710 });
1703}1711}
17041712
1705test "tokenizer - multiline string literal with literal tab" {1713test "tokenizer - multiline string literal with literal tab" {
1706 testTokenize(1714 testTokenize(
1707 \\\\foo bar1715 \\\\foo bar
1708 , &[_]Token.Id{1716 , &.{
1709 .MultilineStringLiteralLine,1717 .multiline_string_literal_line,
1710 });1718 });
1711}1719}
17121720
...@@ -1718,32 +1726,30 @@ test "tokenizer - comments with literal tab" {...@@ -1718,32 +1726,30 @@ test "tokenizer - comments with literal tab" {
1718 \\// foo1726 \\// foo
1719 \\/// foo1727 \\/// foo
1720 \\/// /foo1728 \\/// /foo
1721 , &[_]Token.Id{1729 , &.{
1722 .LineComment,1730 .container_doc_comment,
1723 .ContainerDocComment,1731 .doc_comment,
1724 .DocComment,1732 .doc_comment,
1725 .LineComment,1733 .doc_comment,
1726 .DocComment,
1727 .DocComment,
1728 });1734 });
1729}1735}
17301736
1731test "tokenizer - pipe and then invalid" {1737test "tokenizer - pipe and then invalid" {
1732 testTokenize("||=", &[_]Token.Id{1738 testTokenize("||=", &.{
1733 .PipePipe,1739 .pipe_pipe,
1734 .Equal,1740 .equal,
1735 });1741 });
1736}1742}
17371743
1738test "tokenizer - line comment and doc comment" {1744test "tokenizer - line comment and doc comment" {
1739 testTokenize("//", &[_]Token.Id{.LineComment});1745 testTokenize("//", &.{});
1740 testTokenize("// a / b", &[_]Token.Id{.LineComment});1746 testTokenize("// a / b", &.{});
1741 testTokenize("// /", &[_]Token.Id{.LineComment});1747 testTokenize("// /", &.{});
1742 testTokenize("/// a", &[_]Token.Id{.DocComment});1748 testTokenize("/// a", &.{.doc_comment});
1743 testTokenize("///", &[_]Token.Id{.DocComment});1749 testTokenize("///", &.{.doc_comment});
1744 testTokenize("////", &[_]Token.Id{.LineComment});1750 testTokenize("////", &.{});
1745 testTokenize("//!", &[_]Token.Id{.ContainerDocComment});1751 testTokenize("//!", &.{.container_doc_comment});
1746 testTokenize("//!!", &[_]Token.Id{.ContainerDocComment});1752 testTokenize("//!!", &.{.container_doc_comment});
1747}1753}
17481754
1749test "tokenizer - line comment followed by identifier" {1755test "tokenizer - line comment followed by identifier" {
...@@ -1751,304 +1757,304 @@ test "tokenizer - line comment followed by identifier" {...@@ -1751,304 +1757,304 @@ test "tokenizer - line comment followed by identifier" {
1751 \\ Unexpected,1757 \\ Unexpected,
1752 \\ // another1758 \\ // another
1753 \\ Another,1759 \\ Another,
1754 , &[_]Token.Id{1760 , &.{
1755 .Identifier,1761 .identifier,
1756 .Comma,1762 .comma,
1757 .LineComment,1763 .identifier,
1758 .Identifier,1764 .comma,
1759 .Comma,
1760 });1765 });
1761}1766}
17621767
1763test "tokenizer - UTF-8 BOM is recognized and skipped" {1768test "tokenizer - UTF-8 BOM is recognized and skipped" {
1764 testTokenize("\xEF\xBB\xBFa;\n", &[_]Token.Id{1769 testTokenize("\xEF\xBB\xBFa;\n", &.{
1765 .Identifier,1770 .identifier,
1766 .Semicolon,1771 .semicolon,
1767 });1772 });
1768}1773}
17691774
1770test "correctly parse pointer assignment" {1775test "correctly parse pointer assignment" {
1771 testTokenize("b.*=3;\n", &[_]Token.Id{1776 testTokenize("b.*=3;\n", &.{
1772 .Identifier,1777 .identifier,
1773 .PeriodAsterisk,1778 .period_asterisk,
1774 .Equal,1779 .equal,
1775 .IntegerLiteral,1780 .integer_literal,
1776 .Semicolon,1781 .semicolon,
1777 });1782 });
1778}1783}
17791784
1780test "correctly parse pointer dereference followed by asterisk" {1785test "correctly parse pointer dereference followed by asterisk" {
1781 testTokenize("\"b\".* ** 10", &[_]Token.Id{1786 testTokenize("\"b\".* ** 10", &.{
1782 .StringLiteral,1787 .string_literal,
1783 .PeriodAsterisk,1788 .period_asterisk,
1784 .AsteriskAsterisk,1789 .asterisk_asterisk,
1785 .IntegerLiteral,1790 .integer_literal,
1786 });1791 });
17871792
1788 testTokenize("(\"b\".*)** 10", &[_]Token.Id{1793 testTokenize("(\"b\".*)** 10", &.{
1789 .LParen,1794 .l_paren,
1790 .StringLiteral,1795 .string_literal,
1791 .PeriodAsterisk,1796 .period_asterisk,
1792 .RParen,1797 .r_paren,
1793 .AsteriskAsterisk,1798 .asterisk_asterisk,
1794 .IntegerLiteral,1799 .integer_literal,
1795 });1800 });
17961801
1797 testTokenize("\"b\".*** 10", &[_]Token.Id{1802 testTokenize("\"b\".*** 10", &.{
1798 .StringLiteral,1803 .string_literal,
1799 .Invalid_periodasterisks,1804 .invalid_periodasterisks,
1800 .AsteriskAsterisk,1805 .asterisk_asterisk,
1801 .IntegerLiteral,1806 .integer_literal,
1802 });1807 });
1803}1808}
18041809
1805test "tokenizer - range literals" {1810test "tokenizer - range literals" {
1806 testTokenize("0...9", &[_]Token.Id{ .IntegerLiteral, .Ellipsis3, .IntegerLiteral });1811 testTokenize("0...9", &.{ .integer_literal, .ellipsis3, .integer_literal });
1807 testTokenize("'0'...'9'", &[_]Token.Id{ .CharLiteral, .Ellipsis3, .CharLiteral });1812 testTokenize("'0'...'9'", &.{ .char_literal, .ellipsis3, .char_literal });
1808 testTokenize("0x00...0x09", &[_]Token.Id{ .IntegerLiteral, .Ellipsis3, .IntegerLiteral });1813 testTokenize("0x00...0x09", &.{ .integer_literal, .ellipsis3, .integer_literal });
1809 testTokenize("0b00...0b11", &[_]Token.Id{ .IntegerLiteral, .Ellipsis3, .IntegerLiteral });1814 testTokenize("0b00...0b11", &.{ .integer_literal, .ellipsis3, .integer_literal });
1810 testTokenize("0o00...0o11", &[_]Token.Id{ .IntegerLiteral, .Ellipsis3, .IntegerLiteral });1815 testTokenize("0o00...0o11", &.{ .integer_literal, .ellipsis3, .integer_literal });
1811}1816}
18121817
1813test "tokenizer - number literals decimal" {1818test "tokenizer - number literals decimal" {
1814 testTokenize("0", &[_]Token.Id{.IntegerLiteral});1819 testTokenize("0", &.{.integer_literal});
1815 testTokenize("1", &[_]Token.Id{.IntegerLiteral});1820 testTokenize("1", &.{.integer_literal});
1816 testTokenize("2", &[_]Token.Id{.IntegerLiteral});1821 testTokenize("2", &.{.integer_literal});
1817 testTokenize("3", &[_]Token.Id{.IntegerLiteral});1822 testTokenize("3", &.{.integer_literal});
1818 testTokenize("4", &[_]Token.Id{.IntegerLiteral});1823 testTokenize("4", &.{.integer_literal});
1819 testTokenize("5", &[_]Token.Id{.IntegerLiteral});1824 testTokenize("5", &.{.integer_literal});
1820 testTokenize("6", &[_]Token.Id{.IntegerLiteral});1825 testTokenize("6", &.{.integer_literal});
1821 testTokenize("7", &[_]Token.Id{.IntegerLiteral});1826 testTokenize("7", &.{.integer_literal});
1822 testTokenize("8", &[_]Token.Id{.IntegerLiteral});1827 testTokenize("8", &.{.integer_literal});
1823 testTokenize("9", &[_]Token.Id{.IntegerLiteral});1828 testTokenize("9", &.{.integer_literal});
1824 testTokenize("1..", &[_]Token.Id{ .IntegerLiteral, .Ellipsis2 });1829 testTokenize("1..", &.{ .integer_literal, .ellipsis2 });
1825 testTokenize("0a", &[_]Token.Id{ .Invalid, .Identifier });1830 testTokenize("0a", &.{ .invalid, .identifier });
1826 testTokenize("9b", &[_]Token.Id{ .Invalid, .Identifier });1831 testTokenize("9b", &.{ .invalid, .identifier });
1827 testTokenize("1z", &[_]Token.Id{ .Invalid, .Identifier });1832 testTokenize("1z", &.{ .invalid, .identifier });
1828 testTokenize("1z_1", &[_]Token.Id{ .Invalid, .Identifier });1833 testTokenize("1z_1", &.{ .invalid, .identifier });
1829 testTokenize("9z3", &[_]Token.Id{ .Invalid, .Identifier });1834 testTokenize("9z3", &.{ .invalid, .identifier });
18301835
1831 testTokenize("0_0", &[_]Token.Id{.IntegerLiteral});1836 testTokenize("0_0", &.{.integer_literal});
1832 testTokenize("0001", &[_]Token.Id{.IntegerLiteral});1837 testTokenize("0001", &.{.integer_literal});
1833 testTokenize("01234567890", &[_]Token.Id{.IntegerLiteral});1838 testTokenize("01234567890", &.{.integer_literal});
1834 testTokenize("012_345_6789_0", &[_]Token.Id{.IntegerLiteral});1839 testTokenize("012_345_6789_0", &.{.integer_literal});
1835 testTokenize("0_1_2_3_4_5_6_7_8_9_0", &[_]Token.Id{.IntegerLiteral});1840 testTokenize("0_1_2_3_4_5_6_7_8_9_0", &.{.integer_literal});
18361841
1837 testTokenize("00_", &[_]Token.Id{.Invalid});1842 testTokenize("00_", &.{.invalid});
1838 testTokenize("0_0_", &[_]Token.Id{.Invalid});1843 testTokenize("0_0_", &.{.invalid});
1839 testTokenize("0__0", &[_]Token.Id{ .Invalid, .Identifier });1844 testTokenize("0__0", &.{ .invalid, .identifier });
1840 testTokenize("0_0f", &[_]Token.Id{ .Invalid, .Identifier });1845 testTokenize("0_0f", &.{ .invalid, .identifier });
1841 testTokenize("0_0_f", &[_]Token.Id{ .Invalid, .Identifier });1846 testTokenize("0_0_f", &.{ .invalid, .identifier });
1842 testTokenize("0_0_f_00", &[_]Token.Id{ .Invalid, .Identifier });1847 testTokenize("0_0_f_00", &.{ .invalid, .identifier });
1843 testTokenize("1_,", &[_]Token.Id{ .Invalid, .Comma });1848 testTokenize("1_,", &.{ .invalid, .comma });
18441849
1845 testTokenize("1.", &[_]Token.Id{.FloatLiteral});1850 testTokenize("1.", &.{.float_literal});
1846 testTokenize("0.0", &[_]Token.Id{.FloatLiteral});1851 testTokenize("0.0", &.{.float_literal});
1847 testTokenize("1.0", &[_]Token.Id{.FloatLiteral});1852 testTokenize("1.0", &.{.float_literal});
1848 testTokenize("10.0", &[_]Token.Id{.FloatLiteral});1853 testTokenize("10.0", &.{.float_literal});
1849 testTokenize("0e0", &[_]Token.Id{.FloatLiteral});1854 testTokenize("0e0", &.{.float_literal});
1850 testTokenize("1e0", &[_]Token.Id{.FloatLiteral});1855 testTokenize("1e0", &.{.float_literal});
1851 testTokenize("1e100", &[_]Token.Id{.FloatLiteral});1856 testTokenize("1e100", &.{.float_literal});
1852 testTokenize("1.e100", &[_]Token.Id{.FloatLiteral});1857 testTokenize("1.e100", &.{.float_literal});
1853 testTokenize("1.0e100", &[_]Token.Id{.FloatLiteral});1858 testTokenize("1.0e100", &.{.float_literal});
1854 testTokenize("1.0e+100", &[_]Token.Id{.FloatLiteral});1859 testTokenize("1.0e+100", &.{.float_literal});
1855 testTokenize("1.0e-100", &[_]Token.Id{.FloatLiteral});1860 testTokenize("1.0e-100", &.{.float_literal});
1856 testTokenize("1_0_0_0.0_0_0_0_0_1e1_0_0_0", &[_]Token.Id{.FloatLiteral});1861 testTokenize("1_0_0_0.0_0_0_0_0_1e1_0_0_0", &.{.float_literal});
1857 testTokenize("1.+", &[_]Token.Id{ .FloatLiteral, .Plus });1862 testTokenize("1.+", &.{ .float_literal, .plus });
18581863
1859 testTokenize("1e", &[_]Token.Id{.Invalid});1864 testTokenize("1e", &.{.invalid});
1860 testTokenize("1.0e1f0", &[_]Token.Id{ .Invalid, .Identifier });1865 testTokenize("1.0e1f0", &.{ .invalid, .identifier });
1861 testTokenize("1.0p100", &[_]Token.Id{ .Invalid, .Identifier });1866 testTokenize("1.0p100", &.{ .invalid, .identifier });
1862 testTokenize("1.0p-100", &[_]Token.Id{ .Invalid, .Identifier, .Minus, .IntegerLiteral });1867 testTokenize("1.0p-100", &.{ .invalid, .identifier, .minus, .integer_literal });
1863 testTokenize("1.0p1f0", &[_]Token.Id{ .Invalid, .Identifier });1868 testTokenize("1.0p1f0", &.{ .invalid, .identifier });
1864 testTokenize("1.0_,", &[_]Token.Id{ .Invalid, .Comma });1869 testTokenize("1.0_,", &.{ .invalid, .comma });
1865 testTokenize("1_.0", &[_]Token.Id{ .Invalid, .Period, .IntegerLiteral });1870 testTokenize("1_.0", &.{ .invalid, .period, .integer_literal });
1866 testTokenize("1._", &[_]Token.Id{ .Invalid, .Identifier });1871 testTokenize("1._", &.{ .invalid, .identifier });
1867 testTokenize("1.a", &[_]Token.Id{ .Invalid, .Identifier });1872 testTokenize("1.a", &.{ .invalid, .identifier });
1868 testTokenize("1.z", &[_]Token.Id{ .Invalid, .Identifier });1873 testTokenize("1.z", &.{ .invalid, .identifier });
1869 testTokenize("1._0", &[_]Token.Id{ .Invalid, .Identifier });1874 testTokenize("1._0", &.{ .invalid, .identifier });
1870 testTokenize("1._+", &[_]Token.Id{ .Invalid, .Identifier, .Plus });1875 testTokenize("1._+", &.{ .invalid, .identifier, .plus });
1871 testTokenize("1._e", &[_]Token.Id{ .Invalid, .Identifier });1876 testTokenize("1._e", &.{ .invalid, .identifier });
1872 testTokenize("1.0e", &[_]Token.Id{.Invalid});1877 testTokenize("1.0e", &.{.invalid});
1873 testTokenize("1.0e,", &[_]Token.Id{ .Invalid, .Comma });1878 testTokenize("1.0e,", &.{ .invalid, .comma });
1874 testTokenize("1.0e_", &[_]Token.Id{ .Invalid, .Identifier });1879 testTokenize("1.0e_", &.{ .invalid, .identifier });
1875 testTokenize("1.0e+_", &[_]Token.Id{ .Invalid, .Identifier });1880 testTokenize("1.0e+_", &.{ .invalid, .identifier });
1876 testTokenize("1.0e-_", &[_]Token.Id{ .Invalid, .Identifier });1881 testTokenize("1.0e-_", &.{ .invalid, .identifier });
1877 testTokenize("1.0e0_+", &[_]Token.Id{ .Invalid, .Plus });1882 testTokenize("1.0e0_+", &.{ .invalid, .plus });
1878}1883}
18791884
1880test "tokenizer - number literals binary" {1885test "tokenizer - number literals binary" {
1881 testTokenize("0b0", &[_]Token.Id{.IntegerLiteral});1886 testTokenize("0b0", &.{.integer_literal});
1882 testTokenize("0b1", &[_]Token.Id{.IntegerLiteral});1887 testTokenize("0b1", &.{.integer_literal});
1883 testTokenize("0b2", &[_]Token.Id{ .Invalid, .IntegerLiteral });1888 testTokenize("0b2", &.{ .invalid, .integer_literal });
1884 testTokenize("0b3", &[_]Token.Id{ .Invalid, .IntegerLiteral });1889 testTokenize("0b3", &.{ .invalid, .integer_literal });
1885 testTokenize("0b4", &[_]Token.Id{ .Invalid, .IntegerLiteral });1890 testTokenize("0b4", &.{ .invalid, .integer_literal });
1886 testTokenize("0b5", &[_]Token.Id{ .Invalid, .IntegerLiteral });1891 testTokenize("0b5", &.{ .invalid, .integer_literal });
1887 testTokenize("0b6", &[_]Token.Id{ .Invalid, .IntegerLiteral });1892 testTokenize("0b6", &.{ .invalid, .integer_literal });
1888 testTokenize("0b7", &[_]Token.Id{ .Invalid, .IntegerLiteral });1893 testTokenize("0b7", &.{ .invalid, .integer_literal });
1889 testTokenize("0b8", &[_]Token.Id{ .Invalid, .IntegerLiteral });1894 testTokenize("0b8", &.{ .invalid, .integer_literal });
1890 testTokenize("0b9", &[_]Token.Id{ .Invalid, .IntegerLiteral });1895 testTokenize("0b9", &.{ .invalid, .integer_literal });
1891 testTokenize("0ba", &[_]Token.Id{ .Invalid, .Identifier });1896 testTokenize("0ba", &.{ .invalid, .identifier });
1892 testTokenize("0bb", &[_]Token.Id{ .Invalid, .Identifier });1897 testTokenize("0bb", &.{ .invalid, .identifier });
1893 testTokenize("0bc", &[_]Token.Id{ .Invalid, .Identifier });1898 testTokenize("0bc", &.{ .invalid, .identifier });
1894 testTokenize("0bd", &[_]Token.Id{ .Invalid, .Identifier });1899 testTokenize("0bd", &.{ .invalid, .identifier });
1895 testTokenize("0be", &[_]Token.Id{ .Invalid, .Identifier });1900 testTokenize("0be", &.{ .invalid, .identifier });
1896 testTokenize("0bf", &[_]Token.Id{ .Invalid, .Identifier });1901 testTokenize("0bf", &.{ .invalid, .identifier });
1897 testTokenize("0bz", &[_]Token.Id{ .Invalid, .Identifier });1902 testTokenize("0bz", &.{ .invalid, .identifier });
18981903
1899 testTokenize("0b0000_0000", &[_]Token.Id{.IntegerLiteral});1904 testTokenize("0b0000_0000", &.{.integer_literal});
1900 testTokenize("0b1111_1111", &[_]Token.Id{.IntegerLiteral});1905 testTokenize("0b1111_1111", &.{.integer_literal});
1901 testTokenize("0b10_10_10_10", &[_]Token.Id{.IntegerLiteral});1906 testTokenize("0b10_10_10_10", &.{.integer_literal});
1902 testTokenize("0b0_1_0_1_0_1_0_1", &[_]Token.Id{.IntegerLiteral});1907 testTokenize("0b0_1_0_1_0_1_0_1", &.{.integer_literal});
1903 testTokenize("0b1.", &[_]Token.Id{ .IntegerLiteral, .Period });1908 testTokenize("0b1.", &.{ .integer_literal, .period });
1904 testTokenize("0b1.0", &[_]Token.Id{ .IntegerLiteral, .Period, .IntegerLiteral });1909 testTokenize("0b1.0", &.{ .integer_literal, .period, .integer_literal });
19051910
1906 testTokenize("0B0", &[_]Token.Id{ .Invalid, .Identifier });1911 testTokenize("0B0", &.{ .invalid, .identifier });
1907 testTokenize("0b_", &[_]Token.Id{ .Invalid, .Identifier });1912 testTokenize("0b_", &.{ .invalid, .identifier });
1908 testTokenize("0b_0", &[_]Token.Id{ .Invalid, .Identifier });1913 testTokenize("0b_0", &.{ .invalid, .identifier });
1909 testTokenize("0b1_", &[_]Token.Id{.Invalid});1914 testTokenize("0b1_", &.{.invalid});
1910 testTokenize("0b0__1", &[_]Token.Id{ .Invalid, .Identifier });1915 testTokenize("0b0__1", &.{ .invalid, .identifier });
1911 testTokenize("0b0_1_", &[_]Token.Id{.Invalid});1916 testTokenize("0b0_1_", &.{.invalid});
1912 testTokenize("0b1e", &[_]Token.Id{ .Invalid, .Identifier });1917 testTokenize("0b1e", &.{ .invalid, .identifier });
1913 testTokenize("0b1p", &[_]Token.Id{ .Invalid, .Identifier });1918 testTokenize("0b1p", &.{ .invalid, .identifier });
1914 testTokenize("0b1e0", &[_]Token.Id{ .Invalid, .Identifier });1919 testTokenize("0b1e0", &.{ .invalid, .identifier });
1915 testTokenize("0b1p0", &[_]Token.Id{ .Invalid, .Identifier });1920 testTokenize("0b1p0", &.{ .invalid, .identifier });
1916 testTokenize("0b1_,", &[_]Token.Id{ .Invalid, .Comma });1921 testTokenize("0b1_,", &.{ .invalid, .comma });
1917}1922}
19181923
1919test "tokenizer - number literals octal" {1924test "tokenizer - number literals octal" {
1920 testTokenize("0o0", &[_]Token.Id{.IntegerLiteral});1925 testTokenize("0o0", &.{.integer_literal});
1921 testTokenize("0o1", &[_]Token.Id{.IntegerLiteral});1926 testTokenize("0o1", &.{.integer_literal});
1922 testTokenize("0o2", &[_]Token.Id{.IntegerLiteral});1927 testTokenize("0o2", &.{.integer_literal});
1923 testTokenize("0o3", &[_]Token.Id{.IntegerLiteral});1928 testTokenize("0o3", &.{.integer_literal});
1924 testTokenize("0o4", &[_]Token.Id{.IntegerLiteral});1929 testTokenize("0o4", &.{.integer_literal});
1925 testTokenize("0o5", &[_]Token.Id{.IntegerLiteral});1930 testTokenize("0o5", &.{.integer_literal});
1926 testTokenize("0o6", &[_]Token.Id{.IntegerLiteral});1931 testTokenize("0o6", &.{.integer_literal});
1927 testTokenize("0o7", &[_]Token.Id{.IntegerLiteral});1932 testTokenize("0o7", &.{.integer_literal});
1928 testTokenize("0o8", &[_]Token.Id{ .Invalid, .IntegerLiteral });1933 testTokenize("0o8", &.{ .invalid, .integer_literal });
1929 testTokenize("0o9", &[_]Token.Id{ .Invalid, .IntegerLiteral });1934 testTokenize("0o9", &.{ .invalid, .integer_literal });
1930 testTokenize("0oa", &[_]Token.Id{ .Invalid, .Identifier });1935 testTokenize("0oa", &.{ .invalid, .identifier });
1931 testTokenize("0ob", &[_]Token.Id{ .Invalid, .Identifier });1936 testTokenize("0ob", &.{ .invalid, .identifier });
1932 testTokenize("0oc", &[_]Token.Id{ .Invalid, .Identifier });1937 testTokenize("0oc", &.{ .invalid, .identifier });
1933 testTokenize("0od", &[_]Token.Id{ .Invalid, .Identifier });1938 testTokenize("0od", &.{ .invalid, .identifier });
1934 testTokenize("0oe", &[_]Token.Id{ .Invalid, .Identifier });1939 testTokenize("0oe", &.{ .invalid, .identifier });
1935 testTokenize("0of", &[_]Token.Id{ .Invalid, .Identifier });1940 testTokenize("0of", &.{ .invalid, .identifier });
1936 testTokenize("0oz", &[_]Token.Id{ .Invalid, .Identifier });1941 testTokenize("0oz", &.{ .invalid, .identifier });
19371942
1938 testTokenize("0o01234567", &[_]Token.Id{.IntegerLiteral});1943 testTokenize("0o01234567", &.{.integer_literal});
1939 testTokenize("0o0123_4567", &[_]Token.Id{.IntegerLiteral});1944 testTokenize("0o0123_4567", &.{.integer_literal});
1940 testTokenize("0o01_23_45_67", &[_]Token.Id{.IntegerLiteral});1945 testTokenize("0o01_23_45_67", &.{.integer_literal});
1941 testTokenize("0o0_1_2_3_4_5_6_7", &[_]Token.Id{.IntegerLiteral});1946 testTokenize("0o0_1_2_3_4_5_6_7", &.{.integer_literal});
1942 testTokenize("0o7.", &[_]Token.Id{ .IntegerLiteral, .Period });1947 testTokenize("0o7.", &.{ .integer_literal, .period });
1943 testTokenize("0o7.0", &[_]Token.Id{ .IntegerLiteral, .Period, .IntegerLiteral });1948 testTokenize("0o7.0", &.{ .integer_literal, .period, .integer_literal });
19441949
1945 testTokenize("0O0", &[_]Token.Id{ .Invalid, .Identifier });1950 testTokenize("0O0", &.{ .invalid, .identifier });
1946 testTokenize("0o_", &[_]Token.Id{ .Invalid, .Identifier });1951 testTokenize("0o_", &.{ .invalid, .identifier });
1947 testTokenize("0o_0", &[_]Token.Id{ .Invalid, .Identifier });1952 testTokenize("0o_0", &.{ .invalid, .identifier });
1948 testTokenize("0o1_", &[_]Token.Id{.Invalid});1953 testTokenize("0o1_", &.{.invalid});
1949 testTokenize("0o0__1", &[_]Token.Id{ .Invalid, .Identifier });1954 testTokenize("0o0__1", &.{ .invalid, .identifier });
1950 testTokenize("0o0_1_", &[_]Token.Id{.Invalid});1955 testTokenize("0o0_1_", &.{.invalid});
1951 testTokenize("0o1e", &[_]Token.Id{ .Invalid, .Identifier });1956 testTokenize("0o1e", &.{ .invalid, .identifier });
1952 testTokenize("0o1p", &[_]Token.Id{ .Invalid, .Identifier });1957 testTokenize("0o1p", &.{ .invalid, .identifier });
1953 testTokenize("0o1e0", &[_]Token.Id{ .Invalid, .Identifier });1958 testTokenize("0o1e0", &.{ .invalid, .identifier });
1954 testTokenize("0o1p0", &[_]Token.Id{ .Invalid, .Identifier });1959 testTokenize("0o1p0", &.{ .invalid, .identifier });
1955 testTokenize("0o_,", &[_]Token.Id{ .Invalid, .Identifier, .Comma });1960 testTokenize("0o_,", &.{ .invalid, .identifier, .comma });
1956}1961}
19571962
1958test "tokenizer - number literals hexadeciaml" {1963test "tokenizer - number literals hexadeciaml" {
1959 testTokenize("0x0", &[_]Token.Id{.IntegerLiteral});1964 testTokenize("0x0", &.{.integer_literal});
1960 testTokenize("0x1", &[_]Token.Id{.IntegerLiteral});1965 testTokenize("0x1", &.{.integer_literal});
1961 testTokenize("0x2", &[_]Token.Id{.IntegerLiteral});1966 testTokenize("0x2", &.{.integer_literal});
1962 testTokenize("0x3", &[_]Token.Id{.IntegerLiteral});1967 testTokenize("0x3", &.{.integer_literal});
1963 testTokenize("0x4", &[_]Token.Id{.IntegerLiteral});1968 testTokenize("0x4", &.{.integer_literal});
1964 testTokenize("0x5", &[_]Token.Id{.IntegerLiteral});1969 testTokenize("0x5", &.{.integer_literal});
1965 testTokenize("0x6", &[_]Token.Id{.IntegerLiteral});1970 testTokenize("0x6", &.{.integer_literal});
1966 testTokenize("0x7", &[_]Token.Id{.IntegerLiteral});1971 testTokenize("0x7", &.{.integer_literal});
1967 testTokenize("0x8", &[_]Token.Id{.IntegerLiteral});1972 testTokenize("0x8", &.{.integer_literal});
1968 testTokenize("0x9", &[_]Token.Id{.IntegerLiteral});1973 testTokenize("0x9", &.{.integer_literal});
1969 testTokenize("0xa", &[_]Token.Id{.IntegerLiteral});1974 testTokenize("0xa", &.{.integer_literal});
1970 testTokenize("0xb", &[_]Token.Id{.IntegerLiteral});1975 testTokenize("0xb", &.{.integer_literal});
1971 testTokenize("0xc", &[_]Token.Id{.IntegerLiteral});1976 testTokenize("0xc", &.{.integer_literal});
1972 testTokenize("0xd", &[_]Token.Id{.IntegerLiteral});1977 testTokenize("0xd", &.{.integer_literal});
1973 testTokenize("0xe", &[_]Token.Id{.IntegerLiteral});1978 testTokenize("0xe", &.{.integer_literal});
1974 testTokenize("0xf", &[_]Token.Id{.IntegerLiteral});1979 testTokenize("0xf", &.{.integer_literal});
1975 testTokenize("0xA", &[_]Token.Id{.IntegerLiteral});1980 testTokenize("0xA", &.{.integer_literal});
1976 testTokenize("0xB", &[_]Token.Id{.IntegerLiteral});1981 testTokenize("0xB", &.{.integer_literal});
1977 testTokenize("0xC", &[_]Token.Id{.IntegerLiteral});1982 testTokenize("0xC", &.{.integer_literal});
1978 testTokenize("0xD", &[_]Token.Id{.IntegerLiteral});1983 testTokenize("0xD", &.{.integer_literal});
1979 testTokenize("0xE", &[_]Token.Id{.IntegerLiteral});1984 testTokenize("0xE", &.{.integer_literal});
1980 testTokenize("0xF", &[_]Token.Id{.IntegerLiteral});1985 testTokenize("0xF", &.{.integer_literal});
1981 testTokenize("0x0z", &[_]Token.Id{ .Invalid, .Identifier });1986 testTokenize("0x0z", &.{ .invalid, .identifier });
1982 testTokenize("0xz", &[_]Token.Id{ .Invalid, .Identifier });1987 testTokenize("0xz", &.{ .invalid, .identifier });
19831988
1984 testTokenize("0x0123456789ABCDEF", &[_]Token.Id{.IntegerLiteral});1989 testTokenize("0x0123456789ABCDEF", &.{.integer_literal});
1985 testTokenize("0x0123_4567_89AB_CDEF", &[_]Token.Id{.IntegerLiteral});1990 testTokenize("0x0123_4567_89AB_CDEF", &.{.integer_literal});
1986 testTokenize("0x01_23_45_67_89AB_CDE_F", &[_]Token.Id{.IntegerLiteral});1991 testTokenize("0x01_23_45_67_89AB_CDE_F", &.{.integer_literal});
1987 testTokenize("0x0_1_2_3_4_5_6_7_8_9_A_B_C_D_E_F", &[_]Token.Id{.IntegerLiteral});1992 testTokenize("0x0_1_2_3_4_5_6_7_8_9_A_B_C_D_E_F", &.{.integer_literal});
19881993
1989 testTokenize("0X0", &[_]Token.Id{ .Invalid, .Identifier });1994 testTokenize("0X0", &.{ .invalid, .identifier });
1990 testTokenize("0x_", &[_]Token.Id{ .Invalid, .Identifier });1995 testTokenize("0x_", &.{ .invalid, .identifier });
1991 testTokenize("0x_1", &[_]Token.Id{ .Invalid, .Identifier });1996 testTokenize("0x_1", &.{ .invalid, .identifier });
1992 testTokenize("0x1_", &[_]Token.Id{.Invalid});1997 testTokenize("0x1_", &.{.invalid});
1993 testTokenize("0x0__1", &[_]Token.Id{ .Invalid, .Identifier });1998 testTokenize("0x0__1", &.{ .invalid, .identifier });
1994 testTokenize("0x0_1_", &[_]Token.Id{.Invalid});1999 testTokenize("0x0_1_", &.{.invalid});
1995 testTokenize("0x_,", &[_]Token.Id{ .Invalid, .Identifier, .Comma });2000 testTokenize("0x_,", &.{ .invalid, .identifier, .comma });
19962001
1997 testTokenize("0x1.", &[_]Token.Id{.FloatLiteral});2002 testTokenize("0x1.", &.{.float_literal});
1998 testTokenize("0x1.0", &[_]Token.Id{.FloatLiteral});2003 testTokenize("0x1.0", &.{.float_literal});
1999 testTokenize("0xF.", &[_]Token.Id{.FloatLiteral});2004 testTokenize("0xF.", &.{.float_literal});
2000 testTokenize("0xF.0", &[_]Token.Id{.FloatLiteral});2005 testTokenize("0xF.0", &.{.float_literal});
2001 testTokenize("0xF.F", &[_]Token.Id{.FloatLiteral});2006 testTokenize("0xF.F", &.{.float_literal});
2002 testTokenize("0xF.Fp0", &[_]Token.Id{.FloatLiteral});2007 testTokenize("0xF.Fp0", &.{.float_literal});
2003 testTokenize("0xF.FP0", &[_]Token.Id{.FloatLiteral});2008 testTokenize("0xF.FP0", &.{.float_literal});
2004 testTokenize("0x1p0", &[_]Token.Id{.FloatLiteral});2009 testTokenize("0x1p0", &.{.float_literal});
2005 testTokenize("0xfp0", &[_]Token.Id{.FloatLiteral});2010 testTokenize("0xfp0", &.{.float_literal});
2006 testTokenize("0x1.+0xF.", &[_]Token.Id{ .FloatLiteral, .Plus, .FloatLiteral });2011 testTokenize("0x1.+0xF.", &.{ .float_literal, .plus, .float_literal });
20072012
2008 testTokenize("0x0123456.789ABCDEF", &[_]Token.Id{.FloatLiteral});2013 testTokenize("0x0123456.789ABCDEF", &.{.float_literal});
2009 testTokenize("0x0_123_456.789_ABC_DEF", &[_]Token.Id{.FloatLiteral});2014 testTokenize("0x0_123_456.789_ABC_DEF", &.{.float_literal});
2010 testTokenize("0x0_1_2_3_4_5_6.7_8_9_A_B_C_D_E_F", &[_]Token.Id{.FloatLiteral});2015 testTokenize("0x0_1_2_3_4_5_6.7_8_9_A_B_C_D_E_F", &.{.float_literal});
2011 testTokenize("0x0p0", &[_]Token.Id{.FloatLiteral});2016 testTokenize("0x0p0", &.{.float_literal});
2012 testTokenize("0x0.0p0", &[_]Token.Id{.FloatLiteral});2017 testTokenize("0x0.0p0", &.{.float_literal});
2013 testTokenize("0xff.ffp10", &[_]Token.Id{.FloatLiteral});2018 testTokenize("0xff.ffp10", &.{.float_literal});
2014 testTokenize("0xff.ffP10", &[_]Token.Id{.FloatLiteral});2019 testTokenize("0xff.ffP10", &.{.float_literal});
2015 testTokenize("0xff.p10", &[_]Token.Id{.FloatLiteral});2020 testTokenize("0xff.p10", &.{.float_literal});
2016 testTokenize("0xffp10", &[_]Token.Id{.FloatLiteral});2021 testTokenize("0xffp10", &.{.float_literal});
2017 testTokenize("0xff_ff.ff_ffp1_0_0_0", &[_]Token.Id{.FloatLiteral});2022 testTokenize("0xff_ff.ff_ffp1_0_0_0", &.{.float_literal});
2018 testTokenize("0xf_f_f_f.f_f_f_fp+1_000", &[_]Token.Id{.FloatLiteral});2023 testTokenize("0xf_f_f_f.f_f_f_fp+1_000", &.{.float_literal});
2019 testTokenize("0xf_f_f_f.f_f_f_fp-1_00_0", &[_]Token.Id{.FloatLiteral});2024 testTokenize("0xf_f_f_f.f_f_f_fp-1_00_0", &.{.float_literal});
20202025
2021 testTokenize("0x1e", &[_]Token.Id{.IntegerLiteral});2026 testTokenize("0x1e", &.{.integer_literal});
2022 testTokenize("0x1e0", &[_]Token.Id{.IntegerLiteral});2027 testTokenize("0x1e0", &.{.integer_literal});
2023 testTokenize("0x1p", &[_]Token.Id{.Invalid});2028 testTokenize("0x1p", &.{.invalid});
2024 testTokenize("0xfp0z1", &[_]Token.Id{ .Invalid, .Identifier });2029 testTokenize("0xfp0z1", &.{ .invalid, .identifier });
2025 testTokenize("0xff.ffpff", &[_]Token.Id{ .Invalid, .Identifier });2030 testTokenize("0xff.ffpff", &.{ .invalid, .identifier });
2026 testTokenize("0x0.p", &[_]Token.Id{.Invalid});2031 testTokenize("0x0.p", &.{.invalid});
2027 testTokenize("0x0.z", &[_]Token.Id{ .Invalid, .Identifier });2032 testTokenize("0x0.z", &.{ .invalid, .identifier });
2028 testTokenize("0x0._", &[_]Token.Id{ .Invalid, .Identifier });2033 testTokenize("0x0._", &.{ .invalid, .identifier });
2029 testTokenize("0x0_.0", &[_]Token.Id{ .Invalid, .Period, .IntegerLiteral });2034 testTokenize("0x0_.0", &.{ .invalid, .period, .integer_literal });
2030 testTokenize("0x0_.0.0", &[_]Token.Id{ .Invalid, .Period, .FloatLiteral });2035 testTokenize("0x0_.0.0", &.{ .invalid, .period, .float_literal });
2031 testTokenize("0x0._0", &[_]Token.Id{ .Invalid, .Identifier });2036 testTokenize("0x0._0", &.{ .invalid, .identifier });
2032 testTokenize("0x0.0_", &[_]Token.Id{.Invalid});2037 testTokenize("0x0.0_", &.{.invalid});
2033 testTokenize("0x0_p0", &[_]Token.Id{ .Invalid, .Identifier });2038 testTokenize("0x0_p0", &.{ .invalid, .identifier });
2034 testTokenize("0x0_.p0", &[_]Token.Id{ .Invalid, .Period, .Identifier });2039 testTokenize("0x0_.p0", &.{ .invalid, .period, .identifier });
2035 testTokenize("0x0._p0", &[_]Token.Id{ .Invalid, .Identifier });2040 testTokenize("0x0._p0", &.{ .invalid, .identifier });
2036 testTokenize("0x0.0_p0", &[_]Token.Id{ .Invalid, .Identifier });2041 testTokenize("0x0.0_p0", &.{ .invalid, .identifier });
2037 testTokenize("0x0._0p0", &[_]Token.Id{ .Invalid, .Identifier });2042 testTokenize("0x0._0p0", &.{ .invalid, .identifier });
2038 testTokenize("0x0.0p_0", &[_]Token.Id{ .Invalid, .Identifier });2043 testTokenize("0x0.0p_0", &.{ .invalid, .identifier });
2039 testTokenize("0x0.0p+_0", &[_]Token.Id{ .Invalid, .Identifier });2044 testTokenize("0x0.0p+_0", &.{ .invalid, .identifier });
2040 testTokenize("0x0.0p-_0", &[_]Token.Id{ .Invalid, .Identifier });2045 testTokenize("0x0.0p-_0", &.{ .invalid, .identifier });
2041 testTokenize("0x0.0p0_", &[_]Token.Id{ .Invalid, .Eof });2046 testTokenize("0x0.0p0_", &.{ .invalid, .eof });
2042}2047}
20432048
2044fn testTokenize(source: []const u8, expected_tokens: []const Token.Id) void {2049fn testTokenize(source: []const u8, expected_tokens: []const Token.Tag) void {
2045 var tokenizer = Tokenizer.init(source);2050 var tokenizer = Tokenizer.init(source);
2046 for (expected_tokens) |expected_token_id| {2051 for (expected_tokens) |expected_token_id| {
2047 const token = tokenizer.next();2052 const token = tokenizer.next();
2048 if (token.id != expected_token_id) {2053 if (token.tag != expected_token_id) {
2049 std.debug.panic("expected {s}, found {s}\n", .{ @tagName(expected_token_id), @tagName(token.id) });2054 std.debug.panic("expected {s}, found {s}\n", .{ @tagName(expected_token_id), @tagName(token.tag) });
2050 }2055 }
2051 }2056 }
2052 const last_token = tokenizer.next();2057 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);
2054}2060}
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 {...@@ -921,7 +921,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
921 // TODO this is duped so it can be freed in Container.deinit921 // TODO this is duped so it can be freed in Container.deinit
922 .sub_file_path = try gpa.dupe(u8, root_pkg.root_src_path),922 .sub_file_path = try gpa.dupe(u8, root_pkg.root_src_path),
923 .source = .{ .unloaded = {} },923 .source = .{ .unloaded = {} },
924 .contents = .{ .not_available = {} },924 .tree = undefined,
925 .status = .never_loaded,925 .status = .never_loaded,
926 .pkg = root_pkg,926 .pkg = root_pkg,
927 .root_container = .{927 .root_container = .{
...@@ -1334,7 +1334,7 @@ pub fn update(self: *Compilation) !void {...@@ -1334,7 +1334,7 @@ pub fn update(self: *Compilation) !void {
1334 self.c_object_work_queue.writeItemAssumeCapacity(entry.key);1334 self.c_object_work_queue.writeItemAssumeCapacity(entry.key);
1335 }1335 }
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;
1338 if (!use_stage1) {1338 if (!use_stage1) {
1339 if (self.bin_file.options.module) |module| {1339 if (self.bin_file.options.module) |module| {
1340 module.compile_log_text.shrinkAndFree(module.gpa, 0);1340 module.compile_log_text.shrinkAndFree(module.gpa, 0);
...@@ -1884,7 +1884,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {...@@ -1884,7 +1884,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {
1884 const c_headers_dir_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{"include"});1884 const c_headers_dir_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{"include"});
1885 const c_headers_dir_path_z = try arena.dupeZ(u8, c_headers_dir_path);1885 const c_headers_dir_path_z = try arena.dupeZ(u8, c_headers_dir_path);
1886 var clang_errors: []translate_c.ClangErrMsg = &[0]translate_c.ClangErrMsg{};1886 var clang_errors: []translate_c.ClangErrMsg = &[0]translate_c.ClangErrMsg{};
1887 const tree = translate_c.translate(1887 var tree = translate_c.translate(
1888 comp.gpa,1888 comp.gpa,
1889 new_argv.ptr,1889 new_argv.ptr,
1890 new_argv.ptr + new_argv.len,1890 new_argv.ptr + new_argv.len,
...@@ -1903,7 +1903,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {...@@ -1903,7 +1903,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {
1903 };1903 };
1904 },1904 },
1905 };1905 };
1906 defer tree.deinit();1906 defer tree.deinit(comp.gpa);
19071907
1908 if (comp.verbose_cimport) {1908 if (comp.verbose_cimport) {
1909 log.info("C import .d file: {s}", .{out_dep_path});1909 log.info("C import .d file: {s}", .{out_dep_path});
...@@ -1921,9 +1921,10 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {...@@ -1921,9 +1921,10 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {
1921 var out_zig_file = try o_dir.createFile(cimport_zig_basename, .{});1921 var out_zig_file = try o_dir.createFile(cimport_zig_basename, .{});
1922 defer out_zig_file.close();1922 defer out_zig_file.close();
19231923
1924 var bos = std.io.bufferedWriter(out_zig_file.writer());1924 const formatted = try tree.render(comp.gpa);
1925 _ = try std.zig.render(comp.gpa, bos.writer(), tree);1925 defer comp.gpa.free(formatted);
1926 try bos.flush();1926
1927 try out_zig_file.writeAll(formatted);
19271928
1928 man.writeManifest() catch |err| {1929 man.writeManifest() catch |err| {
1929 log.warn("failed to write cache manifest for C import: {s}", .{@errorName(err)});1930 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 {...@@ -1936,7 +1937,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {
1936 "o", &digest, cimport_zig_basename,1937 "o", &digest, cimport_zig_basename,
1937 });1938 });
1938 if (comp.verbose_cimport) {1939 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});
1940 }1941 }
1941 return CImportResult{1942 return CImportResult{
1942 .out_zig_path = out_zig_path,1943 .out_zig_path = out_zig_path,
...@@ -3000,7 +3001,7 @@ pub fn updateSubCompilation(sub_compilation: *Compilation) !void {...@@ -3000,7 +3001,7 @@ pub fn updateSubCompilation(sub_compilation: *Compilation) !void {
3000 for (errors.list) |full_err_msg| {3001 for (errors.list) |full_err_msg| {
3001 switch (full_err_msg) {3002 switch (full_err_msg) {
3002 .src => |src| {3003 .src => |src| {
3003 log.err("{s}:{d}:{d}: {s}\n", .{3004 log.err("{s}:{d}:{d}: {s}", .{
3004 src.src_path,3005 src.src_path,
3005 src.line + 1,3006 src.line + 1,
3006 src.column + 1,3007 src.column + 1,
src/Module.zig+977-637
...@@ -244,9 +244,9 @@ pub const Decl = struct {...@@ -244,9 +244,9 @@ pub const Decl = struct {
244 }244 }
245245
246 pub fn src(self: Decl) usize {246 pub fn src(self: Decl) usize {
247 const tree = self.container.file_scope.contents.tree;247 const tree = &self.container.file_scope.tree;
248 const decl_node = tree.root_node.decls()[self.src_index];248 const decl_node = tree.rootDecls()[self.src_index];
249 return tree.token_locs[decl_node.firstToken()].start;249 return tree.tokens.items(.start)[tree.firstToken(decl_node)];
250 }250 }
251251
252 pub fn fullyQualifiedNameHash(self: Decl) Scope.NameHash {252 pub fn fullyQualifiedNameHash(self: Decl) Scope.NameHash {
...@@ -428,14 +428,14 @@ pub const Scope = struct {...@@ -428,14 +428,14 @@ pub const Scope = struct {
428 }428 }
429429
430 /// Asserts the scope is a child of a File and has an AST tree and returns the tree.430 /// 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 {
432 switch (self.tag) {432 switch (self.tag) {
433 .file => return self.cast(File).?.contents.tree,433 .file => return &self.cast(File).?.tree,
434 .block => return self.cast(Block).?.src_decl.container.file_scope.contents.tree,434 .block => return &self.cast(Block).?.src_decl.container.file_scope.tree,
435 .gen_zir => return self.cast(GenZIR).?.decl.container.file_scope.contents.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.contents.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.contents.tree,437 .local_ptr => return &self.cast(LocalPtr).?.gen_zir.decl.container.file_scope.tree,
438 .container => return self.cast(Container).?.file_scope.contents.tree,438 .container => return &self.cast(Container).?.file_scope.tree,
439 }439 }
440 }440 }
441441
...@@ -540,6 +540,12 @@ pub const Scope = struct {...@@ -540,6 +540,12 @@ pub const Scope = struct {
540 pub const File = struct {540 pub const File = struct {
541 pub const base_tag: Tag = .file;541 pub const base_tag: Tag = .file;
542 base: Scope = Scope{ .tag = base_tag },542 base: Scope = Scope{ .tag = base_tag },
543 status: enum {
544 never_loaded,
545 unloaded_success,
546 unloaded_parse_failure,
547 loaded_success,
548 },
543549
544 /// Relative to the owning package's root_src_dir.550 /// Relative to the owning package's root_src_dir.
545 /// Reference to external memory, not owned by File.551 /// Reference to external memory, not owned by File.
...@@ -548,16 +554,8 @@ pub const Scope = struct {...@@ -548,16 +554,8 @@ pub const Scope = struct {
548 unloaded: void,554 unloaded: void,
549 bytes: [:0]const u8,555 bytes: [:0]const u8,
550 },556 },
551 contents: union {557 /// Whether this is populated or not depends on `status`.
552 not_available: void,558 tree: ast.Tree,
553 tree: *ast.Tree,
554 },
555 status: enum {
556 never_loaded,
557 unloaded_success,
558 unloaded_parse_failure,
559 loaded_success,
560 },
561 /// Package that this file is a part of, managed externally.559 /// Package that this file is a part of, managed externally.
562 pkg: *Package,560 pkg: *Package,
563561
...@@ -571,7 +569,7 @@ pub const Scope = struct {...@@ -571,7 +569,7 @@ pub const Scope = struct {
571 => {},569 => {},
572570
573 .loaded_success => {571 .loaded_success => {
574 self.contents.tree.deinit();572 self.tree.deinit(gpa);
575 self.status = .unloaded_success;573 self.status = .unloaded_success;
576 },574 },
577 }575 }
...@@ -926,7 +924,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void {...@@ -926,7 +924,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void {
926 .complete => return,924 .complete => return,
927925
928 .outdated => blk: {926 .outdated => blk: {
929 log.debug("re-analyzing {s}\n", .{decl.name});927 log.debug("re-analyzing {s}", .{decl.name});
930928
931 // The exports this Decl performs will be re-discovered, so we remove them here929 // The exports this Decl performs will be re-discovered, so we remove them here
932 // prior to re-analysis.930 // prior to re-analysis.
...@@ -950,7 +948,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void {...@@ -950,7 +948,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void {
950 .unreferenced => false,948 .unreferenced => false,
951 };949 };
952950
953 const type_changed = mod.astGenAndAnalyzeDecl(decl) catch |err| switch (err) {951 const type_changed = mod.astgenAndSemaDecl(decl) catch |err| switch (err) {
954 error.OutOfMemory => return error.OutOfMemory,952 error.OutOfMemory => return error.OutOfMemory,
955 error.AnalysisFail => return error.AnalysisFail,953 error.AnalysisFail => return error.AnalysisFail,
956 else => {954 else => {
...@@ -992,141 +990,72 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void {...@@ -992,141 +990,72 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void {
992 }990 }
993}991}
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 {
996 const tracy = trace(@src());997 const tracy = trace(@src());
997 defer tracy.end();998 defer tracy.end();
998999
999 const tree = try self.getAstTree(decl.container.file_scope);1000 const tree = try mod.getAstTree(decl.container.file_scope);
1000 const ast_node = tree.root_node.decls()[decl.src_index];1001 const node_tags = tree.nodes.items(.tag);
1001 switch (ast_node.tag) {1002 const node_datas = tree.nodes.items(.data);
1002 .FnProto => {1003 const decl_node = tree.rootDecls()[decl.src_index];
1003 const fn_proto = ast_node.castTag(.FnProto).?;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" => {
1005 decl.analysis = .in_progress;1039 decl.analysis = .in_progress;
10061040
1007 // This arena allocator's memory is discarded at the end of this function. It is used1041 // A comptime decl does not store any value so we can just deinit this arena after analysis is done.
1008 // to determine the type of the function, and hence the type of the decl, which is needed1042 var analysis_arena = std.heap.ArenaAllocator.init(mod.gpa);
1009 // to complete the Decl analysis.1043 defer analysis_arena.deinit();
1010 var fn_type_scope_arena = std.heap.ArenaAllocator.init(self.gpa);1044 var gen_scope: Scope.GenZIR = .{
1011 defer fn_type_scope_arena.deinit();
1012 var fn_type_scope: Scope.GenZIR = .{
1013 .decl = decl,1045 .decl = decl,
1014 .arena = &fn_type_scope_arena.allocator,1046 .arena = &analysis_arena.allocator,
1015 .parent = &decl.container.base,1047 .parent = &decl.container.base,
1016 .force_comptime = true,1048 .force_comptime = true,
1017 };1049 };
1018 defer fn_type_scope.instructions.deinit(self.gpa);1050 defer gen_scope.instructions.deinit(mod.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);
10241051
1025 const fn_src = tree.token_locs[fn_proto.fn_token].start;1052 const block_expr = node_datas[decl_node].lhs;
1026 const type_type = try astgen.addZIRInstConst(self, &fn_type_scope.base, fn_src, .{1053 _ = try astgen.comptimeExpr(mod, &gen_scope.base, .none, block_expr);
1027 .ty = Type.initTag(.type),1054 if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
1028 .val = Value.initTag(.type_type),1055 zir.dumpZir(mod.gpa, "comptime_block", decl.name, gen_scope.instructions.items) catch {};
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", .{});
1089 }1056 }
10901057
1091 const enum_literal_type = try astgen.addZIRInstConst(self, &fn_type_scope.base, fn_src, .{1058 var inst_table = Scope.Block.InstTable.init(mod.gpa);
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);
1130 defer inst_table.deinit();1059 defer inst_table.deinit();
11311060
1132 var branch_quota: u32 = default_eval_branch_quota;1061 var branch_quota: u32 = default_eval_branch_quota;
...@@ -1138,424 +1067,627 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1138,424 +1067,627 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1138 .owner_decl = decl,1067 .owner_decl = decl,
1139 .src_decl = decl,1068 .src_decl = decl,
1140 .instructions = .{},1069 .instructions = .{},
1141 .arena = &decl_arena.allocator,1070 .arena = &analysis_arena.allocator,
1142 .inlining = null,1071 .inlining = null,
1143 .is_comptime = false,1072 .is_comptime = true,
1144 .branch_quota = &branch_quota,1073 .branch_quota = &branch_quota,
1145 };1074 };
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, .{1077 _ = try zir_sema.analyzeBody(mod, &block_scope, .{
1149 .instructions = fn_type_scope.instructions.items,1078 .instructions = gen_scope.instructions.items,
1150 });1079 });
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);1081 decl.analysis = .complete;
1158 }1082 decl.generation = mod.generation;
1159 const fn_val = try Value.Tag.extern_fn.create(&decl_arena.allocator, decl);1083 return true;
1084 },
1085 .@"usingnamespace" => @panic("TODO usingnamespace decl"),
1086 else => unreachable,
1087 }
1088}
11601089
1161 decl_arena_state.* = decl_arena.state;1090fn astgenAndSemaFn(
1162 decl.typed_value = .{1091 mod: *Module,
1163 .most_recent = .{1092 decl: *Decl,
1164 .typed_value = .{ .ty = fn_type, .val = fn_val },1093 tree: ast.Tree,
1165 .arena = decl_arena_state,1094 body_node: ast.Node.Index,
1166 },1095 fn_proto: ast.full.FnProto,
1167 };1096) !bool {
1168 decl.analysis = .complete;1097 const tracy = trace(@src());
1169 decl.generation = self.generation;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);1118 decl.is_pub = fn_proto.visib_token != null;
1172 try self.comp.work_queue.writeItem(.{ .codegen_decl = decl });
11731119
1174 if (type_changed and self.emit_h != null) {1120 // The AST params array does not contain anytype and ... parameters.
1175 try self.comp.work_queue.writeItem(.{ .emit_h_decl = decl });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,
1176 }1157 }
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;1227 const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1;
1179 };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);1260 if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
1182 const fn_payload = try decl_arena.allocator.create(Value.Payload.Function);1261 zir.dumpZir(mod.gpa, "fn_type", decl.name, fn_type_scope.instructions.items) catch {};
1262 }
11831263
1184 const fn_zir: zir.Body = blk: {1264 // We need the memory for the Type to go into the arena for the Decl
1185 // We put the ZIR inside the Decl arena.1265 var decl_arena = std.heap.ArenaAllocator.init(mod.gpa);
1186 var gen_scope: Scope.GenZIR = .{1266 errdefer decl_arena.deinit();
1187 .decl = decl,1267 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
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 }
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 or1274 var block_scope: Scope.Block = .{
1228 !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn())1275 .parent = null,
1229 {1276 .inst_table = &inst_table,
1230 const src = tree.token_locs[body_block.rbrace].start;1277 .func = null,
1231 _ = try astgen.addZIRNoOp(self, &gen_scope.base, src, .return_void);1278 .owner_decl = decl,
1232 }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) {1288 const fn_type = try zir_sema.analyzeBodyValueAsType(mod, &block_scope, fn_type_inst, .{
1235 zir.dumpZir(self.gpa, "fn_body", decl.name, gen_scope.instructions.items) catch {};1289 .instructions = fn_type_scope.instructions.items,
1236 }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 .{1297 tvm.deinit(mod.gpa);
1239 .instructions = try gen_scope.arena.dupe(*zir.Inst, gen_scope.instructions.items),1298 }
1240 };1299 const fn_val = try Value.Tag.extern_fn.create(&decl_arena.allocator, decl);
1241 };
12421300
1243 const is_inline = fn_type.fnCallingConvention() == .Inline;1301 decl_arena_state.* = decl_arena.state;
1244 const anal_state = ([2]Fn.Analysis{ .queued, .inline_only })[@boolToInt(is_inline)];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.* = .{1311 try mod.comp.bin_file.allocateDeclIndexes(decl);
1247 .state = anal_state,1312 try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl });
1248 .zir = fn_zir,
1249 .body = undefined,
1250 .owner_decl = decl,
1251 };
1252 fn_payload.* = .{
1253 .base = .{ .tag = .function },
1254 .data = new_func,
1255 };
12561313
1257 var prev_type_has_bits = false;1314 if (type_changed and mod.emit_h != null) {
1258 var prev_is_inline = false;1315 try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl });
1259 var type_changed = true;1316 }
12601317
1261 if (decl.typedValueManaged()) |tvm| {1318 return type_changed;
1262 prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits();1319 }
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 }
12681320
1269 tvm.deinit(self.gpa);1321 const new_func = try decl_arena.allocator.create(Fn);
1270 }1322 const fn_payload = try decl_arena.allocator.create(Value.Payload.Function);
12711323
1272 decl_arena_state.* = decl_arena.state;1324 const fn_zir: zir.Body = blk: {
1273 decl.typed_value = .{1325 // We put the ZIR inside the Decl arena.
1274 .most_recent = .{1326 var gen_scope: Scope.GenZIR = .{
1275 .typed_value = .{1327 .decl = decl,
1276 .ty = fn_type,1328 .arena = &decl_arena.allocator,
1277 .val = Value.initPayload(&fn_payload.base),1329 .parent = &decl.container.base,
1278 },1330 .force_comptime = false,
1279 .arena = decl_arena_state,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,
1280 },1351 },
1352 .kw_args = .{},
1281 };1353 };
1282 decl.analysis = .complete;1354 gen_scope.instructions.items[i] = &arg.base;
1283 decl.generation = self.generation;1355 const sub_scope = try decl_arena.allocator.create(Scope.LocalVal);
12841356 sub_scope.* = .{
1285 if (!is_inline and fn_type.hasCodeGenBits()) {1357 .parent = params_scope,
1286 // We don't fully codegen the decl until later, but we do need to reserve a global1358 .gen_zir = &gen_scope,
1287 // offset table index for it. This allows us to codegen decls out of dependency order,1359 .name = param_name,
1288 // increasing how many computations can be done in parallel.1360 .inst = &arg.base,
1289 try self.comp.bin_file.allocateDeclIndexes(decl);1361 };
1290 try self.comp.work_queue.writeItem(.{ .codegen_decl = decl });1362 params_scope = &sub_scope.base;
1291 if (type_changed and self.emit_h != null) {1363 }
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 }
12971364
1298 if (fn_proto.getExternExportInlineToken()) |maybe_export_token| {1365 _ = try astgen.expr(mod, params_scope, .none, body_node);
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);
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 Decl1374 if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
1323 var decl_arena = std.heap.ArenaAllocator.init(self.gpa);1375 zir.dumpZir(mod.gpa, "fn_body", decl.name, gen_scope.instructions.items) catch {};
1324 errdefer decl_arena.deinit();1376 }
1325 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
13261377
1327 var decl_inst_table = Scope.Block.InstTable.init(self.gpa);1378 break :blk .{
1328 defer decl_inst_table.deinit();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 = .{1386 new_func.* = .{
1333 .parent = null,1387 .state = anal_state,
1334 .inst_table = &decl_inst_table,1388 .zir = fn_zir,
1335 .func = null,1389 .body = undefined,
1336 .owner_decl = decl,1390 .owner_decl = decl,
1337 .src_decl = decl,1391 };
1338 .instructions = .{},1392 fn_payload.* = .{
1339 .arena = &decl_arena.allocator,1393 .base = .{ .tag = .function },
1340 .inlining = null,1394 .data = new_func,
1341 .is_comptime = true,1395 };
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 }
13741396
1375 const var_info: struct { ty: Type, val: ?Value } = if (var_decl.getInitNode()) |init_node| vi: {1397 var prev_type_has_bits = false;
1376 var gen_scope_arena = std.heap.ArenaAllocator.init(self.gpa);1398 var prev_is_inline = false;
1377 defer gen_scope_arena.deinit();1399 var type_changed = true;
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);
13851400
1386 const init_result_loc: astgen.ResultLoc = if (var_decl.getTypeNode()) |type_node| rl: {1401 if (decl.typedValueManaged()) |tvm| {
1387 const src = tree.token_locs[type_node.firstToken()].start;1402 prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits();
1388 const type_type = try astgen.addZIRInstConst(self, &gen_scope.base, src, .{1403 type_changed = !tvm.typed_value.ty.eql(fn_type);
1389 .ty = Type.initTag(.type),1404 if (tvm.typed_value.val.castTag(.function)) |payload| {
1390 .val = Value.initTag(.type_type),1405 const prev_func = payload.data;
1391 });1406 prev_is_inline = prev_func.state == .inline_only;
1392 const var_type = try astgen.expr(self, &gen_scope.base, .{ .ty = type_type }, type_node);1407 }
1393 break :rl .{ .ty = var_type };
1394 } else .none;
13951408
1396 const init_inst = try astgen.comptimeExpr(self, &gen_scope.base, init_result_loc, init_node);1409 tvm.deinit(mod.gpa);
1397 if (std.builtin.mode == .Debug and self.comp.verbose_ir) {1410 }
1398 zir.dumpZir(self.gpa, "var_init", decl.name, gen_scope.instructions.items) catch {};
1399 }
14001411
1401 var var_inst_table = Scope.Block.InstTable.init(self.gpa);1412 decl_arena_state.* = decl_arena.state;
1402 defer var_inst_table.deinit();1413 decl.typed_value = .{
14031414 .most_recent = .{
1404 var branch_quota_vi: u32 = default_eval_branch_quota;1415 .typed_value = .{
1405 var inner_block: Scope.Block = .{1416 .ty = fn_type,
1406 .parent = null,1417 .val = Value.initPayload(&fn_payload.base),
1407 .inst_table = &var_inst_table,1418 },
1408 .func = null,1419 .arena = decl_arena_state,
1409 .owner_decl = decl,1420 },
1410 .src_decl = decl,1421 };
1411 .instructions = .{},1422 decl.analysis = .complete;
1412 .arena = &gen_scope_arena.allocator,1423 decl.generation = mod.generation;
1413 .inlining = null,1424
1414 .is_comptime = true,1425 if (!is_inline and fn_type.hasCodeGenBits()) {
1415 .branch_quota = &branch_quota_vi,1426 // We don't fully codegen the decl until later, but we do need to reserve a global
1416 };1427 // offset table index for it. This allows us to codegen decls out of dependency order,
1417 defer inner_block.instructions.deinit(self.gpa);1428 // increasing how many computations can be done in parallel.
1418 try zir_sema.analyzeBody(self, &inner_block, .{1429 try mod.comp.bin_file.allocateDeclIndexes(decl);
1419 .instructions = gen_scope.instructions.items,1430 try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl });
1420 });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.1457fn astgenAndSemaVarDecl(
1423 const analyzed_init_inst = var_inst_table.get(init_inst).?;1458 mod: *Module,
1424 // The is_comptime in the Scope.Block guarantees the result is comptime-known.1459 decl: *Decl,
1425 const val = analyzed_init_inst.value().?;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);1466 decl.analysis = .in_progress;
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);
14451467
1446 const var_type = try astgen.typeExpr(self, &type_scope.base, type_node);1468 const token_starts = tree.tokens.items(.start);
1447 if (std.builtin.mode == .Debug and self.comp.verbose_ir) {1469 const token_tags = tree.tokens.items(.tag);
1448 zir.dumpZir(self.gpa, "var_type", decl.name, type_scope.instructions.items) catch {};
1449 }
14501470
1451 const ty = try zir_sema.analyzeBodyValueAsType(self, &block_scope, var_type, .{1471 // We need the memory for the Type to go into the arena for the Decl
1452 .instructions = type_scope.instructions.items,1472 var decl_arena = std.heap.ArenaAllocator.init(mod.gpa);
1453 });1473 errdefer decl_arena.deinit();
1454 break :vi .{1474 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
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 };
14611475
1462 if (is_mutable and !var_info.ty.isValidVarType(is_extern)) {1476 var decl_inst_table = Scope.Block.InstTable.init(mod.gpa);
1463 return self.failTok(&block_scope.base, var_decl.firstToken(), "variable of type '{}' must be const", .{var_info.ty});1477 defer decl_inst_table.deinit();
1464 }
14651478
1466 var type_changed = true;1479 var branch_quota: u32 = default_eval_branch_quota;
1467 if (decl.typedValueManaged()) |tvm| {
1468 type_changed = !tvm.typed_value.ty.eql(var_info.ty);
14691480
1470 tvm.deinit(self.gpa);1481 var block_scope: Scope.Block = .{
1471 }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);1538 const var_info: struct { ty: Type, val: ?Value } = if (var_decl.ast.init_node != 0) vi: {
1474 new_variable.* = .{1539 var gen_scope_arena = std.heap.ArenaAllocator.init(mod.gpa);
1475 .owner_decl = decl,1540 defer gen_scope_arena.deinit();
1476 .init = var_info.val orelse undefined,1541 var gen_scope: Scope.GenZIR = .{
1477 .is_extern = is_extern,1542 .decl = decl,
1478 .is_mutable = is_mutable,1543 .arena = &gen_scope_arena.allocator,
1479 .is_threadlocal = is_threadlocal,1544 .parent = &decl.container.base,
1480 };1545 .force_comptime = true,
1481 const var_val = try Value.Tag.variable.create(&decl_arena.allocator, new_variable);1546 };
14821547 defer gen_scope.instructions.deinit(mod.gpa);
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);
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 deinit1570 var var_inst_table = Scope.Block.InstTable.init(mod.gpa);
1513 // this arena after analysis is done.1571 defer var_inst_table.deinit();
1514 var analysis_arena = std.heap.ArenaAllocator.init(self.gpa);1572
1515 defer analysis_arena.deinit();1573 var branch_quota_vi: u32 = default_eval_branch_quota;
1516 var gen_scope: Scope.GenZIR = .{1574 var inner_block: Scope.Block = .{
1517 .decl = decl,1575 .parent = null,
1518 .arena = &analysis_arena.allocator,1576 .inst_table = &var_inst_table,
1519 .parent = &decl.container.base,1577 .func = null,
1520 .force_comptime = true,1578 .owner_decl = decl,
1521 };1579 .src_decl = decl,
1522 defer gen_scope.instructions.deinit(self.gpa);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);1591 // The result location guarantees the type coercion.
1525 if (std.builtin.mode == .Debug and self.comp.verbose_ir) {1592 const analyzed_init_inst = var_inst_table.get(init_inst).?;
1526 zir.dumpZir(self.gpa, "comptime_block", decl.name, gen_scope.instructions.items) catch {};1593 // The is_comptime in the Scope.Block guarantees the result is comptime-known.
1527 }1594 const val = analyzed_init_inst.value().?;
15281595
1529 var inst_table = Scope.Block.InstTable.init(self.gpa);1596 const ty = try analyzed_init_inst.ty.copy(block_scope.arena);
1530 defer inst_table.deinit();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 = .{1626 const ty = try zir_sema.analyzeBodyValueAsType(mod, &block_scope, var_type, .{
1535 .parent = null,1627 .instructions = type_scope.instructions.items,
1536 .inst_table = &inst_table,1628 });
1537 .func = null,1629 break :vi .{
1538 .owner_decl = decl,1630 .ty = ty,
1539 .src_decl = decl,1631 .val = null,
1540 .instructions = .{},1632 };
1541 .arena = &analysis_arena.allocator,1633 } else {
1542 .inlining = null,1634 return mod.failTok(
1543 .is_comptime = true,1635 &block_scope.base,
1544 .branch_quota = &branch_quota,1636 var_decl.ast.mut_token,
1545 };1637 "unable to infer variable type",
1546 defer block_scope.instructions.deinit(self.gpa);1638 .{},
1639 );
1640 };
15471641
1548 _ = try zir_sema.analyzeBody(self, &block_scope, .{1642 if (is_mutable and !var_info.ty.isValidVarType(is_extern)) {
1549 .instructions = gen_scope.instructions.items,1643 return mod.failTok(
1550 });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;1651 var type_changed = true;
1553 decl.generation = self.generation;1652 if (decl.typedValueManaged()) |tvm| {
1554 return true;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,
1555 },1676 },
1556 .Use => @panic("TODO usingnamespace decl"),1677 };
1557 else => unreachable,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 }
1558 }1689 }
1690 return type_changed;
1559}1691}
15601692
1561fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void {1693fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void {
...@@ -1566,7 +1698,7 @@ fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void...@@ -1566,7 +1698,7 @@ fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void
1566 dependee.dependants.putAssumeCapacity(depender, {});1698 dependee.dependants.putAssumeCapacity(depender, {});
1567}1699}
15681700
1569pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {1701pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*const ast.Tree {
1570 const tracy = trace(@src());1702 const tracy = trace(@src());
1571 defer tracy.end();1703 defer tracy.end();
15721704
...@@ -1577,8 +1709,10 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {...@@ -1577,8 +1709,10 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {
1577 const source = try root_scope.getSource(self);1709 const source = try root_scope.getSource(self);
15781710
1579 var keep_tree = false;1711 var keep_tree = false;
1580 const tree = try std.zig.parse(self.gpa, source);1712 root_scope.tree = try std.zig.parse(self.gpa, source);
1581 defer if (!keep_tree) tree.deinit();1713 defer if (!keep_tree) root_scope.tree.deinit(self.gpa);
1714
1715 const tree = &root_scope.tree;
15821716
1583 if (tree.errors.len != 0) {1717 if (tree.errors.len != 0) {
1584 const parse_err = tree.errors[0];1718 const parse_err = tree.errors[0];
...@@ -1586,12 +1720,12 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {...@@ -1586,12 +1720,12 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {
1586 var msg = std.ArrayList(u8).init(self.gpa);1720 var msg = std.ArrayList(u8).init(self.gpa);
1587 defer msg.deinit();1721 defer msg.deinit();
15881722
1589 try parse_err.render(tree.token_ids, msg.writer());1723 try tree.renderError(parse_err, msg.writer());
1590 const err_msg = try self.gpa.create(ErrorMsg);1724 const err_msg = try self.gpa.create(ErrorMsg);
1591 err_msg.* = .{1725 err_msg.* = .{
1592 .src_loc = .{1726 .src_loc = .{
1593 .file_scope = root_scope,1727 .file_scope = root_scope,
1594 .byte_offset = tree.token_locs[parse_err.loc()].start,1728 .byte_offset = tree.tokens.items(.start)[parse_err.token],
1595 },1729 },
1596 .msg = msg.toOwnedSlice(),1730 .msg = msg.toOwnedSlice(),
1597 };1731 };
...@@ -1602,7 +1736,6 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {...@@ -1602,7 +1736,6 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {
1602 }1736 }
16031737
1604 root_scope.status = .loaded_success;1738 root_scope.status = .loaded_success;
1605 root_scope.contents = .{ .tree = tree };
1606 keep_tree = true;1739 keep_tree = true;
16071740
1608 return tree;1741 return tree;
...@@ -1610,151 +1743,353 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {...@@ -1610,151 +1743,353 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {
16101743
1611 .unloaded_parse_failure => return error.AnalysisFail,1744 .unloaded_parse_failure => return error.AnalysisFail,
16121745
1613 .loaded_success => return root_scope.contents.tree,1746 .loaded_success => return &root_scope.tree,
1614 }1747 }
1615}1748}
16161749
1617pub fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void {1750pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void {
1618 const tracy = trace(@src());1751 const tracy = trace(@src());
1619 defer tracy.end();1752 defer tracy.end();
16201753
1621 // We may be analyzing it for the first time, or this may be1754 // We may be analyzing it for the first time, or this may be
1622 // an incremental update. This code handles both cases.1755 // an incremental update. This code handles both cases.
1623 const tree = try self.getAstTree(container_scope.file_scope);1756 const tree = try mod.getAstTree(container_scope.file_scope);
1624 const decls = tree.root_node.decls();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);1761 try mod.comp.work_queue.ensureUnusedCapacity(decls.len);
1627 try container_scope.decls.ensureCapacity(self.gpa, decls.len);1762 try container_scope.decls.ensureCapacity(mod.gpa, decls.len);
16281763
1629 // Keep track of the decls that we expect to see in this file so that1764 // Keep track of the decls that we expect to see in this file so that
1630 // we know which ones have been deleted.1765 // 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);
1632 defer deleted_decls.deinit();1767 defer deleted_decls.deinit();
1633 try deleted_decls.ensureCapacity(container_scope.decls.items().len);1768 try deleted_decls.ensureCapacity(container_scope.decls.items().len);
1634 for (container_scope.decls.items()) |entry| {1769 for (container_scope.decls.items()) |entry| {
1635 deleted_decls.putAssumeCapacityNoClobber(entry.key, {});1770 deleted_decls.putAssumeCapacityNoClobber(entry.key, {});
1636 }1771 }
16371772
1638 for (decls) |src_decl, decl_i| {1773 for (decls) |decl_node, decl_i| switch (node_tags[decl_node]) {
1639 if (src_decl.cast(ast.Node.FnProto)) |fn_proto| {1774 .fn_decl => {
1640 // We will create a Decl for it regardless of analysis status.1775 const fn_proto = node_datas[decl_node].lhs;
1641 const name_tok = fn_proto.getNameToken() orelse {1776 const body = node_datas[decl_node].rhs;
1642 @panic("TODO missing function name");1777 switch (node_tags[fn_proto]) {
1643 };1778 .fn_proto_simple => {
16441779 var params: [1]ast.Node.Index = undefined;
1645 const name_loc = tree.token_locs[name_tok];1780 try mod.semaContainerFn(
1646 const name = tree.tokenSliceLoc(name_loc);1781 container_scope,
1647 const name_hash = container_scope.fullyQualifiedNameHash(name);1782 &deleted_decls,
1648 const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));1783 decl_node,
1649 if (self.decl_table.get(name_hash)) |decl| {1784 decl_i,
1650 // Update the AST Node index of the decl, even if its contents are unchanged, it may1785 tree.*,
1651 // have been re-ordered.1786 body,
1652 decl.src_index = decl_i;1787 tree.fnProtoSimple(&params, fn_proto),
1653 if (deleted_decls.swapRemove(decl) == null) {1788 );
1654 decl.analysis = .sema_failure;1789 },
1655 const msg = try ErrorMsg.create(self.gpa, .{1790 .fn_proto_multi => try mod.semaContainerFn(
1656 .file_scope = container_scope.file_scope,1791 container_scope,
1657 .byte_offset = tree.token_locs[name_tok].start,1792 &deleted_decls,
1658 }, "redefinition of '{s}'", .{decl.name});1793 decl_node,
1659 errdefer msg.destroy(self.gpa);1794 decl_i,
1660 try self.failed_decls.putNoClobber(self.gpa, decl, msg);1795 tree.*,
1661 } else {1796 body,
1662 if (!srcHashEql(decl.contents_hash, contents_hash)) {1797 tree.fnProtoMulti(fn_proto),
1663 try self.markOutdatedDecl(decl);1798 ),
1664 decl.contents_hash = contents_hash;1799 .fn_proto_one => {
1665 } else switch (self.comp.bin_file.tag) {1800 var params: [1]ast.Node.Index = undefined;
1666 .coff => {1801 try mod.semaContainerFn(
1667 // TODO Implement for COFF1802 container_scope,
1668 },1803 &deleted_decls,
1669 .elf => if (decl.fn_link.elf.len != 0) {1804 decl_node,
1670 // TODO Look into detecting when this would be unnecessary by storing enough state1805 decl_i,
1671 // in `Decl` to notice that the line number did not change.1806 tree.*,
1672 self.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });1807 body,
1673 },1808 tree.fnProtoOne(&params, fn_proto),
1674 .macho => if (decl.fn_link.macho.len != 0) {1809 );
1675 // TODO Look into detecting when this would be unnecessary by storing enough state1810 },
1676 // in `Decl` to notice that the line number did not change.1811 .fn_proto => try mod.semaContainerFn(
1677 self.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });1812 container_scope,
1678 },1813 &deleted_decls,
1679 .c, .wasm, .spirv => {},1814 decl_node,
1680 }1815 decl_i,
1681 }1816 tree.*,
1682 } else {1817 body,
1683 const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);1818 tree.fnProto(fn_proto),
1684 container_scope.decls.putAssumeCapacity(new_decl, {});1819 ),
1685 if (fn_proto.getExternExportInlineToken()) |maybe_export_token| {1820 else => unreachable,
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 }
1720 }1821 }
1721 } else if (src_decl.castTag(.Comptime)) |comptime_node| {1822 },
1722 const name_index = self.getNextAnonNameIndex();1823 .fn_proto_simple => {
1723 const name = try std.fmt.allocPrint(self.gpa, "__comptime_{d}", .{name_index});1824 var params: [1]ast.Node.Index = undefined;
1724 defer self.gpa.free(name);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
1726 const name_hash = container_scope.fullyQualifiedNameHash(name);1904 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);
1730 container_scope.decls.putAssumeCapacity(new_decl, {});1908 container_scope.decls.putAssumeCapacity(new_decl, {});
1731 self.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });1909 mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
1732 } else if (src_decl.castTag(.ContainerField)) |container_field| {1910 },
1733 log.err("TODO: analyze container field", .{});1911
1734 } else if (src_decl.castTag(.TestDecl)) |test_decl| {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 => {
1735 log.err("TODO: analyze test decl", .{});1938 log.err("TODO: analyze test decl", .{});
1736 } else if (src_decl.castTag(.Use)) |use_decl| {1939 },
1940 .@"usingnamespace" => {
1737 log.err("TODO: analyze usingnamespace decl", .{});1941 log.err("TODO: analyze usingnamespace decl", .{});
1738 } else {1942 },
1739 unreachable;1943 else => unreachable,
1740 }1944 };
1741 }
1742 // Handle explicitly deleted decls from the source code. Not to be confused1945 // Handle explicitly deleted decls from the source code. Not to be confused
1743 // with when we delete decls because they are no longer referenced.1946 // with when we delete decls because they are no longer referenced.
1744 for (deleted_decls.items()) |entry| {1947 for (deleted_decls.items()) |entry| {
1745 log.debug("noticed '{s}' deleted from source\n", .{entry.key.name});1948 log.debug("noticed '{s}' deleted from source", .{entry.key.name});
1746 try self.deleteDecl(entry.key);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 }
1747 }2064 }
1748}2065}
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
1750pub fn deleteDecl(self: *Module, decl: *Decl) !void {2082pub fn deleteDecl(self: *Module, decl: *Decl) !void {
2083 const tracy = trace(@src());
2084 defer tracy.end();
2085
1751 try self.deletion_set.ensureCapacity(self.gpa, self.deletion_set.items.len + decl.dependencies.items().len);2086 try self.deletion_set.ensureCapacity(self.gpa, self.deletion_set.items.len + decl.dependencies.items().len);
17522087
1753 // Remove from the namespace it resides in. In the case of an anonymous Decl it will2088 // Remove from the namespace it resides in. In the case of an anonymous Decl it will
1754 // not be present in the set, and this does nothing.2089 // not be present in the set, and this does nothing.
1755 decl.container.removeDecl(decl);2090 decl.container.removeDecl(decl);
17562091
1757 log.debug("deleting decl '{s}'\n", .{decl.name});2092 log.debug("deleting decl '{s}'", .{decl.name});
1758 const name_hash = decl.fullyQualifiedNameHash();2093 const name_hash = decl.fullyQualifiedNameHash();
1759 self.decl_table.removeAssertDiscard(name_hash);2094 self.decl_table.removeAssertDiscard(name_hash);
1760 // Remove itself from its dependencies, because we are about to destroy the decl pointer.2095 // 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 {...@@ -1856,18 +2191,18 @@ pub fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
1856 defer inner_block.instructions.deinit(self.gpa);2191 defer inner_block.instructions.deinit(self.gpa);
18572192
1858 func.state = .in_progress;2193 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
1861 try zir_sema.analyzeBody(self, &inner_block, func.zir);2196 try zir_sema.analyzeBody(self, &inner_block, func.zir);
18622197
1863 const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items);2198 const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items);
1864 func.state = .success;2199 func.state = .success;
1865 func.body = .{ .instructions = instructions };2200 func.body = .{ .instructions = instructions };
1866 log.debug("set {s} to success\n", .{decl.name});2201 log.debug("set {s} to success", .{decl.name});
1867}2202}
18682203
1869fn markOutdatedDecl(self: *Module, decl: *Decl) !void {2204fn markOutdatedDecl(self: *Module, decl: *Decl) !void {
1870 log.debug("mark {s} outdated\n", .{decl.name});2205 log.debug("mark {s} outdated", .{decl.name});
1871 try self.comp.work_queue.writeItem(.{ .analyze_decl = decl });2206 try self.comp.work_queue.writeItem(.{ .analyze_decl = decl });
1872 if (self.failed_decls.swapRemove(decl)) |entry| {2207 if (self.failed_decls.swapRemove(decl)) |entry| {
1873 entry.value.destroy(self.gpa);2208 entry.value.destroy(self.gpa);
...@@ -2395,15 +2730,16 @@ pub fn createContainerDecl(...@@ -2395,15 +2730,16 @@ pub fn createContainerDecl(
2395fn getAnonTypeName(self: *Module, scope: *Scope, base_token: std.zig.ast.TokenIndex) ![]u8 {2730fn getAnonTypeName(self: *Module, scope: *Scope, base_token: std.zig.ast.TokenIndex) ![]u8 {
2396 // TODO add namespaces, generic function signatrues2731 // TODO add namespaces, generic function signatrues
2397 const tree = scope.tree();2732 const tree = scope.tree();
2398 const base_name = switch (tree.token_ids[base_token]) {2733 const token_tags = tree.tokens.items(.tag);
2399 .Keyword_struct => "struct",2734 const base_name = switch (token_tags[base_token]) {
2400 .Keyword_enum => "enum",2735 .keyword_struct => "struct",
2401 .Keyword_union => "union",2736 .keyword_enum => "enum",
2402 .Keyword_opaque => "opaque",2737 .keyword_union => "union",
2738 .keyword_opaque => "opaque",
2403 else => unreachable,2739 else => unreachable,
2404 };2740 };
2405 const loc = tree.tokenLocationLoc(0, tree.token_locs[base_token]);2741 const loc = tree.tokenLocation(0, base_token);
2406 return std.fmt.allocPrint(self.gpa, "{s}:{}:{}", .{ base_name, loc.line, loc.column });2742 return std.fmt.allocPrint(self.gpa, "{s}:{d}:{d}", .{ base_name, loc.line, loc.column });
2407}2743}
24082744
2409fn getNextAnonNameIndex(self: *Module) usize {2745fn getNextAnonNameIndex(self: *Module) usize {
...@@ -2639,7 +2975,7 @@ pub fn analyzeImport(self: *Module, scope: *Scope, src: usize, target_string: []...@@ -2639,7 +2975,7 @@ pub fn analyzeImport(self: *Module, scope: *Scope, src: usize, target_string: []
2639 file_scope.* = .{2975 file_scope.* = .{
2640 .sub_file_path = resolved_path,2976 .sub_file_path = resolved_path,
2641 .source = .{ .unloaded = {} },2977 .source = .{ .unloaded = {} },
2642 .contents = .{ .not_available = {} },2978 .tree = undefined,
2643 .status = .never_loaded,2979 .status = .never_loaded,
2644 .pkg = found_pkg orelse cur_pkg,2980 .pkg = found_pkg orelse cur_pkg,
2645 .root_container = .{2981 .root_container = .{
...@@ -3149,18 +3485,19 @@ pub fn failTok(...@@ -3149,18 +3485,19 @@ pub fn failTok(
3149 comptime format: []const u8,3485 comptime format: []const u8,
3150 args: anytype,3486 args: anytype,
3151) InnerError {3487) InnerError {
3152 const src = scope.tree().token_locs[token_index].start;3488 const src = scope.tree().tokens.items(.start)[token_index];
3153 return self.fail(scope, src, format, args);3489 return self.fail(scope, src, format, args);
3154}3490}
31553491
3156pub fn failNode(3492pub fn failNode(
3157 self: *Module,3493 self: *Module,
3158 scope: *Scope,3494 scope: *Scope,
3159 ast_node: *ast.Node,3495 ast_node: ast.Node.Index,
3160 comptime format: []const u8,3496 comptime format: []const u8,
3161 args: anytype,3497 args: anytype,
3162) InnerError {3498) 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)];
3164 return self.fail(scope, src, format, args);3501 return self.fail(scope, src, format, args);
3165}3502}
31663503
...@@ -3594,6 +3931,9 @@ pub fn validateVarType(mod: *Module, scope: *Scope, src: usize, ty: Type) !void...@@ -3594,6 +3931,9 @@ pub fn validateVarType(mod: *Module, scope: *Scope, src: usize, ty: Type) !void
3594/// Identifier token -> String (allocated in scope.arena())3931/// Identifier token -> String (allocated in scope.arena())
3595pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 {3932pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 {
3596 const tree = scope.tree();3933 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
3598 const ident_name = tree.tokenSlice(token);3938 const ident_name = tree.tokenSlice(token);
3599 if (mem.startsWith(u8, ident_name, "@")) {3939 if (mem.startsWith(u8, ident_name, "@")) {
...@@ -3602,7 +3942,7 @@ pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex)...@@ -3602,7 +3942,7 @@ pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex)
3602 return std.zig.parseStringLiteral(scope.arena(), raw_string, &bad_index) catch |err| switch (err) {3942 return std.zig.parseStringLiteral(scope.arena(), raw_string, &bad_index) catch |err| switch (err) {
3603 error.InvalidCharacter => {3943 error.InvalidCharacter => {
3604 const bad_byte = raw_string[bad_index];3944 const bad_byte = raw_string[bad_index];
3605 const src = tree.token_locs[token].start;3945 const src = token_starts[token];
3606 return mod.fail(scope, src + 1 + bad_index, "invalid string literal character: '{c}'\n", .{bad_byte});3946 return mod.fail(scope, src + 1 + bad_index, "invalid string literal character: '{c}'\n", .{bad_byte});
3607 },3947 },
3608 else => |e| return e,3948 else => |e| return e,
src/astgen.zig+1908-1427
...@@ -1,16 +1,18 @@...@@ -1,16 +1,18 @@
1const std = @import("std");1const std = @import("std");
2const mem = std.mem;2const mem = std.mem;
3const Allocator = std.mem.Allocator;3const Allocator = std.mem.Allocator;
4const assert = std.debug.assert;
5
4const Value = @import("value.zig").Value;6const Value = @import("value.zig").Value;
5const Type = @import("type.zig").Type;7const Type = @import("type.zig").Type;
6const TypedValue = @import("TypedValue.zig");8const TypedValue = @import("TypedValue.zig");
7const assert = std.debug.assert;
8const zir = @import("zir.zig");9const zir = @import("zir.zig");
9const Module = @import("Module.zig");10const Module = @import("Module.zig");
10const ast = std.zig.ast;11const ast = std.zig.ast;
11const trace = @import("tracy.zig").trace;12const trace = @import("tracy.zig").trace;
12const Scope = Module.Scope;13const Scope = Module.Scope;
13const InnerError = Module.InnerError;14const InnerError = Module.InnerError;
15const BuiltinFn = @import("BuiltinFn.zig");
1416
15pub const ResultLoc = union(enum) {17pub const ResultLoc = union(enum) {
16 /// The expression is the right-hand side of assignment to `_`. Only the side-effects of the18 /// 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) {...@@ -55,8 +57,11 @@ pub const ResultLoc = union(enum) {
55 };57 };
56};58};
5759
58pub fn typeExpr(mod: *Module, scope: *Scope, type_node: *ast.Node) InnerError!*zir.Inst {60pub fn typeExpr(mod: *Module, scope: *Scope, type_node: ast.Node.Index) InnerError!*zir.Inst {
59 const type_src = scope.tree().token_locs[type_node.firstToken()].start;61 const tree = scope.tree();
62 const token_starts = tree.tokens.items(.start);
63
64 const type_src = token_starts[tree.firstToken(type_node)];
60 const type_type = try addZIRInstConst(mod, scope, type_src, .{65 const type_type = try addZIRInstConst(mod, scope, type_src, .{
61 .ty = Type.initTag(.type),66 .ty = Type.initTag(.type),
62 .val = Value.initTag(.type_type),67 .val = Value.initTag(.type_type),
...@@ -65,134 +70,191 @@ pub fn typeExpr(mod: *Module, scope: *Scope, type_node: *ast.Node) InnerError!*z...@@ -65,134 +70,191 @@ pub fn typeExpr(mod: *Module, scope: *Scope, type_node: *ast.Node) InnerError!*z
65 return expr(mod, scope, type_rl, type_node);70 return expr(mod, scope, type_rl, type_node);
66}71}
6772
68fn lvalExpr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst {73fn lvalExpr(mod: *Module, scope: *Scope, node: ast.Node.Index) InnerError!*zir.Inst {
69 switch (node.tag) {74 const tree = scope.tree();
70 .Root => unreachable,75 const node_tags = tree.nodes.items(.tag);
71 .Use => unreachable,76 const main_tokens = tree.nodes.items(.main_token);
72 .TestDecl => unreachable,77 switch (node_tags[node]) {
73 .DocComment => unreachable,78 .root => unreachable,
74 .VarDecl => unreachable,79 .@"usingnamespace" => unreachable,
75 .SwitchCase => unreachable,80 .test_decl => unreachable,
76 .SwitchElse => unreachable,81 .global_var_decl => unreachable,
77 .Else => unreachable,82 .local_var_decl => unreachable,
78 .Payload => unreachable,83 .simple_var_decl => unreachable,
79 .PointerPayload => unreachable,84 .aligned_var_decl => unreachable,
80 .PointerIndexPayload => unreachable,85 .switch_case => unreachable,
81 .ErrorTag => unreachable,86 .switch_case_one => unreachable,
82 .FieldInitializer => unreachable,87 .container_field_init => unreachable,
83 .ContainerField => unreachable,88 .container_field_align => unreachable,
8489 .container_field => unreachable,
85 .Assign,90 .asm_output => unreachable,
86 .AssignBitAnd,91 .asm_input => unreachable,
87 .AssignBitOr,92
88 .AssignBitShiftLeft,93 .assign,
89 .AssignBitShiftRight,94 .assign_bit_and,
90 .AssignBitXor,95 .assign_bit_or,
91 .AssignDiv,96 .assign_bit_shift_left,
92 .AssignSub,97 .assign_bit_shift_right,
93 .AssignSubWrap,98 .assign_bit_xor,
94 .AssignMod,99 .assign_div,
95 .AssignAdd,100 .assign_sub,
96 .AssignAddWrap,101 .assign_sub_wrap,
97 .AssignMul,102 .assign_mod,
98 .AssignMulWrap,103 .assign_add,
99 .Add,104 .assign_add_wrap,
100 .AddWrap,105 .assign_mul,
101 .Sub,106 .assign_mul_wrap,
102 .SubWrap,107 .add,
103 .Mul,108 .add_wrap,
104 .MulWrap,109 .sub,
105 .Div,110 .sub_wrap,
106 .Mod,111 .mul,
107 .BitAnd,112 .mul_wrap,
108 .BitOr,113 .div,
109 .BitShiftLeft,114 .mod,
110 .BitShiftRight,115 .bit_and,
111 .BitXor,116 .bit_or,
112 .BangEqual,117 .bit_shift_left,
113 .EqualEqual,118 .bit_shift_right,
114 .GreaterThan,119 .bit_xor,
115 .GreaterOrEqual,120 .bang_equal,
116 .LessThan,121 .equal_equal,
117 .LessOrEqual,122 .greater_than,
118 .ArrayCat,123 .greater_or_equal,
119 .ArrayMult,124 .less_than,
120 .BoolAnd,125 .less_or_equal,
121 .BoolOr,126 .array_cat,
122 .Asm,127 .array_mult,
123 .StringLiteral,128 .bool_and,
124 .IntegerLiteral,129 .bool_or,
125 .Call,130 .@"asm",
126 .Unreachable,131 .asm_simple,
127 .Return,132 .string_literal,
128 .If,133 .integer_literal,
129 .While,134 .call,
130 .BoolNot,135 .call_comma,
131 .AddressOf,136 .async_call,
132 .FloatLiteral,137 .async_call_comma,
133 .UndefinedLiteral,138 .call_one,
134 .BoolLiteral,139 .call_one_comma,
135 .NullLiteral,140 .async_call_one,
136 .OptionalType,141 .async_call_one_comma,
137 .Block,142 .unreachable_literal,
138 .LabeledBlock,143 .@"return",
139 .Break,144 .@"if",
140 .PtrType,145 .if_simple,
141 .ArrayType,146 .@"while",
142 .ArrayTypeSentinel,147 .while_simple,
143 .EnumLiteral,148 .while_cont,
144 .MultilineStringLiteral,149 .bool_not,
145 .CharLiteral,150 .address_of,
146 .Defer,151 .float_literal,
147 .Catch,152 .undefined_literal,
148 .ErrorUnion,153 .true_literal,
149 .MergeErrorSets,154 .false_literal,
150 .Range,155 .null_literal,
151 .Await,156 .optional_type,
152 .BitNot,157 .block,
153 .Negation,158 .block_semicolon,
154 .NegationWrap,159 .block_two,
155 .Resume,160 .block_two_semicolon,
156 .Try,161 .@"break",
157 .SliceType,162 .ptr_type_aligned,
158 .Slice,163 .ptr_type_sentinel,
159 .ArrayInitializer,164 .ptr_type,
160 .ArrayInitializerDot,165 .ptr_type_bit_range,
161 .StructInitializer,166 .array_type,
162 .StructInitializerDot,167 .array_type_sentinel,
163 .Switch,168 .enum_literal,
164 .For,169 .multiline_string_literal,
165 .Suspend,170 .char_literal,
166 .Continue,171 .@"defer",
167 .AnyType,172 .@"errdefer",
168 .ErrorType,173 .@"catch",
169 .FnProto,174 .error_union,
170 .AnyFrameType,175 .merge_error_sets,
171 .ErrorSetDecl,176 .switch_range,
172 .ContainerDecl,177 .@"await",
173 .Comptime,178 .bit_not,
174 .Nosuspend,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,
175 => return mod.failNode(scope, node, "invalid left-hand side to assignment", .{}),232 => return mod.failNode(scope, node, "invalid left-hand side to assignment", .{}),
176233
177 // @field can be assigned to234 .builtin_call,
178 .BuiltinCall => {235 .builtin_call_comma,
179 const call = node.castTag(.BuiltinCall).?;236 .builtin_call_two,
180 const tree = scope.tree();237 .builtin_call_two_comma,
181 const builtin_name = tree.tokenSlice(call.builtin_token);238 => {
182239 const builtin_token = main_tokens[node];
183 if (!mem.eql(u8, builtin_name, "@field")) {240 const builtin_name = tree.tokenSlice(builtin_token);
184 return mod.failNode(scope, node, "invalid left-hand side to assignment", .{});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 }
185 }247 }
186 },248 },
187249
188 // can be assigned to250 // These can be assigned to.
189 .UnwrapOptional,251 .unwrap_optional,
190 .Deref,252 .deref,
191 .Period,253 .field_access,
192 .ArrayAccess,254 .array_access,
193 .Identifier,255 .identifier,
194 .GroupedExpression,256 .grouped_expression,
195 .OrElse,257 .@"orelse",
196 => {},258 => {},
197 }259 }
198 return expr(mod, scope, .ref, node);260 return expr(mod, scope, .ref, node);
...@@ -202,154 +264,403 @@ fn lvalExpr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst {...@@ -202,154 +264,403 @@ fn lvalExpr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst {
202/// When `rl` is discard, ptr, inferred_ptr, bitcasted_ptr, or inferred_ptr, the264/// When `rl` is discard, ptr, inferred_ptr, bitcasted_ptr, or inferred_ptr, the
203/// result instruction can be used to inspect whether it is isNoReturn() but that is it,265/// result instruction can be used to inspect whether it is isNoReturn() but that is it,
204/// it must otherwise not be used.266/// it must otherwise not be used.
205pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerError!*zir.Inst {267pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!*zir.Inst {
206 switch (node.tag) {268 const tree = scope.tree();
207 .Root => unreachable, // Top-level declaration.269 const main_tokens = tree.nodes.items(.main_token);
208 .Use => unreachable, // Top-level declaration.270 const token_tags = tree.tokens.items(.tag);
209 .TestDecl => unreachable, // Top-level declaration.271 const node_datas = tree.nodes.items(.data);
210 .DocComment => unreachable, // Top-level declaration.272 const node_tags = tree.nodes.items(.tag);
211 .VarDecl => unreachable, // Handled in `blockExpr`.273 const token_starts = tree.tokens.items(.start);
212 .SwitchCase => unreachable, // Handled in `switchExpr`.274
213 .SwitchElse => unreachable, // Handled in `switchExpr`.275 switch (node_tags[node]) {
214 .Range => unreachable, // Handled in `switchExpr`.276 .root => unreachable, // Top-level declaration.
215 .Else => unreachable, // Handled explicitly the control flow expression functions.277 .@"usingnamespace" => unreachable, // Top-level declaration.
216 .Payload => unreachable, // Handled explicitly.278 .test_decl => unreachable, // Top-level declaration.
217 .PointerPayload => unreachable, // Handled explicitly.279 .container_field_init => unreachable, // Top-level declaration.
218 .PointerIndexPayload => unreachable, // Handled explicitly.280 .container_field_align => unreachable, // Top-level declaration.
219 .ErrorTag => unreachable, // Handled explicitly.281 .container_field => unreachable, // Top-level declaration.
220 .FieldInitializer => unreachable, // Handled explicitly.282 .fn_decl => unreachable, // Top-level declaration.
221 .ContainerField => unreachable, // Handled explicitly.283
222284 .global_var_decl => unreachable, // Handled in `blockExpr`.
223 .Assign => return rvalueVoid(mod, scope, rl, node, try assign(mod, scope, node.castTag(.Assign).?)),285 .local_var_decl => unreachable, // Handled in `blockExpr`.
224 .AssignBitAnd => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitAnd).?, .bit_and)),286 .simple_var_decl => unreachable, // Handled in `blockExpr`.
225 .AssignBitOr => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitOr).?, .bit_or)),287 .aligned_var_decl => unreachable, // Handled in `blockExpr`.
226 .AssignBitShiftLeft => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitShiftLeft).?, .shl)),288
227 .AssignBitShiftRight => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitShiftRight).?, .shr)),289 .switch_case => unreachable, // Handled in `switchExpr`.
228 .AssignBitXor => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitXor).?, .xor)),290 .switch_case_one => unreachable, // Handled in `switchExpr`.
229 .AssignDiv => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignDiv).?, .div)),291 .switch_range => unreachable, // Handled in `switchExpr`.
230 .AssignSub => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignSub).?, .sub)),292
231 .AssignSubWrap => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignSubWrap).?, .subwrap)),293 .asm_output => unreachable, // Handled in `asmExpr`.
232 .AssignMod => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignMod).?, .mod_rem)),294 .asm_input => unreachable, // Handled in `asmExpr`.
233 .AssignAdd => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignAdd).?, .add)),295
234 .AssignAddWrap => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignAddWrap).?, .addwrap)),296 .assign => return rvalueVoid(mod, scope, rl, node, try assign(mod, scope, node)),
235 .AssignMul => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignMul).?, .mul)),297 .assign_bit_and => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .bit_and)),
236 .AssignMulWrap => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignMulWrap).?, .mulwrap)),298 .assign_bit_or => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .bit_or)),
237299 .assign_bit_shift_left => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .shl)),
238 .Add => return simpleBinOp(mod, scope, rl, node.castTag(.Add).?, .add),300 .assign_bit_shift_right => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .shr)),
239 .AddWrap => return simpleBinOp(mod, scope, rl, node.castTag(.AddWrap).?, .addwrap),301 .assign_bit_xor => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .xor)),
240 .Sub => return simpleBinOp(mod, scope, rl, node.castTag(.Sub).?, .sub),302 .assign_div => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .div)),
241 .SubWrap => return simpleBinOp(mod, scope, rl, node.castTag(.SubWrap).?, .subwrap),303 .assign_sub => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .sub)),
242 .Mul => return simpleBinOp(mod, scope, rl, node.castTag(.Mul).?, .mul),304 .assign_sub_wrap => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .subwrap)),
243 .MulWrap => return simpleBinOp(mod, scope, rl, node.castTag(.MulWrap).?, .mulwrap),305 .assign_mod => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .mod_rem)),
244 .Div => return simpleBinOp(mod, scope, rl, node.castTag(.Div).?, .div),306 .assign_add => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .add)),
245 .Mod => return simpleBinOp(mod, scope, rl, node.castTag(.Mod).?, .mod_rem),307 .assign_add_wrap => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .addwrap)),
246 .BitAnd => return simpleBinOp(mod, scope, rl, node.castTag(.BitAnd).?, .bit_and),308 .assign_mul => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .mul)),
247 .BitOr => return simpleBinOp(mod, scope, rl, node.castTag(.BitOr).?, .bit_or),309 .assign_mul_wrap => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node, .mulwrap)),
248 .BitShiftLeft => return simpleBinOp(mod, scope, rl, node.castTag(.BitShiftLeft).?, .shl),310
249 .BitShiftRight => return simpleBinOp(mod, scope, rl, node.castTag(.BitShiftRight).?, .shr),311 .add => return simpleBinOp(mod, scope, rl, node, .add),
250 .BitXor => return simpleBinOp(mod, scope, rl, node.castTag(.BitXor).?, .xor),312 .add_wrap => return simpleBinOp(mod, scope, rl, node, .addwrap),
251313 .sub => return simpleBinOp(mod, scope, rl, node, .sub),
252 .BangEqual => return simpleBinOp(mod, scope, rl, node.castTag(.BangEqual).?, .cmp_neq),314 .sub_wrap => return simpleBinOp(mod, scope, rl, node, .subwrap),
253 .EqualEqual => return simpleBinOp(mod, scope, rl, node.castTag(.EqualEqual).?, .cmp_eq),315 .mul => return simpleBinOp(mod, scope, rl, node, .mul),
254 .GreaterThan => return simpleBinOp(mod, scope, rl, node.castTag(.GreaterThan).?, .cmp_gt),316 .mul_wrap => return simpleBinOp(mod, scope, rl, node, .mulwrap),
255 .GreaterOrEqual => return simpleBinOp(mod, scope, rl, node.castTag(.GreaterOrEqual).?, .cmp_gte),317 .div => return simpleBinOp(mod, scope, rl, node, .div),
256 .LessThan => return simpleBinOp(mod, scope, rl, node.castTag(.LessThan).?, .cmp_lt),318 .mod => return simpleBinOp(mod, scope, rl, node, .mod_rem),
257 .LessOrEqual => return simpleBinOp(mod, scope, rl, node.castTag(.LessOrEqual).?, .cmp_lte),319 .bit_and => return simpleBinOp(mod, scope, rl, node, .bit_and),
258320 .bit_or => return simpleBinOp(mod, scope, rl, node, .bit_or),
259 .ArrayCat => return simpleBinOp(mod, scope, rl, node.castTag(.ArrayCat).?, .array_cat),321 .bit_shift_left => return simpleBinOp(mod, scope, rl, node, .shl),
260 .ArrayMult => return simpleBinOp(mod, scope, rl, node.castTag(.ArrayMult).?, .array_mul),322 .bit_shift_right => return simpleBinOp(mod, scope, rl, node, .shr),
261323 .bit_xor => return simpleBinOp(mod, scope, rl, node, .xor),
262 .BoolAnd => return boolBinOp(mod, scope, rl, node.castTag(.BoolAnd).?),324
263 .BoolOr => return boolBinOp(mod, scope, rl, node.castTag(.BoolOr).?),325 .bang_equal => return simpleBinOp(mod, scope, rl, node, .cmp_neq),
264326 .equal_equal => return simpleBinOp(mod, scope, rl, node, .cmp_eq),
265 .BoolNot => return rvalue(mod, scope, rl, try boolNot(mod, scope, node.castTag(.BoolNot).?)),327 .greater_than => return simpleBinOp(mod, scope, rl, node, .cmp_gt),
266 .BitNot => return rvalue(mod, scope, rl, try bitNot(mod, scope, node.castTag(.BitNot).?)),328 .greater_or_equal => return simpleBinOp(mod, scope, rl, node, .cmp_gte),
267 .Negation => return rvalue(mod, scope, rl, try negation(mod, scope, node.castTag(.Negation).?, .sub)),329 .less_than => return simpleBinOp(mod, scope, rl, node, .cmp_lt),
268 .NegationWrap => return rvalue(mod, scope, rl, try negation(mod, scope, node.castTag(.NegationWrap).?, .subwrap)),330 .less_or_equal => return simpleBinOp(mod, scope, rl, node, .cmp_lte),
269331
270 .Identifier => return try identifier(mod, scope, rl, node.castTag(.Identifier).?),332 .array_cat => return simpleBinOp(mod, scope, rl, node, .array_cat),
271 .Asm => return rvalue(mod, scope, rl, try assembly(mod, scope, node.castTag(.Asm).?)),333 .array_mult => return simpleBinOp(mod, scope, rl, node, .array_mul),
272 .StringLiteral => return rvalue(mod, scope, rl, try stringLiteral(mod, scope, node.castTag(.StringLiteral).?)),334
273 .IntegerLiteral => return rvalue(mod, scope, rl, try integerLiteral(mod, scope, node.castTag(.IntegerLiteral).?)),335 .bool_and => return boolBinOp(mod, scope, rl, node, true),
274 .BuiltinCall => return builtinCall(mod, scope, rl, node.castTag(.BuiltinCall).?),336 .bool_or => return boolBinOp(mod, scope, rl, node, false),
275 .Call => return callExpr(mod, scope, rl, node.castTag(.Call).?),337
276 .Unreachable => return unreach(mod, scope, node.castTag(.Unreachable).?),338 .bool_not => return rvalue(mod, scope, rl, try boolNot(mod, scope, node)),
277 .Return => return ret(mod, scope, node.castTag(.Return).?),339 .bit_not => return rvalue(mod, scope, rl, try bitNot(mod, scope, node)),
278 .If => return ifExpr(mod, scope, rl, node.castTag(.If).?),340 .negation => return rvalue(mod, scope, rl, try negation(mod, scope, node, .sub)),
279 .While => return whileExpr(mod, scope, rl, node.castTag(.While).?),341 .negation_wrap => return rvalue(mod, scope, rl, try negation(mod, scope, node, .subwrap)),
280 .Period => return field(mod, scope, rl, node.castTag(.Period).?),342
281 .Deref => return rvalue(mod, scope, rl, try deref(mod, scope, node.castTag(.Deref).?)),343 .identifier => return identifier(mod, scope, rl, node),
282 .AddressOf => return rvalue(mod, scope, rl, try addressOf(mod, scope, node.castTag(.AddressOf).?)),344
283 .FloatLiteral => return rvalue(mod, scope, rl, try floatLiteral(mod, scope, node.castTag(.FloatLiteral).?)),345 .asm_simple => return asmExpr(mod, scope, rl, tree.asmSimple(node)),
284 .UndefinedLiteral => return rvalue(mod, scope, rl, try undefLiteral(mod, scope, node.castTag(.UndefinedLiteral).?)),346 .@"asm" => return asmExpr(mod, scope, rl, tree.asmFull(node)),
285 .BoolLiteral => return rvalue(mod, scope, rl, try boolLiteral(mod, scope, node.castTag(.BoolLiteral).?)),347
286 .NullLiteral => return rvalue(mod, scope, rl, try nullLiteral(mod, scope, node.castTag(.NullLiteral).?)),348 .string_literal => return stringLiteral(mod, scope, rl, node),
287 .OptionalType => return rvalue(mod, scope, rl, try optionalType(mod, scope, node.castTag(.OptionalType).?)),349 .multiline_string_literal => return multilineStringLiteral(mod, scope, rl, node),
288 .UnwrapOptional => return unwrapOptional(mod, scope, rl, node.castTag(.UnwrapOptional).?),350
289 .Block => return rvalueVoid(mod, scope, rl, node, try blockExpr(mod, scope, node.castTag(.Block).?)),351 .integer_literal => return integerLiteral(mod, scope, rl, node),
290 .LabeledBlock => return labeledBlockExpr(mod, scope, rl, node.castTag(.LabeledBlock).?, .block),352
291 .Break => return rvalue(mod, scope, rl, try breakExpr(mod, scope, node.castTag(.Break).?)),353 .builtin_call_two, .builtin_call_two_comma => {
292 .Continue => return rvalue(mod, scope, rl, try continueExpr(mod, scope, node.castTag(.Continue).?)),354 if (node_datas[node].lhs == 0) {
293 .PtrType => return rvalue(mod, scope, rl, try ptrType(mod, scope, node.castTag(.PtrType).?)),355 const params = [_]ast.Node.Index{};
294 .GroupedExpression => return expr(mod, scope, rl, node.castTag(.GroupedExpression).?.expr),356 return builtinCall(mod, scope, rl, node, &params);
295 .ArrayType => return rvalue(mod, scope, rl, try arrayType(mod, scope, node.castTag(.ArrayType).?)),357 } else if (node_datas[node].rhs == 0) {
296 .ArrayTypeSentinel => return rvalue(mod, scope, rl, try arrayTypeSentinel(mod, scope, node.castTag(.ArrayTypeSentinel).?)),358 const params = [_]ast.Node.Index{node_datas[node].lhs};
297 .EnumLiteral => return rvalue(mod, scope, rl, try enumLiteral(mod, scope, node.castTag(.EnumLiteral).?)),359 return builtinCall(mod, scope, rl, node, &params);
298 .MultilineStringLiteral => return rvalue(mod, scope, rl, try multilineStrLiteral(mod, scope, node.castTag(.MultilineStringLiteral).?)),360 } else {
299 .CharLiteral => return rvalue(mod, scope, rl, try charLiteral(mod, scope, node.castTag(.CharLiteral).?)),361 const params = [_]ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs };
300 .SliceType => return rvalue(mod, scope, rl, try sliceType(mod, scope, node.castTag(.SliceType).?)),362 return builtinCall(mod, scope, rl, node, &params);
301 .ErrorUnion => return rvalue(mod, scope, rl, try typeInixOp(mod, scope, node.castTag(.ErrorUnion).?, .error_union_type)),363 }
302 .MergeErrorSets => return rvalue(mod, scope, rl, try typeInixOp(mod, scope, node.castTag(.MergeErrorSets).?, .merge_error_sets)),364 },
303 .AnyFrameType => return rvalue(mod, scope, rl, try anyFrameType(mod, scope, node.castTag(.AnyFrameType).?)),365 .builtin_call, .builtin_call_comma => {
304 .ErrorSetDecl => return rvalue(mod, scope, rl, try errorSetDecl(mod, scope, node.castTag(.ErrorSetDecl).?)),366 const params = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
305 .ErrorType => return rvalue(mod, scope, rl, try errorType(mod, scope, node.castTag(.ErrorType).?)),367 return builtinCall(mod, scope, rl, node, params);
306 .For => return forExpr(mod, scope, rl, node.castTag(.For).?),368 },
307 .ArrayAccess => return arrayAccess(mod, scope, rl, node.castTag(.ArrayAccess).?),369
308 .Slice => return rvalue(mod, scope, rl, try sliceExpr(mod, scope, node.castTag(.Slice).?)),370 .call_one, .call_one_comma, .async_call_one, .async_call_one_comma => {
309 .Catch => return catchExpr(mod, scope, rl, node.castTag(.Catch).?),371 var params: [1]ast.Node.Index = undefined;
310 .Comptime => return comptimeKeyword(mod, scope, rl, node.castTag(.Comptime).?),372 return callExpr(mod, scope, rl, tree.callOne(&params, node));
311 .OrElse => return orelseExpr(mod, scope, rl, node.castTag(.OrElse).?),373 },
312 .Switch => return switchExpr(mod, scope, rl, node.castTag(.Switch).?),374 .call, .call_comma, .async_call, .async_call_comma => {
313 .ContainerDecl => return containerDecl(mod, scope, rl, node.castTag(.ContainerDecl).?),375 return callExpr(mod, scope, rl, tree.callFull(node));
314376 },
315 .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}),377
316 .Await => return mod.failNode(scope, node, "TODO implement astgen.expr for .Await", .{}),378 .unreachable_literal => {
317 .Resume => return mod.failNode(scope, node, "TODO implement astgen.expr for .Resume", .{}),379 const main_token = main_tokens[node];
318 .Try => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}),380 const src = token_starts[main_token];
319 .ArrayInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializer", .{}),381 return addZIRNoOp(mod, scope, src, .unreachable_safe);
320 .ArrayInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializerDot", .{}),382 },
321 .StructInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializer", .{}),383 .@"return" => return ret(mod, scope, node),
322 .StructInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializerDot", .{}),384 .field_access => return fieldAccess(mod, scope, rl, node),
323 .Suspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Suspend", .{}),385 .float_literal => return floatLiteral(mod, scope, rl, node),
324 .AnyType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyType", .{}),386
325 .FnProto => return mod.failNode(scope, node, "TODO implement astgen.expr for .FnProto", .{}),387 .if_simple => return ifExpr(mod, scope, rl, tree.ifSimple(node)),
326 .Nosuspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Nosuspend", .{}),388 .@"if" => return ifExpr(mod, scope, rl, tree.ifFull(node)),
327 }389
328}390 .while_simple => return whileExpr(mod, scope, rl, tree.whileSimple(node)),
329391 .while_cont => return whileExpr(mod, scope, rl, tree.whileCont(node)),
330fn comptimeKeyword(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Comptime) InnerError!*zir.Inst {392 .@"while" => return whileExpr(mod, scope, rl, tree.whileFull(node)),
331 const tracy = trace(@src());393
332 defer tracy.end();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 }
335}649}
336650
337pub fn comptimeExpr(651pub fn comptimeExpr(
338 mod: *Module,652 mod: *Module,
339 parent_scope: *Scope,653 parent_scope: *Scope,
340 rl: ResultLoc,654 rl: ResultLoc,
341 node: *ast.Node,655 node: ast.Node.Index,
342) InnerError!*zir.Inst {656) InnerError!*zir.Inst {
343 // If we are already in a comptime scope, no need to make another one.657 // If we are already in a comptime scope, no need to make another one.
344 if (parent_scope.isComptime()) {658 if (parent_scope.isComptime()) {
345 return expr(mod, parent_scope, rl, node);659 return expr(mod, parent_scope, rl, node);
346 }660 }
347661
348 // Optimization for labeled blocks: don't need to have 2 layers of blocks,662 const tree = parent_scope.tree();
349 // we can reuse the existing one.663 const token_starts = tree.tokens.items(.start);
350 if (node.castTag(.LabeledBlock)) |block_node| {
351 return labeledBlockExpr(mod, parent_scope, rl, block_node, .block_comptime);
352 }
353664
354 // Make a scope to collect generated instructions in the sub-expression.665 // Make a scope to collect generated instructions in the sub-expression.
355 var block_scope: Scope.GenZIR = .{666 var block_scope: Scope.GenZIR = .{
...@@ -365,9 +676,7 @@ pub fn comptimeExpr(...@@ -365,9 +676,7 @@ pub fn comptimeExpr(
365 // instruction is the block's result value.676 // instruction is the block's result value.
366 _ = try expr(mod, &block_scope.base, rl, node);677 _ = try expr(mod, &block_scope.base, rl, node);
367678
368 const tree = parent_scope.tree();679 const src = token_starts[tree.firstToken(node)];
369 const src = tree.token_locs[node.firstToken()].start;
370
371 const block = try addZIRInstBlock(mod, parent_scope, src, .block_comptime_flat, .{680 const block = try addZIRInstBlock(mod, parent_scope, src, .block_comptime_flat, .{
372 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),681 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
373 });682 });
...@@ -378,10 +687,17 @@ pub fn comptimeExpr(...@@ -378,10 +687,17 @@ pub fn comptimeExpr(
378fn breakExpr(687fn breakExpr(
379 mod: *Module,688 mod: *Module,
380 parent_scope: *Scope,689 parent_scope: *Scope,
381 node: *ast.Node.ControlFlowExpression,690 rl: ResultLoc,
691 node: ast.Node.Index,
382) InnerError!*zir.Inst {692) InnerError!*zir.Inst {
383 const tree = parent_scope.tree();693 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
386 // Look for the label in the scope.702 // Look for the label in the scope.
387 var scope = parent_scope;703 var scope = parent_scope;
...@@ -391,7 +707,7 @@ fn breakExpr(...@@ -391,7 +707,7 @@ fn breakExpr(
391 const gen_zir = scope.cast(Scope.GenZIR).?;707 const gen_zir = scope.cast(Scope.GenZIR).?;
392708
393 const block_inst = blk: {709 const block_inst = blk: {
394 if (node.getLabel()) |break_label| {710 if (break_label != 0) {
395 if (gen_zir.label) |*label| {711 if (gen_zir.label) |*label| {
396 if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) {712 if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) {
397 label.used = true;713 label.used = true;
...@@ -405,11 +721,12 @@ fn breakExpr(...@@ -405,11 +721,12 @@ fn breakExpr(
405 continue;721 continue;
406 };722 };
407723
408 const rhs = node.getRHS() orelse {724 if (rhs == 0) {
409 return addZirInstTag(mod, parent_scope, src, .break_void, .{725 const result = try addZirInstTag(mod, parent_scope, src, .break_void, .{
410 .block = block_inst,726 .block = block_inst,
411 });727 });
412 };728 return rvalue(mod, parent_scope, rl, result);
729 }
413 gen_zir.break_count += 1;730 gen_zir.break_count += 1;
414 const prev_rvalue_rl_count = gen_zir.rvalue_rl_count;731 const prev_rvalue_rl_count = gen_zir.rvalue_rl_count;
415 const operand = try expr(mod, parent_scope, gen_zir.break_result_loc, rhs);732 const operand = try expr(mod, parent_scope, gen_zir.break_result_loc, rhs);
...@@ -429,11 +746,11 @@ fn breakExpr(...@@ -429,11 +746,11 @@ fn breakExpr(
429 try gen_zir.labeled_store_to_block_ptr_list.append(mod.gpa, store_inst);746 try gen_zir.labeled_store_to_block_ptr_list.append(mod.gpa, store_inst);
430 }747 }
431 }748 }
432 return br;749 return rvalue(mod, parent_scope, rl, br);
433 },750 },
434 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,751 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
435 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,752 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
436 else => if (node.getLabel()) |break_label| {753 else => if (break_label != 0) {
437 const label_name = try mod.identifierTokenString(parent_scope, break_label);754 const label_name = try mod.identifierTokenString(parent_scope, break_label);
438 return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name});755 return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name});
439 } else {756 } else {
...@@ -443,9 +760,19 @@ fn breakExpr(...@@ -443,9 +760,19 @@ fn breakExpr(
443 }760 }
444}761}
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 {
447 const tree = parent_scope.tree();769 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
450 // Look for the label in the scope.777 // Look for the label in the scope.
451 var scope = parent_scope;778 var scope = parent_scope;
...@@ -457,7 +784,7 @@ fn continueExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowE...@@ -457,7 +784,7 @@ fn continueExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowE
457 scope = gen_zir.parent;784 scope = gen_zir.parent;
458 continue;785 continue;
459 };786 };
460 if (node.getLabel()) |break_label| blk: {787 if (break_label != 0) blk: {
461 if (gen_zir.label) |*label| {788 if (gen_zir.label) |*label| {
462 if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) {789 if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) {
463 label.used = true;790 label.used = true;
...@@ -469,13 +796,14 @@ fn continueExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowE...@@ -469,13 +796,14 @@ fn continueExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowE
469 continue;796 continue;
470 }797 }
471798
472 return addZirInstTag(mod, parent_scope, src, .break_void, .{799 const result = try addZirInstTag(mod, parent_scope, src, .break_void, .{
473 .block = continue_block,800 .block = continue_block,
474 });801 });
802 return rvalue(mod, parent_scope, rl, result);
475 },803 },
476 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,804 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
477 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,805 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
478 else => if (node.getLabel()) |break_label| {806 else => if (break_label != 0) {
479 const label_name = try mod.identifierTokenString(parent_scope, break_label);807 const label_name = try mod.identifierTokenString(parent_scope, break_label);
480 return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name});808 return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name});
481 } else {809 } else {
...@@ -485,11 +813,27 @@ fn continueExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowE...@@ -485,11 +813,27 @@ fn continueExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowE
485 }813 }
486}814}
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 {
489 const tracy = trace(@src());823 const tracy = trace(@src());
490 defer tracy.end();824 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, {});
493}837}
494838
495fn checkLabelRedefinition(mod: *Module, parent_scope: *Scope, label: ast.TokenIndex) !void {839fn checkLabelRedefinition(mod: *Module, parent_scope: *Scope, label: ast.TokenIndex) !void {
...@@ -502,8 +846,11 @@ fn checkLabelRedefinition(mod: *Module, parent_scope: *Scope, label: ast.TokenIn...@@ -502,8 +846,11 @@ fn checkLabelRedefinition(mod: *Module, parent_scope: *Scope, label: ast.TokenIn
502 if (gen_zir.label) |prev_label| {846 if (gen_zir.label) |prev_label| {
503 if (try tokenIdentEql(mod, parent_scope, label, prev_label.token)) {847 if (try tokenIdentEql(mod, parent_scope, label, prev_label.token)) {
504 const tree = parent_scope.tree();848 const tree = parent_scope.tree();
505 const label_src = tree.token_locs[label].start;849 const main_tokens = tree.nodes.items(.main_token);
506 const prev_label_src = tree.token_locs[prev_label.token].start;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
508 const label_name = try mod.identifierTokenString(parent_scope, label);855 const label_name = try mod.identifierTokenString(parent_scope, label);
509 const msg = msg: {856 const msg = msg: {
...@@ -539,7 +886,8 @@ fn labeledBlockExpr(...@@ -539,7 +886,8 @@ fn labeledBlockExpr(
539 mod: *Module,886 mod: *Module,
540 parent_scope: *Scope,887 parent_scope: *Scope,
541 rl: ResultLoc,888 rl: ResultLoc,
542 block_node: *ast.Node.LabeledBlock,889 block_node: ast.Node.Index,
890 statements: []const ast.Node.Index,
543 zir_tag: zir.Inst.Tag,891 zir_tag: zir.Inst.Tag,
544) InnerError!*zir.Inst {892) InnerError!*zir.Inst {
545 const tracy = trace(@src());893 const tracy = trace(@src());
...@@ -548,9 +896,16 @@ fn labeledBlockExpr(...@@ -548,9 +896,16 @@ fn labeledBlockExpr(
548 assert(zir_tag == .block or zir_tag == .block_comptime);896 assert(zir_tag == .block or zir_tag == .block_comptime);
549897
550 const tree = parent_scope.tree();898 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
555 // Create the Block ZIR instruction so that we can put it into the GenZIR struct910 // Create the Block ZIR instruction so that we can put it into the GenZIR struct
556 // so that break statements can reference it.911 // so that break statements can reference it.
...@@ -575,7 +930,7 @@ fn labeledBlockExpr(...@@ -575,7 +930,7 @@ fn labeledBlockExpr(
575 .instructions = .{},930 .instructions = .{},
576 // TODO @as here is working around a stage1 miscompilation bug :(931 // TODO @as here is working around a stage1 miscompilation bug :(
577 .label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{932 .label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{
578 .token = block_node.label,933 .token = label_token,
579 .block_inst = block_inst,934 .block_inst = block_inst,
580 }),935 }),
581 };936 };
...@@ -584,10 +939,10 @@ fn labeledBlockExpr(...@@ -584,10 +939,10 @@ fn labeledBlockExpr(
584 defer block_scope.labeled_breaks.deinit(mod.gpa);939 defer block_scope.labeled_breaks.deinit(mod.gpa);
585 defer block_scope.labeled_store_to_block_ptr_list.deinit(mod.gpa);940 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
589 if (!block_scope.label.?.used) {944 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", .{});
591 }946 }
592947
593 try gen_zir.instructions.append(mod.gpa, &block_inst.base);948 try gen_zir.instructions.append(mod.gpa, &block_inst.base);
...@@ -627,37 +982,41 @@ fn labeledBlockExpr(...@@ -627,37 +982,41 @@ fn labeledBlockExpr(
627fn blockExprStmts(982fn blockExprStmts(
628 mod: *Module,983 mod: *Module,
629 parent_scope: *Scope,984 parent_scope: *Scope,
630 node: *ast.Node,985 node: ast.Node.Index,
631 statements: []*ast.Node,986 statements: []const ast.Node.Index,
632) !void {987) !void {
633 const tree = parent_scope.tree();988 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
635 var block_arena = std.heap.ArenaAllocator.init(mod.gpa);993 var block_arena = std.heap.ArenaAllocator.init(mod.gpa);
636 defer block_arena.deinit();994 defer block_arena.deinit();
637995
638 var scope = parent_scope;996 var scope = parent_scope;
639 for (statements) |statement| {997 for (statements) |statement| {
640 const src = tree.token_locs[statement.firstToken()].start;998 const src = token_starts[tree.firstToken(statement)];
641 _ = try addZIRNoOp(mod, scope, src, .dbg_stmt);999 _ = try addZIRNoOp(mod, scope, src, .dbg_stmt);
642 switch (statement.tag) {1000 switch (node_tags[statement]) {
643 .VarDecl => {1001 .global_var_decl => scope = try varDecl(mod, scope, &block_arena.allocator, tree.globalVarDecl(statement)),
644 const var_decl_node = statement.castTag(.VarDecl).?;1002 .local_var_decl => scope = try varDecl(mod, scope, &block_arena.allocator, tree.localVarDecl(statement)),
645 scope = try varDecl(mod, scope, var_decl_node, &block_arena.allocator);1003 .simple_var_decl => scope = try varDecl(mod, scope, &block_arena.allocator, tree.simpleVarDecl(statement)),
646 },1004 .aligned_var_decl => scope = try varDecl(mod, scope, &block_arena.allocator, tree.alignedVarDecl(statement)),
647 .Assign => try assign(mod, scope, statement.castTag(.Assign).?),1005
648 .AssignBitAnd => try assignOp(mod, scope, statement.castTag(.AssignBitAnd).?, .bit_and),1006 .assign => try assign(mod, scope, statement),
649 .AssignBitOr => try assignOp(mod, scope, statement.castTag(.AssignBitOr).?, .bit_or),1007 .assign_bit_and => try assignOp(mod, scope, statement, .bit_and),
650 .AssignBitShiftLeft => try assignOp(mod, scope, statement.castTag(.AssignBitShiftLeft).?, .shl),1008 .assign_bit_or => try assignOp(mod, scope, statement, .bit_or),
651 .AssignBitShiftRight => try assignOp(mod, scope, statement.castTag(.AssignBitShiftRight).?, .shr),1009 .assign_bit_shift_left => try assignOp(mod, scope, statement, .shl),
652 .AssignBitXor => try assignOp(mod, scope, statement.castTag(.AssignBitXor).?, .xor),1010 .assign_bit_shift_right => try assignOp(mod, scope, statement, .shr),
653 .AssignDiv => try assignOp(mod, scope, statement.castTag(.AssignDiv).?, .div),1011 .assign_bit_xor => try assignOp(mod, scope, statement, .xor),
654 .AssignSub => try assignOp(mod, scope, statement.castTag(.AssignSub).?, .sub),1012 .assign_div => try assignOp(mod, scope, statement, .div),
655 .AssignSubWrap => try assignOp(mod, scope, statement.castTag(.AssignSubWrap).?, .subwrap),1013 .assign_sub => try assignOp(mod, scope, statement, .sub),
656 .AssignMod => try assignOp(mod, scope, statement.castTag(.AssignMod).?, .mod_rem),1014 .assign_sub_wrap => try assignOp(mod, scope, statement, .subwrap),
657 .AssignAdd => try assignOp(mod, scope, statement.castTag(.AssignAdd).?, .add),1015 .assign_mod => try assignOp(mod, scope, statement, .mod_rem),
658 .AssignAddWrap => try assignOp(mod, scope, statement.castTag(.AssignAddWrap).?, .addwrap),1016 .assign_add => try assignOp(mod, scope, statement, .add),
659 .AssignMul => try assignOp(mod, scope, statement.castTag(.AssignMul).?, .mul),1017 .assign_add_wrap => try assignOp(mod, scope, statement, .addwrap),
660 .AssignMulWrap => try assignOp(mod, scope, statement.castTag(.AssignMulWrap).?, .mulwrap),1018 .assign_mul => try assignOp(mod, scope, statement, .mul),
1019 .assign_mul_wrap => try assignOp(mod, scope, statement, .mulwrap),
6611020
662 else => {1021 else => {
663 const possibly_unused_result = try expr(mod, scope, .none, statement);1022 const possibly_unused_result = try expr(mod, scope, .none, statement);
...@@ -672,18 +1031,23 @@ fn blockExprStmts(...@@ -672,18 +1031,23 @@ fn blockExprStmts(
672fn varDecl(1031fn varDecl(
673 mod: *Module,1032 mod: *Module,
674 scope: *Scope,1033 scope: *Scope,
675 node: *ast.Node.VarDecl,
676 block_arena: *Allocator,1034 block_arena: *Allocator,
1035 var_decl: ast.full.VarDecl,
677) InnerError!*Scope {1036) InnerError!*Scope {
678 if (node.getComptimeToken()) |comptime_token| {1037 if (var_decl.comptime_token) |comptime_token| {
679 return mod.failTok(scope, comptime_token, "TODO implement comptime locals", .{});1038 return mod.failTok(scope, comptime_token, "TODO implement comptime locals", .{});
680 }1039 }
681 if (node.getAlignNode()) |align_node| {1040 if (var_decl.ast.align_node != 0) {
682 return mod.failNode(scope, align_node, "TODO implement alignment on locals", .{});1041 return mod.failNode(scope, var_decl.ast.align_node, "TODO implement alignment on locals", .{});
683 }1042 }
684 const tree = scope.tree();1043 const tree = scope.tree();
685 const name_src = tree.token_locs[node.name_token].start;1044 const main_tokens = tree.nodes.items(.main_token);
686 const ident_name = try mod.identifierTokenString(scope, node.name_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
688 // Local variables shadowing detection, including function parameters.1052 // Local variables shadowing detection, including function parameters.
689 {1053 {
...@@ -729,20 +1093,21 @@ fn varDecl(...@@ -729,20 +1093,21 @@ fn varDecl(
729 // TODO add note for other definition1093 // TODO add note for other definition
730 return mod.fail(scope, name_src, "redefinition of '{s}'", .{ident_name});1094 return mod.fail(scope, name_src, "redefinition of '{s}'", .{ident_name});
731 }1095 }
732 const init_node = node.getInitNode() orelse1096 if (var_decl.ast.init_node == 0) {
733 return mod.fail(scope, name_src, "variables must be initialized", .{});1097 return mod.fail(scope, name_src, "variables must be initialized", .{});
1098 }
7341099
735 switch (tree.token_ids[node.mut_token]) {1100 switch (token_tags[var_decl.ast.mut_token]) {
736 .Keyword_const => {1101 .keyword_const => {
737 // Depending on the type of AST the initialization expression is, we may need an lvalue1102 // Depending on the type of AST the initialization expression is, we may need an lvalue
738 // or an rvalue as a result location. If it is an rvalue, we can use the instruction as1103 // or an rvalue as a result location. If it is an rvalue, we can use the instruction as
739 // the variable, no memory location needed.1104 // the variable, no memory location needed.
740 if (!nodeMayNeedMemoryLocation(init_node, scope)) {1105 if (!nodeMayNeedMemoryLocation(scope, var_decl.ast.init_node)) {
741 const result_loc: ResultLoc = if (node.getTypeNode()) |type_node|1106 const result_loc: ResultLoc = if (var_decl.ast.type_node != 0)
742 .{ .ty = try typeExpr(mod, scope, type_node) }1107 .{ .ty = try typeExpr(mod, scope, var_decl.ast.type_node) }
743 else1108 else
744 .none;1109 .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);
746 const sub_scope = try block_arena.create(Scope.LocalVal);1111 const sub_scope = try block_arena.create(Scope.LocalVal);
747 sub_scope.* = .{1112 sub_scope.* = .{
748 .parent = scope,1113 .parent = scope,
...@@ -766,8 +1131,8 @@ fn varDecl(...@@ -766,8 +1131,8 @@ fn varDecl(
7661131
767 var resolve_inferred_alloc: ?*zir.Inst = null;1132 var resolve_inferred_alloc: ?*zir.Inst = null;
768 var opt_type_inst: ?*zir.Inst = null;1133 var opt_type_inst: ?*zir.Inst = null;
769 if (node.getTypeNode()) |type_node| {1134 if (var_decl.ast.type_node != 0) {
770 const type_inst = try typeExpr(mod, &init_scope.base, type_node);1135 const type_inst = try typeExpr(mod, &init_scope.base, var_decl.ast.type_node);
771 opt_type_inst = type_inst;1136 opt_type_inst = type_inst;
772 init_scope.rl_ptr = try addZIRUnOp(mod, &init_scope.base, name_src, .alloc, type_inst);1137 init_scope.rl_ptr = try addZIRUnOp(mod, &init_scope.base, name_src, .alloc, type_inst);
773 } else {1138 } else {
...@@ -776,7 +1141,7 @@ fn varDecl(...@@ -776,7 +1141,7 @@ fn varDecl(
776 init_scope.rl_ptr = &alloc.base;1141 init_scope.rl_ptr = &alloc.base;
777 }1142 }
778 const init_result_loc: ResultLoc = .{ .block_ptr = &init_scope };1143 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);
780 const parent_zir = &scope.getGenZIR().instructions;1145 const parent_zir = &scope.getGenZIR().instructions;
781 if (init_scope.rvalue_rl_count == 1) {1146 if (init_scope.rvalue_rl_count == 1) {
782 // Result location pointer not used. We don't need an alloc for this1147 // Result location pointer not used. We don't need an alloc for this
...@@ -834,10 +1199,13 @@ fn varDecl(...@@ -834,10 +1199,13 @@ fn varDecl(
834 };1199 };
835 return &sub_scope.base;1200 return &sub_scope.base;
836 },1201 },
837 .Keyword_var => {1202 .keyword_var => {
838 var resolve_inferred_alloc: ?*zir.Inst = null;1203 var resolve_inferred_alloc: ?*zir.Inst = null;
839 const var_data: struct { result_loc: ResultLoc, alloc: *zir.Inst } = if (node.getTypeNode()) |type_node| a: {1204 const var_data: struct {
840 const type_inst = try typeExpr(mod, scope, type_node);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);
841 const alloc = try addZIRUnOp(mod, scope, name_src, .alloc_mut, type_inst);1209 const alloc = try addZIRUnOp(mod, scope, name_src, .alloc_mut, type_inst);
842 break :a .{ .alloc = alloc, .result_loc = .{ .ptr = alloc } };1210 break :a .{ .alloc = alloc, .result_loc = .{ .ptr = alloc } };
843 } else a: {1211 } else a: {
...@@ -845,7 +1213,7 @@ fn varDecl(...@@ -845,7 +1213,7 @@ fn varDecl(
845 resolve_inferred_alloc = &alloc.base;1213 resolve_inferred_alloc = &alloc.base;
846 break :a .{ .alloc = &alloc.base, .result_loc = .{ .inferred_ptr = alloc } };1214 break :a .{ .alloc = &alloc.base, .result_loc = .{ .inferred_ptr = alloc } };
847 };1215 };
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);
849 if (resolve_inferred_alloc) |inst| {1217 if (resolve_inferred_alloc) |inst| {
850 _ = try addZIRUnOp(mod, scope, name_src, .resolve_inferred_alloc, inst);1218 _ = try addZIRUnOp(mod, scope, name_src, .resolve_inferred_alloc, inst);
851 }1219 }
...@@ -862,232 +1230,210 @@ fn varDecl(...@@ -862,232 +1230,210 @@ fn varDecl(
862 }1230 }
863}1231}
8641232
865fn assign(mod: *Module, scope: *Scope, infix_node: *ast.Node.SimpleInfixOp) InnerError!void {1233fn assign(mod: *Module, scope: *Scope, infix_node: ast.Node.Index) InnerError!void {
866 if (infix_node.lhs.castTag(.Identifier)) |ident| {1234 const tree = scope.tree();
867 // This intentionally does not support @"_" syntax.1235 const node_datas = tree.nodes.items(.data);
868 const ident_name = scope.tree().tokenSlice(ident.token);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]);
869 if (mem.eql(u8, ident_name, "_")) {1244 if (mem.eql(u8, ident_name, "_")) {
870 _ = try expr(mod, scope, .discard, infix_node.rhs);1245 _ = try expr(mod, scope, .discard, rhs);
871 return;1246 return;
872 }1247 }
873 }1248 }
874 const lvalue = try lvalExpr(mod, scope, infix_node.lhs);1249 const lvalue = try lvalExpr(mod, scope, lhs);
875 _ = try expr(mod, scope, .{ .ptr = lvalue }, infix_node.rhs);1250 _ = try expr(mod, scope, .{ .ptr = lvalue }, rhs);
876}1251}
8771252
878fn assignOp(1253fn assignOp(
879 mod: *Module,1254 mod: *Module,
880 scope: *Scope,1255 scope: *Scope,
881 infix_node: *ast.Node.SimpleInfixOp,1256 infix_node: ast.Node.Index,
882 op_inst_tag: zir.Inst.Tag,1257 op_inst_tag: zir.Inst.Tag,
883) InnerError!void {1258) 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
889 const tree = scope.tree();1259 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]];
892 const result = try addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs);1269 const result = try addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs);
893 _ = try addZIRBinOp(mod, scope, src, .store, lhs_ptr, result);1270 _ = try addZIRBinOp(mod, scope, src, .store, lhs_ptr, result);
894}1271}
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 {
897 const tree = scope.tree();1274 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]];
899 const bool_type = try addZIRInstConst(mod, scope, src, .{1280 const bool_type = try addZIRInstConst(mod, scope, src, .{
900 .ty = Type.initTag(.type),1281 .ty = Type.initTag(.type),
901 .val = Value.initTag(.bool_type),1282 .val = Value.initTag(.bool_type),
902 });1283 });
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);
904 return addZIRUnOp(mod, scope, src, .bool_not, operand);1285 return addZIRUnOp(mod, scope, src, .bool_not, operand);
905}1286}
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 {
908 const tree = scope.tree();1289 const tree = scope.tree();
909 const src = tree.token_locs[node.op_token].start;1290 const node_datas = tree.nodes.items(.data);
910 const operand = try expr(mod, scope, .none, node.rhs);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);
911 return addZIRUnOp(mod, scope, src, .bit_not, operand);1296 return addZIRUnOp(mod, scope, src, .bit_not, operand);
912}1297}
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 {
915 const tree = scope.tree();1305 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]];
918 const lhs = try addZIRInstConst(mod, scope, src, .{1311 const lhs = try addZIRInstConst(mod, scope, src, .{
919 .ty = Type.initTag(.comptime_int),1312 .ty = Type.initTag(.comptime_int),
920 .val = Value.initTag(.zero),1313 .val = Value.initTag(.zero),
921 });1314 });
922 const rhs = try expr(mod, scope, .none, node.rhs);1315 const rhs = try expr(mod, scope, .none, node_datas[node].lhs);
923
924 return addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs);1316 return addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs);
925}1317}
9261318
927fn addressOf(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {1319fn ptrType(
928 return expr(mod, scope, .ref, node.rhs);1320 mod: *Module,
929}1321 scope: *Scope,
9301322 rl: ResultLoc,
931fn optionalType(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {1323 ptr_info: ast.full.PtrType,
932 const tree = scope.tree();1324) InnerError!*zir.Inst {
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 {
939 const tree = scope.tree();1325 const tree = scope.tree();
940 const src = tree.token_locs[node.op_token].start;1326 const token_starts = tree.tokens.items(.start);
941 return ptrSliceType(mod, scope, src, &node.ptr_info, node.rhs, .Slice);
942}
9431327
944fn ptrType(mod: *Module, scope: *Scope, node: *ast.Node.PtrType) InnerError!*zir.Inst {1328 const src = token_starts[ptr_info.ast.main_token];
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}
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 {
959 const simple = ptr_info.allowzero_token == null and1330 const simple = ptr_info.allowzero_token == null and
960 ptr_info.align_info == null and1331 ptr_info.ast.align_node == 0 and
961 ptr_info.volatile_token == null and1332 ptr_info.volatile_token == null and
962 ptr_info.sentinel == null;1333 ptr_info.ast.sentinel == 0;
9631334
964 if (simple) {1335 if (simple) {
965 const child_type = try typeExpr(mod, scope, rhs);1336 const child_type = try typeExpr(mod, scope, ptr_info.ast.child_type);
966 const mutable = ptr_info.const_token == null;1337 const mutable = ptr_info.const_token == null;
967 // TODO stage1 type inference bug
968 const T = zir.Inst.Tag;1338 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) {
970 .One => if (mutable) T.single_mut_ptr_type else T.single_const_ptr_type,1340 .One => if (mutable) T.single_mut_ptr_type else T.single_const_ptr_type,
971 .Many => if (mutable) T.many_mut_ptr_type else T.many_const_ptr_type,1341 .Many => if (mutable) T.many_mut_ptr_type else T.many_const_ptr_type,
972 .C => if (mutable) T.c_mut_ptr_type else T.c_const_ptr_type,1342 .C => if (mutable) T.c_mut_ptr_type else T.c_const_ptr_type,
973 .Slice => if (mutable) T.mut_slice_type else T.const_slice_type,1343 .Slice => if (mutable) T.mut_slice_type else T.const_slice_type,
974 }, child_type);1344 }, child_type);
1345 return rvalue(mod, scope, rl, result);
975 }1346 }
9761347
977 var kw_args: std.meta.fieldInfo(zir.Inst.PtrType, .kw_args).field_type = .{};1348 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;
979 kw_args.@"allowzero" = ptr_info.allowzero_token != null;1350 kw_args.@"allowzero" = ptr_info.allowzero_token != null;
980 if (ptr_info.align_info) |some| {1351 if (ptr_info.ast.align_node != 0) {
981 kw_args.@"align" = try expr(mod, scope, .none, some.node);1352 kw_args.@"align" = try expr(mod, scope, .none, ptr_info.ast.align_node);
982 if (some.bit_range) |bit_range| {1353 if (ptr_info.ast.bit_range_start != 0) {
983 kw_args.align_bit_start = try expr(mod, scope, .none, bit_range.start);1354 kw_args.align_bit_start = try expr(mod, scope, .none, ptr_info.ast.bit_range_start);
984 kw_args.align_bit_end = try expr(mod, scope, .none, bit_range.end);1355 kw_args.align_bit_end = try expr(mod, scope, .none, ptr_info.ast.bit_range_end);
985 }1356 }
986 }1357 }
987 kw_args.mutable = ptr_info.const_token == null;1358 kw_args.mutable = ptr_info.const_token == null;
988 kw_args.@"volatile" = ptr_info.volatile_token != null;1359 kw_args.@"volatile" = ptr_info.volatile_token != null;
989 if (ptr_info.sentinel) |some| {1360 const child_type = try typeExpr(mod, scope, ptr_info.ast.child_type);
990 kw_args.sentinel = try expr(mod, scope, .none, some);1361 if (ptr_info.ast.sentinel != 0) {
991 }1362 kw_args.sentinel = try expr(mod, scope, .{ .ty = child_type }, ptr_info.ast.sentinel);
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);
996 }1363 }
9971364 const result = try addZIRInst(mod, scope, src, zir.Inst.PtrType, .{ .child_type = child_type }, kw_args);
998 return addZIRInst(mod, scope, src, zir.Inst.PtrType, .{ .child_type = child_type }, kw_args);1365 return rvalue(mod, scope, rl, result);
999}1366}
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 {
1002 const tree = scope.tree();1369 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]];
1004 const usize_type = try addZIRInstConst(mod, scope, src, .{1375 const usize_type = try addZIRInstConst(mod, scope, src, .{
1005 .ty = Type.initTag(.type),1376 .ty = Type.initTag(.type),
1006 .val = Value.initTag(.usize_type),1377 .val = Value.initTag(.usize_type),
1007 });1378 });
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 [_]T1390 const result = try addZIRBinOp(mod, scope, src, .array_type, len, elem_type);
1010 const len = try expr(mod, scope, .{ .ty = usize_type }, node.len_expr);1391 return rvalue(mod, scope, rl, result);
1011 const elem_type = try typeExpr(mod, scope, node.rhs);1392 }
1012
1013 return addZIRBinOp(mod, scope, src, .array_type, len, elem_type);
1014}1393}
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 {
1017 const tree = scope.tree();1396 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]];
1019 const usize_type = try addZIRInstConst(mod, scope, src, .{1404 const usize_type = try addZIRInstConst(mod, scope, src, .{
1020 .ty = Type.initTag(.type),1405 .ty = Type.initTag(.type),
1021 .val = Value.initTag(.usize_type),1406 .val = Value.initTag(.usize_type),
1022 });1407 });
10231408
1024 // TODO check for [_]T1409 // TODO check for [_]T
1025 const len = try expr(mod, scope, .{ .ty = usize_type }, node.len_expr);1410 const len = try expr(mod, scope, .{ .ty = usize_type }, len_node);
1026 const sentinel_uncasted = try expr(mod, scope, .none, node.sentinel);1411 const sentinel_uncasted = try expr(mod, scope, .none, extra.sentinel);
1027 const elem_type = try typeExpr(mod, scope, node.rhs);1412 const elem_type = try typeExpr(mod, scope, extra.elem_type);
1028 const sentinel = try addZIRBinOp(mod, scope, src, .as, elem_type, sentinel_uncasted);1413 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, .{
1031 .len = len,1416 .len = len,
1032 .sentinel = sentinel,1417 .sentinel = sentinel,
1033 .elem_type = elem_type,1418 .elem_type = elem_type,
1034 }, .{});1419 }, .{});
1035}1420 return rvalue(mod, scope, rl, result);
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);
1077}1421}
10781422
1079fn containerField(1423fn containerField(
1080 mod: *Module,1424 mod: *Module,
1081 scope: *Scope,1425 scope: *Scope,
1082 node: *ast.Node.ContainerField,1426 field: ast.full.ContainerField,
1083) InnerError!*zir.Inst {1427) InnerError!*zir.Inst {
1084 const tree = scope.tree();1428 const tree = scope.tree();
1085 const src = tree.token_locs[node.firstToken()].start;1429 const token_starts = tree.tokens.items(.start);
1086 const name = try mod.identifierTokenString(scope, node.name_token);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) {1434 if (field.comptime_token == null and field.ast.value_expr == 0 and field.ast.align_expr == 0) {
1089 if (node.type_expr) |some| {1435 if (field.ast.type_expr != 0) {
1090 const ty = try typeExpr(mod, scope, some);1436 const ty = try typeExpr(mod, scope, field.ast.type_expr);
1091 return addZIRInst(mod, scope, src, zir.Inst.ContainerFieldTyped, .{1437 return addZIRInst(mod, scope, src, zir.Inst.ContainerFieldTyped, .{
1092 .bytes = name,1438 .bytes = name,
1093 .ty = ty,1439 .ty = ty,
...@@ -1099,9 +1445,11 @@ fn containerField(...@@ -1099,9 +1445,11 @@ fn containerField(
1099 }1445 }
1100 }1446 }
11011447
1102 const ty = if (node.type_expr) |some| try typeExpr(mod, scope, some) else null;1448 const ty = if (field.ast.type_expr != 0) try typeExpr(mod, scope, field.ast.type_expr) else null;
1103 const alignment = if (node.align_expr) |some| try expr(mod, scope, .none, some) else null;1449 // TODO result location should be alignment type
1104 const init = if (node.value_expr) |some| try expr(mod, scope, .none, some) else null;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
1106 return addZIRInst(mod, scope, src, zir.Inst.ContainerField, .{1454 return addZIRInst(mod, scope, src, zir.Inst.ContainerField, .{
1107 .bytes = name,1455 .bytes = name,
...@@ -1109,13 +1457,22 @@ fn containerField(...@@ -1109,13 +1457,22 @@ fn containerField(
1109 .ty = ty,1457 .ty = ty,
1110 .init = init,1458 .init = init,
1111 .alignment = alignment,1459 .alignment = alignment,
1112 .is_comptime = node.comptime_token != null,1460 .is_comptime = field.comptime_token != null,
1113 });1461 });
1114}1462}
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 {
1117 const tree = scope.tree();1470 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
1120 var gen_scope: Scope.GenZIR = .{1477 var gen_scope: Scope.GenZIR = .{
1121 .parent = scope,1478 .parent = scope,
...@@ -1129,10 +1486,16 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con...@@ -1129,10 +1486,16 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con
1129 var fields = std.ArrayList(*zir.Inst).init(mod.gpa);1486 var fields = std.ArrayList(*zir.Inst).init(mod.gpa);
1130 defer fields.deinit();1487 defer fields.deinit();
11311488
1132 for (node.fieldsAndDecls()) |fd| {1489 for (container_decl.ast.members) |member| {
1133 if (fd.castTag(.ContainerField)) |f| {1490 // TODO just handle these cases differently since they end up with different ZIR
1134 try fields.append(try containerField(mod, &gen_scope.base, f));1491 // instructions anyway. It will be simpler & have fewer branches.
1135 }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);
1136 }1499 }
11371500
1138 var decl_arena = std.heap.ArenaAllocator.init(mod.gpa);1501 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...@@ -1140,19 +1503,22 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con
1140 const arena = &decl_arena.allocator;1503 const arena = &decl_arena.allocator;
11411504
1142 var layout: std.builtin.TypeInfo.ContainerLayout = .Auto;1505 var layout: std.builtin.TypeInfo.ContainerLayout = .Auto;
1143 if (node.layout_token) |some| switch (tree.token_ids[some]) {1506 if (container_decl.layout_token) |some| switch (token_tags[some]) {
1144 .Keyword_extern => layout = .Extern,1507 .keyword_extern => layout = .Extern,
1145 .Keyword_packed => layout = .Packed,1508 .keyword_packed => layout = .Packed,
1146 else => unreachable,1509 else => unreachable,
1147 };1510 };
11481511
1149 const container_type = switch (tree.token_ids[node.kind_token]) {1512 // TODO this implementation is incorrect. The types must be created in semantic
1150 .Keyword_enum => blk: {1513 // analysis, not astgen, because the same ZIR is re-used for multiple inline function calls,
1151 const tag_type: ?*zir.Inst = switch (node.init_arg_expr) {1514 // comptime function calls, and generic function instantiations, and these
1152 .Type => |t| try typeExpr(mod, &gen_scope.base, t),1515 // must result in different instances of container types.
1153 .None => null,1516 const container_type = switch (token_tags[container_decl.ast.main_token]) {
1154 .Enum => unreachable,1517 .keyword_enum => blk: {
1155 };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;
1156 const inst = try addZIRInst(mod, &gen_scope.base, src, zir.Inst.EnumType, .{1522 const inst = try addZIRInst(mod, &gen_scope.base, src, zir.Inst.EnumType, .{
1157 .fields = try arena.dupe(*zir.Inst, fields.items),1523 .fields = try arena.dupe(*zir.Inst, fields.items),
1158 }, .{1524 }, .{
...@@ -1174,8 +1540,8 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con...@@ -1174,8 +1540,8 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con
1174 };1540 };
1175 break :blk Type.initPayload(&enum_type.base);1541 break :blk Type.initPayload(&enum_type.base);
1176 },1542 },
1177 .Keyword_struct => blk: {1543 .keyword_struct => blk: {
1178 assert(node.init_arg_expr == .None);1544 assert(container_decl.ast.arg == 0);
1179 const inst = try addZIRInst(mod, &gen_scope.base, src, zir.Inst.StructType, .{1545 const inst = try addZIRInst(mod, &gen_scope.base, src, zir.Inst.StructType, .{
1180 .fields = try arena.dupe(*zir.Inst, fields.items),1546 .fields = try arena.dupe(*zir.Inst, fields.items),
1181 }, .{1547 }, .{
...@@ -1196,22 +1562,17 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con...@@ -1196,22 +1562,17 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con
1196 };1562 };
1197 break :blk Type.initPayload(&struct_type.base);1563 break :blk Type.initPayload(&struct_type.base);
1198 },1564 },
1199 .Keyword_union => blk: {1565 .keyword_union => blk: {
1200 const init_inst = switch (node.init_arg_expr) {1566 const init_inst: ?*zir.Inst = if (container_decl.ast.arg != 0)
1201 .Enum => |e| if (e) |t| try typeExpr(mod, &gen_scope.base, t) else null,1567 try typeExpr(mod, &gen_scope.base, container_decl.ast.arg)
1202 .None => null,1568 else
1203 .Type => |t| try typeExpr(mod, &gen_scope.base, t),1569 null;
1204 };1570 const has_enum_token = container_decl.ast.enum_token != null;
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 };
1210 const inst = try addZIRInst(mod, &gen_scope.base, src, zir.Inst.UnionType, .{1571 const inst = try addZIRInst(mod, &gen_scope.base, src, zir.Inst.UnionType, .{
1211 .fields = try arena.dupe(*zir.Inst, fields.items),1572 .fields = try arena.dupe(*zir.Inst, fields.items),
1212 }, .{1573 }, .{
1213 .layout = layout,1574 .layout = layout,
1214 .init_kind = init_kind,1575 .has_enum_token = has_enum_token,
1215 .init_inst = init_inst,1576 .init_inst = init_inst,
1216 });1577 });
1217 const union_type = try arena.create(Type.Payload.Union);1578 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...@@ -1229,7 +1590,7 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con
1229 };1590 };
1230 break :blk Type.initPayload(&union_type.base);1591 break :blk Type.initPayload(&union_type.base);
1231 },1592 },
1232 .Keyword_opaque => blk: {1593 .keyword_opaque => blk: {
1233 if (fields.items.len > 0) {1594 if (fields.items.len > 0) {
1234 return mod.fail(scope, fields.items[0].src, "opaque types cannot have fields", .{});1595 return mod.fail(scope, fields.items[0].src, "opaque types cannot have fields", .{});
1235 }1596 }
...@@ -1245,7 +1606,7 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con...@@ -1245,7 +1606,7 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con
1245 else => unreachable,1606 else => unreachable,
1246 };1607 };
1247 const val = try Value.Tag.ty.create(arena, container_type);1608 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, .{
1249 .ty = Type.initTag(.type),1610 .ty = Type.initTag(.type),
1250 .val = val,1611 .val = val,
1251 });1612 });
...@@ -1258,101 +1619,69 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con...@@ -1258,101 +1619,69 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con
1258 }1619 }
1259}1620}
12601621
1261fn errorSetDecl(mod: *Module, scope: *Scope, node: *ast.Node.ErrorSetDecl) InnerError!*zir.Inst {1622fn errorSetDecl(
1262 const tree = scope.tree();1623 mod: *Module,
1263 const src = tree.token_locs[node.error_token].start;1624 scope: *Scope,
1264 const decls = node.decls();1625 rl: ResultLoc,
1265 const fields = try scope.arena().alloc([]const u8, decls.len);1626 node: ast.Node.Index,
12661627) InnerError!*zir.Inst {
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 {
1276 const tree = scope.tree();1628 const tree = scope.tree();
1277 const src = tree.token_locs[node.token].start;1629 const main_tokens = tree.nodes.items(.main_token);
1278 return addZIRInstConst(mod, scope, src, .{1630 const token_tags = tree.tokens.items(.tag);
1279 .ty = Type.initTag(.type),1631 const token_starts = tree.tokens.items(.start);
1280 .val = Value.initTag(.anyerror_type),1632
1281 });1633 // Count how many fields there are.
1282}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 {1648 const fields = try scope.arena().alloc([]const u8, count);
1285 switch (rl) {1649 {
1286 .ref => return orelseCatchExpr(1650 var tok_i = error_token + 2;
1287 mod,1651 var field_i: usize = 0;
1288 scope,1652 while (true) : (tok_i += 1) {
1289 rl,1653 switch (token_tags[tok_i]) {
1290 node.lhs,1654 .doc_comment, .comma => {},
1291 node.op_token,1655 .identifier => {
1292 .is_err_ptr,1656 fields[field_i] = try mod.identifierTokenString(scope, tok_i);
1293 .err_union_payload_unsafe_ptr,1657 field_i += 1;
1294 .err_union_code_ptr,1658 },
1295 node.rhs,1659 .r_paren => break,
1296 node.payload,1660 else => unreachable,
1297 ),1661 }
1298 else => return orelseCatchExpr(1662 }
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 ),
1339 }1663 }
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);
1340}1667}
13411668
1342fn orelseCatchExpr(1669fn orelseCatchExpr(
1343 mod: *Module,1670 mod: *Module,
1344 scope: *Scope,1671 scope: *Scope,
1345 rl: ResultLoc,1672 rl: ResultLoc,
1346 lhs: *ast.Node,1673 lhs: ast.Node.Index,
1347 op_token: ast.TokenIndex,1674 op_token: ast.TokenIndex,
1348 cond_op: zir.Inst.Tag,1675 cond_op: zir.Inst.Tag,
1349 unwrap_op: zir.Inst.Tag,1676 unwrap_op: zir.Inst.Tag,
1350 unwrap_code_op: zir.Inst.Tag,1677 unwrap_code_op: zir.Inst.Tag,
1351 rhs: *ast.Node,1678 rhs: ast.Node.Index,
1352 payload_node: ?*ast.Node,1679 payload_token: ?ast.TokenIndex,
1353) InnerError!*zir.Inst {1680) InnerError!*zir.Inst {
1354 const tree = scope.tree();1681 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
1357 var block_scope: Scope.GenZIR = .{1686 var block_scope: Scope.GenZIR = .{
1358 .parent = scope,1687 .parent = scope,
...@@ -1390,12 +1719,11 @@ fn orelseCatchExpr(...@@ -1390,12 +1719,11 @@ fn orelseCatchExpr(
13901719
1391 var err_val_scope: Scope.LocalVal = undefined;1720 var err_val_scope: Scope.LocalVal = undefined;
1392 const then_sub_scope = blk: {1721 const then_sub_scope = blk: {
1393 const payload = payload_node orelse break :blk &then_scope.base;1722 const payload = payload_token orelse break :blk &then_scope.base;
13941723 if (mem.eql(u8, tree.tokenSlice(payload), "_")) {
1395 const err_name = tree.tokenSlice(payload.castTag(.Payload).?.error_symbol.firstToken());1724 return mod.failTok(&then_scope.base, payload, "discard of error capture; omit it instead", .{});
1396 if (mem.eql(u8, err_name, "_"))1725 }
1397 break :blk &then_scope.base;1726 const err_name = try mod.identifierTokenString(scope, payload);
1398
1399 err_val_scope = .{1727 err_val_scope = .{
1400 .parent = &then_scope.base,1728 .parent = &then_scope.base,
1401 .gen_zir = &then_scope,1729 .gen_zir = &then_scope,
...@@ -1524,124 +1852,121 @@ fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: as...@@ -1524,124 +1852,121 @@ fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: as
1524 return mem.eql(u8, ident_name_1, ident_name_2);1852 return mem.eql(u8, ident_name_1, ident_name_2);
1525}1853}
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 {
1528 const tree = scope.tree();1856 const tree = scope.tree();
1529 const src = tree.token_locs[node.op_token].start;1857 const token_starts = tree.tokens.items(.start);
1530 // TODO custom AST node for field access so that we don't have to go through a node cast here1858 const main_tokens = tree.nodes.items(.main_token);
1531 const field_name = try mod.identifierTokenString(scope, node.rhs.castTag(.Identifier).?.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);
1532 if (rl == .ref) {1865 if (rl == .ref) {
1533 return addZirInstTag(mod, scope, src, .field_ptr, .{1866 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),
1535 .field_name = field_name,1868 .field_name = field_name,
1536 });1869 });
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 }));
1537 }1875 }
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 }));
1542}1876}
15431877
1544fn namedField(1878fn arrayAccess(
1545 mod: *Module,1879 mod: *Module,
1546 scope: *Scope,1880 scope: *Scope,
1547 rl: ResultLoc,1881 rl: ResultLoc,
1548 call: *ast.Node.BuiltinCall,1882 node: ast.Node.Index,
1549) InnerError!*zir.Inst {1883) InnerError!*zir.Inst {
1550 try ensureBuiltinParamCount(mod, scope, call, 2);
1551
1552 const tree = scope.tree();1884 const tree = scope.tree();
1553 const src = tree.token_locs[call.builtin_token].start;1885 const main_tokens = tree.nodes.items(.main_token);
1554 const params = call.params();1886 const token_starts = tree.tokens.items(.start);
15551887 const node_datas = tree.nodes.items(.data);
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}
15731888
1574fn arrayAccess(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.ArrayAccess) InnerError!*zir.Inst {1889 const src = token_starts[main_tokens[node]];
1575 const tree = scope.tree();
1576 const src = tree.token_locs[node.rtoken].start;
1577 const usize_type = try addZIRInstConst(mod, scope, src, .{1890 const usize_type = try addZIRInstConst(mod, scope, src, .{
1578 .ty = Type.initTag(.type),1891 .ty = Type.initTag(.type),
1579 .val = Value.initTag(.usize_type),1892 .val = Value.initTag(.usize_type),
1580 });1893 });
1581 const index_rl: ResultLoc = .{ .ty = usize_type };1894 const index_rl: ResultLoc = .{ .ty = usize_type };
15821895 switch (rl) {
1583 if (rl == .ref) {1896 .ref => return addZirInstTag(mod, scope, src, .elem_ptr, .{
1584 return addZirInstTag(mod, scope, src, .elem_ptr, .{1897 .array = try expr(mod, scope, .ref, node_datas[node].lhs),
1585 .array = try expr(mod, scope, .ref, node.lhs),1898 .index = try expr(mod, scope, index_rl, node_datas[node].rhs),
1586 .index = try expr(mod, scope, index_rl, node.index_expr),1899 }),
1587 });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 })),
1588 }1904 }
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 }));
1593}1905}
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 {
1596 const tree = scope.tree();1913 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
1599 const usize_type = try addZIRInstConst(mod, scope, src, .{1918 const usize_type = try addZIRInstConst(mod, scope, src, .{
1600 .ty = Type.initTag(.type),1919 .ty = Type.initTag(.type),
1601 .val = Value.initTag(.usize_type),1920 .val = Value.initTag(.usize_type),
1602 });1921 });
16031922
1604 const array_ptr = try expr(mod, scope, .ref, node.lhs);1923 const array_ptr = try expr(mod, scope, .ref, slice.ast.sliced);
1605 const start = try expr(mod, scope, .{ .ty = usize_type }, node.start);1924 const start = try expr(mod, scope, .{ .ty = usize_type }, slice.ast.start);
16061925
1607 if (node.end == null and node.sentinel == null) {1926 if (slice.ast.sentinel == 0) {
1608 return try addZIRBinOp(mod, scope, src, .slice_start, array_ptr, start);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 }
1609 }1939 }
16101940
1611 const end = if (node.end) |end| try expr(mod, scope, .{ .ty = usize_type }, end) else null;1941 const end = try expr(mod, scope, .{ .ty = usize_type }, slice.ast.end);
1612 // we could get the child type here, but it is easier to just do it in semantic analysis.1942 // TODO pass the proper result loc to this expression using a ZIR instruction
1613 const sentinel = if (node.sentinel) |sentinel| try expr(mod, scope, .none, sentinel) else null;1943 // "get the child element type for a slice target".
16141944 const sentinel = try expr(mod, scope, .none, slice.ast.sentinel);
1615 return try addZIRInst(1945 const result = try addZIRInst(mod, scope, src, zir.Inst.Slice, .{
1616 mod,1946 .array_ptr = array_ptr,
1617 scope,1947 .start = start,
1618 src,1948 }, .{
1619 zir.Inst.Slice,1949 .end = end,
1620 .{ .array_ptr = array_ptr, .start = start },1950 .sentinel = sentinel,
1621 .{ .end = end, .sentinel = sentinel },1951 });
1622 );1952 return rvalue(mod, scope, rl, result);
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);
1630}1953}
16311954
1632fn simpleBinOp(1955fn simpleBinOp(
1633 mod: *Module,1956 mod: *Module,
1634 scope: *Scope,1957 scope: *Scope,
1635 rl: ResultLoc,1958 rl: ResultLoc,
1636 infix_node: *ast.Node.SimpleInfixOp,1959 infix_node: ast.Node.Index,
1637 op_inst_tag: zir.Inst.Tag,1960 op_inst_tag: zir.Inst.Tag,
1638) InnerError!*zir.Inst {1961) InnerError!*zir.Inst {
1639 const tree = scope.tree();1962 const tree = scope.tree();
1640 const src = tree.token_locs[infix_node.op_token].start;1963 const node_datas = tree.nodes.items(.data);
16411964 const main_tokens = tree.nodes.items(.main_token);
1642 const lhs = try expr(mod, scope, .none, infix_node.lhs);1965 const token_starts = tree.tokens.items(.start);
1643 const rhs = try expr(mod, scope, .none, infix_node.rhs);
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]];
1645 const result = try addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs);1970 const result = try addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs);
1646 return rvalue(mod, scope, rl, result);1971 return rvalue(mod, scope, rl, result);
1647}1972}
...@@ -1650,10 +1975,15 @@ fn boolBinOp(...@@ -1650,10 +1975,15 @@ fn boolBinOp(
1650 mod: *Module,1975 mod: *Module,
1651 scope: *Scope,1976 scope: *Scope,
1652 rl: ResultLoc,1977 rl: ResultLoc,
1653 infix_node: *ast.Node.SimpleInfixOp,1978 infix_node: ast.Node.Index,
1979 is_bool_and: bool,
1654) InnerError!*zir.Inst {1980) InnerError!*zir.Inst {
1655 const tree = scope.tree();1981 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]];
1657 const bool_type = try addZIRInstConst(mod, scope, src, .{1987 const bool_type = try addZIRInstConst(mod, scope, src, .{
1658 .ty = Type.initTag(.type),1988 .ty = Type.initTag(.type),
1659 .val = Value.initTag(.bool_type),1989 .val = Value.initTag(.bool_type),
...@@ -1668,7 +1998,7 @@ fn boolBinOp(...@@ -1668,7 +1998,7 @@ fn boolBinOp(
1668 };1998 };
1669 defer block_scope.instructions.deinit(mod.gpa);1999 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);
1672 const condbr = try addZIRInstSpecial(mod, &block_scope.base, src, zir.Inst.CondBr, .{2002 const condbr = try addZIRInstSpecial(mod, &block_scope.base, src, zir.Inst.CondBr, .{
1673 .condition = lhs,2003 .condition = lhs,
1674 .then_body = undefined, // populated below2004 .then_body = undefined, // populated below
...@@ -1688,7 +2018,7 @@ fn boolBinOp(...@@ -1688,7 +2018,7 @@ fn boolBinOp(
1688 };2018 };
1689 defer rhs_scope.instructions.deinit(mod.gpa);2019 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);
1692 _ = try addZIRInst(mod, &rhs_scope.base, src, zir.Inst.Break, .{2022 _ = try addZIRInst(mod, &rhs_scope.base, src, zir.Inst.Break, .{
1693 .block = block,2023 .block = block,
1694 .operand = rhs,2024 .operand = rhs,
...@@ -1703,7 +2033,6 @@ fn boolBinOp(...@@ -1703,7 +2033,6 @@ fn boolBinOp(
1703 };2033 };
1704 defer const_scope.instructions.deinit(mod.gpa);2034 defer const_scope.instructions.deinit(mod.gpa);
17052035
1706 const is_bool_and = infix_node.base.tag == .BoolAnd;
1707 _ = try addZIRInst(mod, &const_scope.base, src, zir.Inst.Break, .{2036 _ = try addZIRInst(mod, &const_scope.base, src, zir.Inst.Break, .{
1708 .block = block,2037 .block = block,
1709 .operand = try addZIRInstConst(mod, &const_scope.base, src, .{2038 .operand = try addZIRInstConst(mod, &const_scope.base, src, .{
...@@ -1731,96 +2060,42 @@ fn boolBinOp(...@@ -1731,96 +2060,42 @@ fn boolBinOp(
1731 return rvalue(mod, scope, rl, &block.base);2060 return rvalue(mod, scope, rl, &block.base);
1732}2061}
17332062
1734const CondKind = union(enum) {2063fn ifExpr(
1735 bool,2064 mod: *Module,
1736 optional: ?*zir.Inst,2065 scope: *Scope,
1737 err_union: ?*zir.Inst,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 {2079 const tree = scope.tree();
1740 switch (self.*) {2080 const main_tokens = tree.nodes.items(.main_token);
1741 .bool => {2081 const token_starts = tree.tokens.items(.start);
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 }
17982082
1799 return mod.failNode(&else_scope.base, payload.error_symbol, "TODO implement payload symbols", .{});2083 const if_src = token_starts[if_full.ast.if_token];
1800 }
1801};
18022084
1803fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) InnerError!*zir.Inst {2085 const cond = c: {
1804 var cond_kind: CondKind = .bool;2086 // TODO https://github.com/ziglang/zig/issues/7929
1805 if (if_node.payload) |_| cond_kind = .{ .optional = null };2087 if (if_full.error_token) |error_token| {
1806 if (if_node.@"else") |else_node| {2088 return mod.failTok(scope, error_token, "TODO implement if error union", .{});
1807 if (else_node.payload) |payload| {2089 } else if (if_full.payload_token) |payload_token| {
1808 cond_kind = .{ .err_union = null };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);
1809 }2097 }
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 = .{},
1817 };2098 };
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
1825 const condbr = try addZIRInstSpecial(mod, &block_scope.base, if_src, zir.Inst.CondBr, .{2100 const condbr = try addZIRInstSpecial(mod, &block_scope.base, if_src, zir.Inst.CondBr, .{
1826 .condition = cond,2101 .condition = cond,
...@@ -1832,7 +2107,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn...@@ -1832,7 +2107,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
1832 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),2107 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
1833 });2108 });
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)];
1836 var then_scope: Scope.GenZIR = .{2111 var then_scope: Scope.GenZIR = .{
1837 .parent = scope,2112 .parent = scope,
1838 .decl = block_scope.decl,2113 .decl = block_scope.decl,
...@@ -1843,10 +2118,10 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn...@@ -1843,10 +2118,10 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
1843 defer then_scope.instructions.deinit(mod.gpa);2118 defer then_scope.instructions.deinit(mod.gpa);
18442119
1845 // declare payload to the then_scope2120 // 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
1848 block_scope.break_count += 1;2123 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);
1850 // We hold off on the break instructions as well as copying the then/else2125 // We hold off on the break instructions as well as copying the then/else
1851 // instructions into place until we know whether to keep store_to_block_ptr2126 // instructions into place until we know whether to keep store_to_block_ptr
1852 // instructions or not.2127 // instructions or not.
...@@ -1860,20 +2135,19 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn...@@ -1860,20 +2135,19 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
1860 };2135 };
1861 defer else_scope.instructions.deinit(mod.gpa);2136 defer else_scope.instructions.deinit(mod.gpa);
18622137
1863 var else_src: usize = undefined;2138 const else_node = if_full.ast.else_expr;
1864 var else_sub_scope: *Module.Scope = undefined;2139 const else_info: struct { src: usize, result: ?*zir.Inst } = if (else_node != 0) blk: {
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
1870 block_scope.break_count += 1;2140 block_scope.break_count += 1;
1871 break :blk try expr(mod, else_sub_scope, block_scope.break_result_loc, else_node.body);2141 const sub_scope = &else_scope.base;
1872 } else blk: {2142 break :blk .{
1873 else_src = tree.token_locs[if_node.lastToken()].start;2143 .src = token_starts[tree.lastToken(else_node)],
1874 else_sub_scope = &else_scope.base;2144 .result = try expr(mod, sub_scope, block_scope.break_result_loc, else_node),
1875 break :blk null;2145 };
1876 };2146 } else
2147 .{
2148 .src = token_starts[tree.lastToken(if_full.ast.then_expr)],
2149 .result = null,
2150 };
18772151
1878 return finishThenElseBlock(2152 return finishThenElseBlock(
1879 mod,2153 mod,
...@@ -1885,9 +2159,9 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn...@@ -1885,9 +2159,9 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
1885 &condbr.positionals.then_body,2159 &condbr.positionals.then_body,
1886 &condbr.positionals.else_body,2160 &condbr.positionals.else_body,
1887 then_src,2161 then_src,
1888 else_src,2162 else_info.src,
1889 then_result,2163 then_result,
1890 else_result,2164 else_info.result,
1891 block,2165 block,
1892 block,2166 block,
1893 );2167 );
...@@ -1918,23 +2192,15 @@ fn whileExpr(...@@ -1918,23 +2192,15 @@ fn whileExpr(
1918 mod: *Module,2192 mod: *Module,
1919 scope: *Scope,2193 scope: *Scope,
1920 rl: ResultLoc,2194 rl: ResultLoc,
1921 while_node: *ast.Node.While,2195 while_full: ast.full.While,
1922) InnerError!*zir.Inst {2196) InnerError!*zir.Inst {
1923 var cond_kind: CondKind = .bool;2197 if (while_full.label_token) |label_token| {
1924 if (while_node.payload) |_| cond_kind = .{ .optional = null };2198 try checkLabelRedefinition(mod, scope, label_token);
1925 if (while_node.@"else") |else_node| {
1926 if (else_node.payload) |payload| {
1927 cond_kind = .{ .err_union = null };
1928 }
1929 }2199 }
19302200 if (while_full.inline_token) |inline_token| {
1931 if (while_node.label) |label| {2201 return mod.failTok(scope, inline_token, "TODO inline while", .{});
1932 try checkLabelRedefinition(mod, scope, label);
1933 }2202 }
19342203
1935 if (while_node.inline_token) |tok|
1936 return mod.failTok(scope, tok, "TODO inline while", .{});
1937
1938 var loop_scope: Scope.GenZIR = .{2204 var loop_scope: Scope.GenZIR = .{
1939 .parent = scope,2205 .parent = scope,
1940 .decl = scope.ownerDecl().?,2206 .decl = scope.ownerDecl().?,
...@@ -1955,12 +2221,28 @@ fn whileExpr(...@@ -1955,12 +2221,28 @@ fn whileExpr(
1955 defer continue_scope.instructions.deinit(mod.gpa);2221 defer continue_scope.instructions.deinit(mod.gpa);
19562222
1957 const tree = scope.tree();2223 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];
1959 const void_type = try addZIRInstConst(mod, scope, while_src, .{2228 const void_type = try addZIRInstConst(mod, scope, while_src, .{
1960 .ty = Type.initTag(.type),2229 .ty = Type.initTag(.type),
1961 .val = Value.initTag(.void_type),2230 .val = Value.initTag(.void_type),
1962 });2231 });
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
1965 const condbr = try addZIRInstSpecial(mod, &continue_scope.base, while_src, zir.Inst.CondBr, .{2247 const condbr = try addZIRInstSpecial(mod, &continue_scope.base, while_src, zir.Inst.CondBr, .{
1966 .condition = cond,2248 .condition = cond,
...@@ -1974,8 +2256,8 @@ fn whileExpr(...@@ -1974,8 +2256,8 @@ fn whileExpr(
1974 // are no jumps to it. This happens when the last statement of a while body is noreturn2256 // are no jumps to it. This happens when the last statement of a while body is noreturn
1975 // and there are no `continue` statements.2257 // and there are no `continue` statements.
1976 // The "repeat" at the end of a loop body is implied.2258 // The "repeat" at the end of a loop body is implied.
1977 if (while_node.continue_expr) |cont_expr| {2259 if (while_full.ast.cont_expr != 0) {
1978 _ = try expr(mod, &loop_scope.base, .{ .ty = void_type }, cont_expr);2260 _ = try expr(mod, &loop_scope.base, .{ .ty = void_type }, while_full.ast.cont_expr);
1979 }2261 }
1980 const loop = try scope.arena().create(zir.Inst.Loop);2262 const loop = try scope.arena().create(zir.Inst.Loop);
1981 loop.* = .{2263 loop.* = .{
...@@ -1995,14 +2277,14 @@ fn whileExpr(...@@ -1995,14 +2277,14 @@ fn whileExpr(
1995 });2277 });
1996 loop_scope.break_block = while_block;2278 loop_scope.break_block = while_block;
1997 loop_scope.continue_block = cond_block;2279 loop_scope.continue_block = cond_block;
1998 if (while_node.label) |some| {2280 if (while_full.label_token) |label_token| {
1999 loop_scope.label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{2281 loop_scope.label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{
2000 .token = some,2282 .token = label_token,
2001 .block_inst = while_block,2283 .block_inst = while_block,
2002 });2284 });
2003 }2285 }
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)];
2006 var then_scope: Scope.GenZIR = .{2288 var then_scope: Scope.GenZIR = .{
2007 .parent = &continue_scope.base,2289 .parent = &continue_scope.base,
2008 .decl = continue_scope.decl,2290 .decl = continue_scope.decl,
...@@ -2012,11 +2294,10 @@ fn whileExpr(...@@ -2012,11 +2294,10 @@ fn whileExpr(
2012 };2294 };
2013 defer then_scope.instructions.deinit(mod.gpa);2295 defer then_scope.instructions.deinit(mod.gpa);
20142296
2015 // declare payload to the then_scope2297 const then_sub_scope = &then_scope.base;
2016 const then_sub_scope = try cond_kind.thenSubScope(mod, &then_scope, then_src, while_node.payload);
20172298
2018 loop_scope.break_count += 1;2299 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
2021 var else_scope: Scope.GenZIR = .{2302 var else_scope: Scope.GenZIR = .{
2022 .parent = &continue_scope.base,2303 .parent = &continue_scope.base,
...@@ -2027,21 +2308,23 @@ fn whileExpr(...@@ -2027,21 +2308,23 @@ fn whileExpr(
2027 };2308 };
2028 defer else_scope.instructions.deinit(mod.gpa);2309 defer else_scope.instructions.deinit(mod.gpa);
20292310
2030 var else_src: usize = undefined;2311 const else_node = while_full.ast.else_expr;
2031 const else_result: ?*zir.Inst = if (while_node.@"else") |else_node| blk: {2312 const else_info: struct { src: usize, result: ?*zir.Inst } = if (else_node != 0) 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
2036 loop_scope.break_count += 1;2313 loop_scope.break_count += 1;
2037 break :blk try expr(mod, else_sub_scope, loop_scope.break_result_loc, else_node.body);2314 const sub_scope = &else_scope.base;
2038 } else blk: {2315 break :blk .{
2039 else_src = tree.token_locs[while_node.lastToken()].start;2316 .src = token_starts[tree.lastToken(else_node)],
2040 break :blk null;2317 .result = try expr(mod, sub_scope, loop_scope.break_result_loc, else_node),
2041 };2318 };
2319 } else
2320 .{
2321 .src = token_starts[tree.lastToken(while_full.ast.then_expr)],
2322 .result = null,
2323 };
2324
2042 if (loop_scope.label) |some| {2325 if (loop_scope.label) |some| {
2043 if (!some.used) {2326 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", .{});
2045 }2328 }
2046 }2329 }
2047 return finishThenElseBlock(2330 return finishThenElseBlock(
...@@ -2054,9 +2337,9 @@ fn whileExpr(...@@ -2054,9 +2337,9 @@ fn whileExpr(
2054 &condbr.positionals.then_body,2337 &condbr.positionals.then_body,
2055 &condbr.positionals.else_body,2338 &condbr.positionals.else_body,
2056 then_src,2339 then_src,
2057 else_src,2340 else_info.src,
2058 then_result,2341 then_result,
2059 else_result,2342 else_info.result,
2060 while_block,2343 while_block,
2061 cond_block,2344 cond_block,
2062 );2345 );
...@@ -2066,18 +2349,23 @@ fn forExpr(...@@ -2066,18 +2349,23 @@ fn forExpr(
2066 mod: *Module,2349 mod: *Module,
2067 scope: *Scope,2350 scope: *Scope,
2068 rl: ResultLoc,2351 rl: ResultLoc,
2069 for_node: *ast.Node.For,2352 for_full: ast.full.While,
2070) InnerError!*zir.Inst {2353) InnerError!*zir.Inst {
2071 if (for_node.label) |label| {2354 if (for_full.label_token) |label_token| {
2072 try checkLabelRedefinition(mod, scope, label);2355 try checkLabelRedefinition(mod, scope, label_token);
2073 }2356 }
20742357
2075 if (for_node.inline_token) |tok|2358 if (for_full.inline_token) |inline_token| {
2076 return mod.failTok(scope, tok, "TODO inline for", .{});2359 return mod.failTok(scope, inline_token, "TODO inline for", .{});
2360 }
20772361
2078 // setup variables and constants2362 // Set up variables and constants.
2079 const tree = scope.tree();2363 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];
2081 const index_ptr = blk: {2369 const index_ptr = blk: {
2082 const usize_type = try addZIRInstConst(mod, scope, for_src, .{2370 const usize_type = try addZIRInstConst(mod, scope, for_src, .{
2083 .ty = Type.initTag(.type),2371 .ty = Type.initTag(.type),
...@@ -2092,8 +2380,8 @@ fn forExpr(...@@ -2092,8 +2380,8 @@ fn forExpr(
2092 _ = try addZIRBinOp(mod, scope, for_src, .store, index_ptr, zero);2380 _ = try addZIRBinOp(mod, scope, for_src, .store, index_ptr, zero);
2093 break :blk index_ptr;2381 break :blk index_ptr;
2094 };2382 };
2095 const array_ptr = try expr(mod, scope, .ref, for_node.array_expr);2383 const array_ptr = try expr(mod, scope, .ref, for_full.ast.cond_expr);
2096 const cond_src = tree.token_locs[for_node.array_expr.firstToken()].start;2384 const cond_src = token_starts[tree.firstToken(for_full.ast.cond_expr)];
2097 const len = try addZIRUnOp(mod, scope, cond_src, .indexable_ptr_len, array_ptr);2385 const len = try addZIRUnOp(mod, scope, cond_src, .indexable_ptr_len, array_ptr);
20982386
2099 var loop_scope: Scope.GenZIR = .{2387 var loop_scope: Scope.GenZIR = .{
...@@ -2155,15 +2443,15 @@ fn forExpr(...@@ -2155,15 +2443,15 @@ fn forExpr(
2155 });2443 });
2156 loop_scope.break_block = for_block;2444 loop_scope.break_block = for_block;
2157 loop_scope.continue_block = cond_block;2445 loop_scope.continue_block = cond_block;
2158 if (for_node.label) |some| {2446 if (for_full.label_token) |label_token| {
2159 loop_scope.label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{2447 loop_scope.label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{
2160 .token = some,2448 .token = label_token,
2161 .block_inst = for_block,2449 .block_inst = for_block,
2162 });2450 });
2163 }2451 }
21642452
2165 // while body2453 // 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)];
2167 var then_scope: Scope.GenZIR = .{2455 var then_scope: Scope.GenZIR = .{
2168 .parent = &cond_scope.base,2456 .parent = &cond_scope.base,
2169 .decl = cond_scope.decl,2457 .decl = cond_scope.decl,
...@@ -2175,23 +2463,27 @@ fn forExpr(...@@ -2175,23 +2463,27 @@ fn forExpr(
21752463
2176 var index_scope: Scope.LocalPtr = undefined;2464 var index_scope: Scope.LocalPtr = undefined;
2177 const then_sub_scope = blk: {2465 const then_sub_scope = blk: {
2178 const payload = for_node.payload.castTag(.PointerIndexPayload).?;2466 const payload_token = for_full.payload_token.?;
2179 const is_ptr = payload.ptr_token != null;2467 const ident = if (token_tags[payload_token] == .asterisk)
2180 const value_name = tree.tokenSlice(payload.value_symbol.firstToken());2468 payload_token + 1
2469 else
2470 payload_token;
2471 const is_ptr = ident != payload_token;
2472 const value_name = tree.tokenSlice(ident);
2181 if (!mem.eql(u8, value_name, "_")) {2473 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", .{});
2183 } else if (is_ptr) {2475 } 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", .{});
2185 }2477 }
21862478
2187 const index_symbol_node = payload.index_symbol orelse2479 const index_token = if (token_tags[ident + 1] == .comma)
2188 break :blk &then_scope.base;2480 ident + 2
21892481 else
2190 const index_name = tree.tokenSlice(index_symbol_node.firstToken());
2191 if (mem.eql(u8, index_name, "_")) {
2192 break :blk &then_scope.base;2482 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", .{});
2193 }2485 }
2194 // TODO make this const without an extra copy?2486 const index_name = try mod.identifierTokenString(&then_scope.base, index_token);
2195 index_scope = .{2487 index_scope = .{
2196 .parent = &then_scope.base,2488 .parent = &then_scope.base,
2197 .gen_zir = &then_scope,2489 .gen_zir = &then_scope,
...@@ -2202,7 +2494,7 @@ fn forExpr(...@@ -2202,7 +2494,7 @@ fn forExpr(
2202 };2494 };
22032495
2204 loop_scope.break_count += 1;2496 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
2207 // else branch2499 // else branch
2208 var else_scope: Scope.GenZIR = .{2500 var else_scope: Scope.GenZIR = .{
...@@ -2214,18 +2506,23 @@ fn forExpr(...@@ -2214,18 +2506,23 @@ fn forExpr(
2214 };2506 };
2215 defer else_scope.instructions.deinit(mod.gpa);2507 defer else_scope.instructions.deinit(mod.gpa);
22162508
2217 var else_src: usize = undefined;2509 const else_node = for_full.ast.else_expr;
2218 const else_result: ?*zir.Inst = if (for_node.@"else") |else_node| blk: {2510 const else_info: struct { src: usize, result: ?*zir.Inst } = if (else_node != 0) blk: {
2219 else_src = tree.token_locs[else_node.body.lastToken()].start;
2220 loop_scope.break_count += 1;2511 loop_scope.break_count += 1;
2221 break :blk try expr(mod, &else_scope.base, loop_scope.break_result_loc, else_node.body);2512 const sub_scope = &else_scope.base;
2222 } else blk: {2513 break :blk .{
2223 else_src = tree.token_locs[for_node.lastToken()].start;2514 .src = token_starts[tree.lastToken(else_node)],
2224 break :blk null;2515 .result = try expr(mod, sub_scope, loop_scope.break_result_loc, else_node),
2225 };2516 };
2517 } else
2518 .{
2519 .src = token_starts[tree.lastToken(for_full.ast.then_expr)],
2520 .result = null,
2521 };
2522
2226 if (loop_scope.label) |some| {2523 if (loop_scope.label) |some| {
2227 if (!some.used) {2524 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", .{});
2229 }2526 }
2230 }2527 }
2231 return finishThenElseBlock(2528 return finishThenElseBlock(
...@@ -2238,39 +2535,48 @@ fn forExpr(...@@ -2238,39 +2535,48 @@ fn forExpr(
2238 &condbr.positionals.then_body,2535 &condbr.positionals.then_body,
2239 &condbr.positionals.else_body,2536 &condbr.positionals.else_body,
2240 then_src,2537 then_src,
2241 else_src,2538 else_info.src,
2242 then_result,2539 then_result,
2243 else_result,2540 else_info.result,
2244 for_block,2541 for_block,
2245 cond_block,2542 cond_block,
2246 );2543 );
2247}2544}
22482545
2249fn switchCaseUsesRef(node: *ast.Node.Switch) bool {2546fn getRangeNode(
2250 for (node.cases()) |uncasted_case| {2547 node_tags: []const ast.Node.Tag,
2251 const case = uncasted_case.castTag(.SwitchCase).?;2548 node_datas: []const ast.Node.Data,
2252 const uncasted_payload = case.payload orelse continue;2549 start_node: ast.Node.Index,
2253 const payload = uncasted_payload.castTag(.PointerPayload).?;2550) ?ast.Node.Index {
2254 if (payload.ptr_token) |_| return true;2551 var node = start_node;
2255 }
2256 return false;
2257}
2258
2259fn getRangeNode(node: *ast.Node) ?*ast.Node.SimpleInfixOp {
2260 var cur = node;
2261 while (true) {2552 while (true) {
2262 switch (cur.tag) {2553 switch (node_tags[node]) {
2263 .Range => return @fieldParentPtr(ast.Node.SimpleInfixOp, "base", cur),2554 .switch_range => return node,
2264 .GroupedExpression => cur = @fieldParentPtr(ast.Node.GroupedExpression, "base", cur).expr,2555 .grouped_expression => node = node_datas[node].lhs,
2265 else => return null,2556 else => return null,
2266 }2557 }
2267 }2558 }
2268}2559}
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 {
2271 const tree = scope.tree();2567 const tree = scope.tree();
2272 const switch_src = tree.token_locs[switch_node.switch_token].start;2568 const node_datas = tree.nodes.items(.data);
2273 const use_ref = switchCaseUsesRef(switch_node);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
2275 var block_scope: Scope.GenZIR = .{2581 var block_scope: Scope.GenZIR = .{
2276 .parent = scope,2582 .parent = scope,
...@@ -2285,18 +2591,26 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node...@@ -2285,18 +2591,26 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
2285 var items = std.ArrayList(*zir.Inst).init(mod.gpa);2591 var items = std.ArrayList(*zir.Inst).init(mod.gpa);
2286 defer items.deinit();2592 defer items.deinit();
22872593
2288 // first we gather all the switch items and check else/'_' prongs2594 // First we gather all the switch items and check else/'_' prongs.
2289 var else_src: ?usize = null;2595 var else_src: ?usize = null;
2290 var underscore_src: ?usize = null;2596 var underscore_src: ?usize = null;
2291 var first_range: ?*zir.Inst = null;2597 var first_range: ?*zir.Inst = null;
2292 var simple_case_count: usize = 0;2598 var simple_case_count: usize = 0;
2293 for (switch_node.cases()) |uncasted_case| {2599 var any_payload_is_ref = false;
2294 const case = uncasted_case.castTag(.SwitchCase).?;2600 for (case_nodes) |case_node| {
2295 const case_src = tree.token_locs[case.firstToken()].start;2601 const case = switch (node_tags[case_node]) {
2296 assert(case.items_len != 0);2602 .switch_case_one => tree.switchCaseOne(case_node),
22972603 .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 }
2298 // Check for else/_ prong, those are handled last.2611 // 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];
2300 if (else_src) |src| {2614 if (else_src) |src| {
2301 const msg = msg: {2615 const msg = msg: {
2302 const msg = try mod.errMsg(2616 const msg = try mod.errMsg(
...@@ -2313,9 +2627,11 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node...@@ -2313,9 +2627,11 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
2313 }2627 }
2314 else_src = case_src;2628 else_src = case_src;
2315 continue;2629 continue;
2316 } else if (case.items_len == 1 and case.items()[0].tag == .Identifier and2630 } else if (case.ast.values.len == 1 and
2317 mem.eql(u8, tree.tokenSlice(case.items()[0].firstToken()), "_"))2631 node_tags[case.ast.values[0]] == .identifier and
2632 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"))
2318 {2633 {
2634 const case_src = token_starts[case.ast.arrow_token - 1];
2319 if (underscore_src) |src| {2635 if (underscore_src) |src| {
2320 const msg = msg: {2636 const msg = msg: {
2321 const msg = try mod.errMsg(2637 const msg = try mod.errMsg(
...@@ -2352,14 +2668,18 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node...@@ -2352,14 +2668,18 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
2352 }2668 }
2353 }2669 }
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 expressions2677 // Generate all the switch items as comptime expressions.
2358 for (case.items()) |item| {2678 for (case.ast.values) |item| {
2359 if (getRangeNode(item)) |range| {2679 if (getRangeNode(node_tags, node_datas, item)) |range| {
2360 const start = try comptimeExpr(mod, &block_scope.base, .none, range.lhs);2680 const start = try comptimeExpr(mod, &block_scope.base, .none, node_datas[range].lhs);
2361 const end = try comptimeExpr(mod, &block_scope.base, .none, range.rhs);2681 const end = try comptimeExpr(mod, &block_scope.base, .none, node_datas[range].rhs);
2362 const range_src = tree.token_locs[range.op_token].start;2682 const range_src = token_starts[main_tokens[range]];
2363 const range_inst = try addZIRBinOp(mod, &block_scope.base, range_src, .switch_range, start, end);2683 const range_inst = try addZIRBinOp(mod, &block_scope.base, range_src, .switch_range, start, end);
2364 try items.append(range_inst);2684 try items.append(range_inst);
2365 } else {2685 } else {
...@@ -2374,21 +2694,25 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node...@@ -2374,21 +2694,25 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
2374 if (underscore_src != null) special_prong = .underscore;2694 if (underscore_src != null) special_prong = .underscore;
2375 var cases = try block_scope.arena.alloc(zir.Inst.SwitchBr.Case, simple_case_count);2695 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;2697 const rl_and_tag: struct { rl: ResultLoc, tag: zir.Inst.Tag } = if (any_payload_is_ref)
2378 const target = if (target_ptr) |some|2698 .{
2379 try addZIRUnOp(mod, &block_scope.base, some.src, .deref, some)2699 .rl = .ref,
2700 .tag = .switchbr_ref,
2701 }
2380 else2702 else
2381 try expr(mod, &block_scope.base, .none, switch_node.expr);2703 .{
2382 const switch_inst = try addZIRInst(mod, &block_scope.base, switch_src, zir.Inst.SwitchBr, .{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, .{
2383 .target = target,2709 .target = target,
2384 .cases = cases,2710 .cases = cases,
2385 .items = try block_scope.arena.dupe(*zir.Inst, items.items),2711 .items = try block_scope.arena.dupe(*zir.Inst, items.items),
2386 .else_body = undefined, // populated below2712 .else_body = undefined, // populated below
2387 }, .{
2388 .range = first_range,2713 .range = first_range,
2389 .special_prong = special_prong,2714 .special_prong = special_prong,
2390 });2715 });
2391
2392 const block = try addZIRInstBlock(mod, scope, switch_src, .block, .{2716 const block = try addZIRInstBlock(mod, scope, switch_src, .block, .{
2393 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),2717 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
2394 });2718 });
...@@ -2411,32 +2735,38 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node...@@ -2411,32 +2735,38 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
2411 };2735 };
2412 defer else_scope.instructions.deinit(mod.gpa);2736 defer else_scope.instructions.deinit(mod.gpa);
24132737
2414 // Now generate all but the special cases2738 // Now generate all but the special cases.
2415 var special_case: ?*ast.Node.SwitchCase = null;2739 var special_case: ?ast.full.SwitchCase = null;
2416 var items_index: usize = 0;2740 var items_index: usize = 0;
2417 var case_index: usize = 0;2741 var case_index: usize = 0;
2418 for (switch_node.cases()) |uncasted_case| {2742 for (case_nodes) |case_node| {
2419 const case = uncasted_case.castTag(.SwitchCase).?;2743 const case = switch (node_tags[case_node]) {
2420 const case_src = tree.token_locs[case.firstToken()].start;2744 .switch_case_one => tree.switchCaseOne(case_node),
2421 // reset without freeing to reduce allocations.2745 .switch_case => tree.switchCase(case_node),
2422 case_scope.instructions.items.len = 0;2746 else => unreachable,
2747 };
2748 const case_src = token_starts[main_tokens[case_node]];
2749 case_scope.instructions.shrinkRetainingCapacity(0);
24232750
2424 // Check for else/_ prong, those are handled last.2751 // 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) {
2426 special_case = case;2753 special_case = case;
2427 continue;2754 continue;
2428 } else if (case.items_len == 1 and case.items()[0].tag == .Identifier and2755 } else if (case.ast.values.len == 1 and
2429 mem.eql(u8, tree.tokenSlice(case.items()[0].firstToken()), "_"))2756 node_tags[case.ast.values[0]] == .identifier and
2757 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"))
2430 {2758 {
2431 special_case = case;2759 special_case = case;
2432 continue;2760 continue;
2433 }2761 }
24342762
2435 // If this is a simple one item prong then it is handled by the switchbr.2763 // 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 {
2437 const item = items.items[items_index];2767 const item = items.items[items_index];
2438 items_index += 1;2768 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
2441 cases[case_index] = .{2771 cases[case_index] = .{
2442 .item = item,2772 .item = item,
...@@ -2446,16 +2776,14 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node...@@ -2446,16 +2776,14 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
2446 continue;2776 continue;
2447 }2777 }
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
2452 // Check if the target matches any of the items.2779 // Check if the target matches any of the items.
2453 // 1, 2, 3..6 will result in2780 // 1, 2, 3..6 will result in
2454 // target == 1 or target == 2 or (target >= 3 and target <= 6)2781 // target == 1 or target == 2 or (target >= 3 and target <= 6)
2782 // TODO handle multiple items as switch prongs rather than along with ranges.
2455 var any_ok: ?*zir.Inst = null;2783 var any_ok: ?*zir.Inst = null;
2456 for (case.items()) |item| {2784 for (case.ast.values) |item| {
2457 if (getRangeNode(item)) |range| {2785 if (getRangeNode(node_tags, node_datas, item)) |range| {
2458 const range_src = tree.token_locs[range.op_token].start;2786 const range_src = token_starts[main_tokens[range]];
2459 const range_inst = items.items[items_index].castTag(.switch_range).?;2787 const range_inst = items.items[items_index].castTag(.switch_range).?;
2460 items_index += 1;2788 items_index += 1;
24612789
...@@ -2494,7 +2822,7 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node...@@ -2494,7 +2822,7 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
24942822
2495 // reset cond_scope for then_body2823 // reset cond_scope for then_body
2496 case_scope.instructions.items.len = 0;2824 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);
2498 condbr.positionals.then_body = .{2826 condbr.positionals.then_body = .{
2499 .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items),2827 .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items),
2500 };2828 };
...@@ -2511,12 +2839,12 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node...@@ -2511,12 +2839,12 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
25112839
2512 // Finally generate else block or a break.2840 // Finally generate else block or a break.
2513 if (special_case) |case| {2841 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);
2515 } else {2843 } else {
2516 // Not handling all possible cases is a compile error.2844 // Not handling all possible cases is a compile error.
2517 _ = try addZIRNoOp(mod, &else_scope.base, switch_src, .unreachable_unsafe);2845 _ = try addZIRNoOp(mod, &else_scope.base, switch_src, .unreachable_unsafe);
2518 }2846 }
2519 switch_inst.castTag(.switchbr).?.positionals.else_body = .{2847 switch_inst.positionals.else_body = .{
2520 .instructions = try block_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),2848 .instructions = try block_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),
2521 };2849 };
25222850
...@@ -2528,27 +2856,34 @@ fn switchCaseExpr(...@@ -2528,27 +2856,34 @@ fn switchCaseExpr(
2528 scope: *Scope,2856 scope: *Scope,
2529 rl: ResultLoc,2857 rl: ResultLoc,
2530 block: *zir.Inst.Block,2858 block: *zir.Inst.Block,
2531 case: *ast.Node.SwitchCase,2859 case: ast.full.SwitchCase,
2532 target: *zir.Inst,2860 target: *zir.Inst,
2533 target_ptr: ?*zir.Inst,
2534) !void {2861) !void {
2535 const tree = scope.tree();2862 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];
2537 const sub_scope = blk: {2869 const sub_scope = blk: {
2538 const uncasted_payload = case.payload orelse break :blk scope;2870 const payload_token = case.payload_token orelse break :blk scope;
2539 const payload = uncasted_payload.castTag(.PointerPayload).?;2871 const ident = if (token_tags[payload_token] == .asterisk)
2540 const is_ptr = payload.ptr_token != null;2872 payload_token + 1
2541 const value_name = tree.tokenSlice(payload.value_symbol.firstToken());2873 else
2874 payload_token;
2875 const is_ptr = ident != payload_token;
2876 const value_name = tree.tokenSlice(ident);
2542 if (mem.eql(u8, value_name, "_")) {2877 if (mem.eql(u8, value_name, "_")) {
2543 if (is_ptr) {2878 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", .{});
2545 }2880 }
2546 break :blk scope;2881 break :blk scope;
2547 }2882 }
2548 return mod.failNode(scope, payload.value_symbol, "TODO implement switch value payload", .{});2883 return mod.failTok(scope, ident, "TODO implement switch value payload", .{});
2549 };2884 };
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);
2552 if (!case_body.tag.isNoReturn()) {2887 if (!case_body.tag.isNoReturn()) {
2553 _ = try addZIRInst(mod, sub_scope, case_src, zir.Inst.Break, .{2888 _ = try addZIRInst(mod, sub_scope, case_src, zir.Inst.Break, .{
2554 .block = block,2889 .block = block,
...@@ -2557,11 +2892,16 @@ fn switchCaseExpr(...@@ -2557,11 +2892,16 @@ fn switchCaseExpr(
2557 }2892 }
2558}2893}
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 {
2561 const tree = scope.tree();2896 const tree = scope.tree();
2562 const src = tree.token_locs[cfe.ltoken].start;2897 const node_datas = tree.nodes.items(.data);
2563 if (cfe.getRHS()) |rhs_node| {2898 const main_tokens = tree.nodes.items(.main_token);
2564 if (nodeMayNeedMemoryLocation(rhs_node, scope)) {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)) {
2565 const ret_ptr = try addZIRNoOp(mod, scope, src, .ret_ptr);2905 const ret_ptr = try addZIRNoOp(mod, scope, src, .ret_ptr);
2566 const operand = try expr(mod, scope, .{ .ptr = ret_ptr }, rhs_node);2906 const operand = try expr(mod, scope, .{ .ptr = ret_ptr }, rhs_node);
2567 return addZIRUnOp(mod, scope, src, .@"return", operand);2907 return addZIRUnOp(mod, scope, src, .@"return", operand);
...@@ -2575,19 +2915,31 @@ fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerE...@@ -2575,19 +2915,31 @@ fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerE
2575 }2915 }
2576}2916}
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 {
2579 const tracy = trace(@src());2924 const tracy = trace(@src());
2580 defer tracy.end();2925 defer tracy.end();
25812926
2582 const tree = scope.tree();2927 const tree = scope.tree();
2583 const ident_name = try mod.identifierTokenString(scope, ident.token);2928 const main_tokens = tree.nodes.items(.main_token);
2584 const src = tree.token_locs[ident.token].start;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];
2585 if (mem.eql(u8, ident_name, "_")) {2934 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", .{});
2587 }2936 }
25882937
2589 if (getSimplePrimitiveValue(ident_name)) |typed_value| {2938 if (simple_types.get(ident_name)) |val_tag| {
2590 const result = try addZIRInstConst(mod, scope, src, typed_value);2939 const result = try addZIRInstConst(mod, scope, src, TypedValue{
2940 .ty = Type.initTag(.type),
2941 .val = Value.initTag(val_tag),
2942 });
2591 return rvalue(mod, scope, rl, result);2943 return rvalue(mod, scope, rl, result);
2592 }2944 }
25932945
...@@ -2598,7 +2950,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo...@@ -2598,7 +2950,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo
2598 const bit_count = std.fmt.parseInt(u16, ident_name[1..], 10) catch |err| switch (err) {2950 const bit_count = std.fmt.parseInt(u16, ident_name[1..], 10) catch |err| switch (err) {
2599 error.Overflow => return mod.failNode(2951 error.Overflow => return mod.failNode(
2600 scope,2952 scope,
2601 &ident.base,2953 ident,
2602 "primitive integer type '{s}' exceeds maximum bit width of 65535",2954 "primitive integer type '{s}' exceeds maximum bit width of 65535",
2603 .{ident_name},2955 .{ident_name},
2604 ),2956 ),
...@@ -2662,59 +3014,104 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo...@@ -2662,59 +3014,104 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo
2662 }3014 }
2663 }3015 }
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});
2666}3018}
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 {
2669 const tree = scope.tree();3021 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);
2671 const arena = scope.arena();3026 const arena = scope.arena();
2672
2673 var bad_index: usize = undefined;3027 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) {
2675 error.InvalidCharacter => {3029 error.InvalidCharacter => {
2676 const bad_byte = unparsed_bytes[bad_index];3030 const bad_byte = unparsed[bad_index];
2677 const src = tree.token_locs[str_lit.token].start;3031 const src = token_starts[token];
2678 return mod.fail(scope, src + bad_index, "invalid string literal character: '{c}'\n", .{bad_byte});3032 return mod.fail(scope, src + bad_index, "invalid string literal character: '{c}'", .{
3033 bad_byte,
3034 });
2679 },3035 },
2680 else => |e| return e,3036 else => |e| return e,
2681 };3037 };
26823038 return bytes;
2683 const src = tree.token_locs[str_lit.token].start;
2684 return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{});
2685}3039}
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 {
2688 const tree = scope.tree();3047 const tree = scope.tree();
2689 const lines = node.linesConst();3048 const main_tokens = tree.nodes.items(.main_token);
2690 const src = tree.token_locs[lines[0]].start;3049 const token_starts = tree.tokens.items(.start);
26913050
2692 // line lengths and new lines3051 const str_lit_token = main_tokens[str_lit];
2693 var len = lines.len - 1;3052 const bytes = try parseStringLiteral(mod, scope, str_lit_token);
2694 for (lines) |line| {3053 const src = token_starts[str_lit_token];
2695 // 2 for the '//' + 1 for '\n'3054 const str_inst = try addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{});
2696 len += tree.tokenSlice(line).len - 3;3055 return rvalue(mod, scope, rl, str_inst);
2697 }3056}
26983057
2699 const bytes = try scope.arena().alloc(u8, len);3058fn multilineStringLiteral(
2700 var i: usize = 0;3059 mod: *Module,
2701 for (lines) |line, line_i| {3060 scope: *Scope,
2702 if (line_i != 0) {3061 rl: ResultLoc,
2703 bytes[i] = '\n';3062 str_lit: ast.Node.Index,
2704 i += 1;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;
2705 }3079 }
2706 const slice = tree.tokenSlice(line);3080 break :len len;
2707 mem.copy(u8, bytes[i..], slice[2 .. slice.len - 1]);3081 };
2708 i += slice.len - 3;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;
2709 }3092 }
27103093 // Following lines: each line prepends a newline.
2711 return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{});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);
2712}3105}
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 {
2715 const tree = scope.tree();3108 const tree = scope.tree();
2716 const src = tree.token_locs[node.token].start;3109 const main_tokens = tree.nodes.items(.main_token);
2717 const slice = tree.tokenSlice(node.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
2719 var bad_index: usize = undefined;3116 var bad_index: usize = undefined;
2720 const value = std.zig.parseCharLiteral(slice, &bad_index) catch |err| switch (err) {3117 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...@@ -2723,18 +3120,27 @@ fn charLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) !*zir.Inst
2723 return mod.fail(scope, src + bad_index, "invalid character: '{c}'\n", .{bad_byte});3120 return mod.fail(scope, src + bad_index, "invalid character: '{c}'\n", .{bad_byte});
2724 },3121 },
2725 };3122 };
27263123 const result = try addZIRInstConst(mod, scope, src, .{
2727 return addZIRInstConst(mod, scope, src, .{
2728 .ty = Type.initTag(.comptime_int),3124 .ty = Type.initTag(.comptime_int),
2729 .val = try Value.Tag.int_u64.create(scope.arena(), value),3125 .val = try Value.Tag.int_u64.create(scope.arena(), value),
2730 });3126 });
3127 return rvalue(mod, scope, rl, result);
2731}3128}
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 {
2734 const arena = scope.arena();3136 const arena = scope.arena();
2735 const tree = scope.tree();3137 const tree = scope.tree();
2736 const prefixed_bytes = tree.tokenSlice(int_lit.token);3138 const main_tokens = tree.nodes.items(.main_token);
2737 const base = if (mem.startsWith(u8, prefixed_bytes, "0x"))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"))
2738 163144 16
2739 else if (mem.startsWith(u8, prefixed_bytes, "0o"))3145 else if (mem.startsWith(u8, prefixed_bytes, "0o"))
2740 83146 8
...@@ -2749,90 +3155,70 @@ fn integerLiteral(mod: *Module, scope: *Scope, int_lit: *ast.Node.OneToken) Inne...@@ -2749,90 +3155,70 @@ fn integerLiteral(mod: *Module, scope: *Scope, int_lit: *ast.Node.OneToken) Inne
2749 prefixed_bytes[2..];3155 prefixed_bytes[2..];
27503156
2751 if (std.fmt.parseInt(u64, bytes, base)) |small_int| {3157 if (std.fmt.parseInt(u64, bytes, base)) |small_int| {
2752 const src = tree.token_locs[int_lit.token].start;3158 const src = token_starts[int_token];
2753 return addZIRInstConst(mod, scope, src, .{3159 const result = try addZIRInstConst(mod, scope, src, .{
2754 .ty = Type.initTag(.comptime_int),3160 .ty = Type.initTag(.comptime_int),
2755 .val = try Value.Tag.int_u64.create(arena, small_int),3161 .val = try Value.Tag.int_u64.create(arena, small_int),
2756 });3162 });
3163 return rvalue(mod, scope, rl, result);
2757 } else |err| {3164 } 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", .{});
2759 }3166 }
2760}3167}
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 {
2763 const arena = scope.arena();3175 const arena = scope.arena();
2764 const tree = scope.tree();3176 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);
2766 if (bytes.len > 2 and bytes[1] == 'x') {3182 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", .{});
2768 }3184 }
2769
2770 const float_number = std.fmt.parseFloat(f128, bytes) catch |e| switch (e) {3185 const float_number = std.fmt.parseFloat(f128, bytes) catch |e| switch (e) {
2771 error.InvalidCharacter => unreachable, // validated by tokenizer3186 error.InvalidCharacter => unreachable, // validated by tokenizer
2772 };3187 };
2773 const src = tree.token_locs[float_lit.token].start;3188 const src = token_starts[main_token];
2774 return addZIRInstConst(mod, scope, src, .{3189 const result = try addZIRInstConst(mod, scope, src, .{
2775 .ty = Type.initTag(.comptime_float),3190 .ty = Type.initTag(.comptime_float),
2776 .val = try Value.Tag.float_128.create(arena, float_number),3191 .val = try Value.Tag.float_128.create(arena, float_number),
2777 });3192 });
3193 return rvalue(mod, scope, rl, result);
2778}3194}
27793195
2780fn undefLiteral(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 {
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 {
2805 const arena = scope.arena();3197 const arena = scope.arena();
2806 const tree = scope.tree();3198 const tree = scope.tree();
2807 const src = tree.token_locs[node.token].start;3199 const main_tokens = tree.nodes.items(.main_token);
2808 return addZIRInstConst(mod, scope, src, .{3200 const token_starts = tree.tokens.items(.start);
2809 .ty = Type.initTag(.@"null"),3201 const node_datas = tree.nodes.items(.data);
2810 .val = Value.initTag(.null_value),
2811 });
2812}
28133202
2814fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zir.Inst {3203 if (full.outputs.len != 0) {
2815 if (asm_node.outputs.len != 0) {3204 return mod.failTok(scope, full.ast.asm_token, "TODO implement asm with an output", .{});
2816 return mod.failNode(scope, &asm_node.base, "TODO implement asm with an output", .{});
2817 }3205 }
2818 const arena = scope.arena();
2819 const tree = scope.tree();
28203206
2821 const inputs = try arena.alloc(*zir.Inst, asm_node.inputs.len);3207 const inputs = try arena.alloc([]const u8, full.inputs.len);
2822 const args = try arena.alloc(*zir.Inst, asm_node.inputs.len);3208 const args = try arena.alloc(*zir.Inst, full.inputs.len);
2823
2824 const src = tree.token_locs[asm_node.asm_token].start;
28253209
3210 const src = token_starts[full.ast.asm_token];
2826 const str_type = try addZIRInstConst(mod, scope, src, .{3211 const str_type = try addZIRInstConst(mod, scope, src, .{
2827 .ty = Type.initTag(.type),3212 .ty = Type.initTag(.type),
2828 .val = Value.initTag(.const_slice_u8_type),3213 .val = Value.initTag(.const_slice_u8_type),
2829 });3214 });
2830 const str_type_rl: ResultLoc = .{ .ty = str_type };3215 const str_type_rl: ResultLoc = .{ .ty = str_type };
28313216
2832 for (asm_node.inputs) |input, i| {3217 for (full.inputs) |input, i| {
2833 // TODO semantically analyze constraints3218 // TODO semantically analyze constraints
2834 inputs[i] = try expr(mod, scope, str_type_rl, input.constraint);3219 const constraint_token = main_tokens[input] + 2;
2835 args[i] = try expr(mod, scope, .none, input.expr);3220 inputs[i] = try parseStringLiteral(mod, scope, constraint_token);
3221 args[i] = try expr(mod, scope, .none, node_datas[input].lhs);
2836 }3222 }
28373223
2838 const return_type = try addZIRInstConst(mod, scope, src, .{3224 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...@@ -2840,81 +3226,47 @@ fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zi
2840 .val = Value.initTag(.void_type),3226 .val = Value.initTag(.void_type),
2841 });3227 });
2842 const asm_inst = try addZIRInst(mod, scope, src, zir.Inst.Asm, .{3228 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),
2844 .return_type = return_type,3230 .return_type = return_type,
2845 }, .{3231 }, .{
2846 .@"volatile" = asm_node.volatile_token != null,3232 .@"volatile" = full.volatile_token != null,
2847 //.clobbers = TODO handle clobbers3233 //.clobbers = TODO handle clobbers
2848 .inputs = inputs,3234 .inputs = inputs,
2849 .args = args,3235 .args = args,
2850 });3236 });
2851 return asm_inst;3237 return rvalue(mod, scope, rl, 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);
2885}3238}
28863239
2887fn as(3240fn as(
2888 mod: *Module,3241 mod: *Module,
2889 scope: *Scope,3242 scope: *Scope,
2890 rl: ResultLoc,3243 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,
2892) InnerError!*zir.Inst {3248) InnerError!*zir.Inst {
2893 try ensureBuiltinParamCount(mod, scope, call, 2);3249 const dest_type = try typeExpr(mod, scope, lhs);
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]);
2898 switch (rl) {3250 switch (rl) {
2899 .none, .discard, .ref, .ty => {3251 .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);
2901 return rvalue(mod, scope, rl, result);3253 return rvalue(mod, scope, rl, result);
2902 },3254 },
29033255
2904 .ptr => |result_ptr| {3256 .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);
2906 },3258 },
2907 .block_ptr => |block_scope| {3259 .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);
2909 },3261 },
29103262
2911 .bitcasted_ptr => |bitcasted_ptr| {3263 .bitcasted_ptr => |bitcasted_ptr| {
2912 // TODO here we should be able to resolve the inference; we now have a type for the result.3264 // 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", .{});
2914 },3266 },
2915 .inferred_ptr => |result_alloc| {3267 .inferred_ptr => |result_alloc| {
2916 // TODO here we should be able to resolve the inference; we now have a type for the result.3268 // 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", .{});
2918 },3270 },
2919 }3271 }
2920}3272}
...@@ -2925,7 +3277,7 @@ fn asRlPtr(...@@ -2925,7 +3277,7 @@ fn asRlPtr(
2925 rl: ResultLoc,3277 rl: ResultLoc,
2926 src: usize,3278 src: usize,
2927 result_ptr: *zir.Inst,3279 result_ptr: *zir.Inst,
2928 operand_node: *ast.Node,3280 operand_node: ast.Node.Index,
2929 dest_type: *zir.Inst,3281 dest_type: *zir.Inst,
2930) InnerError!*zir.Inst {3282) InnerError!*zir.Inst {
2931 // Detect whether this expr() call goes into rvalue() to store the result into the3283 // Detect whether this expr() call goes into rvalue() to store the result into the
...@@ -2963,155 +3315,295 @@ fn asRlPtr(...@@ -2963,155 +3315,295 @@ fn asRlPtr(
2963 }3315 }
2964}3316}
29653317
2966fn bitCast(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {3318fn bitCast(
2967 try ensureBuiltinParamCount(mod, scope, call, 2);3319 mod: *Module,
2968 const tree = scope.tree();3320 scope: *Scope,
2969 const src = tree.token_locs[call.builtin_token].start;3321 rl: ResultLoc,
2970 const params = call.params();3322 builtin_token: ast.TokenIndex,
2971 const dest_type = try typeExpr(mod, scope, params[0]);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);
2972 switch (rl) {3328 switch (rl) {
2973 .none => {3329 .none => {
2974 const operand = try expr(mod, scope, .none, params[1]);3330 const operand = try expr(mod, scope, .none, rhs);
2975 return addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand);3331 return addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand);
2976 },3332 },
2977 .discard => {3333 .discard => {
2978 const operand = try expr(mod, scope, .none, params[1]);3334 const operand = try expr(mod, scope, .none, rhs);
2979 const result = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand);3335 const result = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand);
2980 _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result);3336 _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result);
2981 return result;3337 return result;
2982 },3338 },
2983 .ref => {3339 .ref => {
2984 const operand = try expr(mod, scope, .ref, params[1]);3340 const operand = try expr(mod, scope, .ref, rhs);
2985 const result = try addZIRBinOp(mod, scope, src, .bitcast_ref, dest_type, operand);3341 const result = try addZIRBinOp(mod, scope, src, .bitcast_ref, dest_type, operand);
2986 return result;3342 return result;
2987 },3343 },
2988 .ty => |result_ty| {3344 .ty => |result_ty| {
2989 const result = try expr(mod, scope, .none, params[1]);3345 const result = try expr(mod, scope, .none, rhs);
2990 const bitcasted = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, result);3346 const bitcasted = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, result);
2991 return addZIRBinOp(mod, scope, src, .as, result_ty, bitcasted);3347 return addZIRBinOp(mod, scope, src, .as, result_ty, bitcasted);
2992 },3348 },
2993 .ptr => |result_ptr| {3349 .ptr => |result_ptr| {
2994 const casted_result_ptr = try addZIRUnOp(mod, scope, src, .bitcast_result_ptr, result_ptr);3350 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);
2996 },3352 },
2997 .bitcasted_ptr => |bitcasted_ptr| {3353 .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", .{});
2999 },3355 },
3000 .block_ptr => |block_ptr| {3356 .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", .{});
3002 },3358 },
3003 .inferred_ptr => |result_alloc| {3359 .inferred_ptr => |result_alloc| {
3004 // TODO here we should be able to resolve the inference; we now have a type for the result.3360 // 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", .{});
3006 },3362 },
3007 }3363 }
3008}3364}
30093365
3010fn import(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {3366fn typeOf(
3011 try ensureBuiltinParamCount(mod, scope, call, 1);3367 mod: *Module,
3012 const tree = scope.tree();3368 scope: *Scope,
3013 const src = tree.token_locs[call.builtin_token].start;3369 rl: ResultLoc,
3014 const params = call.params();3370 builtin_token: ast.TokenIndex,
3015 const target = try expr(mod, scope, .none, params[0]);3371 src: usize,
3016 return addZIRUnOp(mod, scope, src, .import, target);3372 params: []const ast.Node.Index,
3017}3373) InnerError!*zir.Inst {
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();
3046 if (params.len < 1) {3374 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", .{});
3048 }3376 }
3049 if (params.len == 1) {3377 if (params.len == 1) {
3050 return rvalue(mod, scope, rl, try addZIRUnOp(mod, scope, src, .typeof, try expr(mod, scope, .none, params[0])));3378 return rvalue(mod, scope, rl, try addZIRUnOp(mod, scope, src, .typeof, try expr(mod, scope, .none, params[0])));
3051 }3379 }
3380 const arena = scope.arena();
3052 var items = try arena.alloc(*zir.Inst, params.len);3381 var items = try arena.alloc(*zir.Inst, params.len);
3053 for (params) |param, param_i|3382 for (params) |param, param_i|
3054 items[param_i] = try expr(mod, scope, .none, param);3383 items[param_i] = try expr(mod, scope, .none, param);
3055 return rvalue(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.TypeOfPeer, .{ .items = items }, .{}));3384 return rvalue(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.TypeOfPeer, .{ .items = items }, .{}));
3056}3385}
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 {
3069 const tree = scope.tree();3394 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
3072 // We handle the different builtins manually because they have different semantics depending3401 // We handle the different builtins manually because they have different semantics depending
3073 // on the function. For example, `@as` and others participate in result location semantics,3402 // on the function. For example, `@as` and others participate in result location semantics,
3074 // and `@cImport` creates a special scope that collects a .c source code text buffer.3403 // and `@cImport` creates a special scope that collects a .c source code text buffer.
3075 // Also, some builtins have a variable number of parameters.3404 // Also, some builtins have a variable number of parameters.
30763405
3077 if (mem.eql(u8, builtin_name, "@ptrToInt")) {3406 const info = BuiltinFn.list.get(builtin_name) orelse {
3078 return rvalue(mod, scope, rl, try ptrToInt(mod, scope, call));3407 return mod.failTok(scope, builtin_token, "invalid builtin function: '{s}'", .{
3079 } else if (mem.eql(u8, builtin_name, "@as")) {3408 builtin_name,
3080 return as(mod, scope, rl, call);3409 });
3081 } else if (mem.eql(u8, builtin_name, "@floatCast")) {3410 };
3082 return simpleCast(mod, scope, rl, call, .floatcast);3411 if (info.param_count) |expected| {
3083 } else if (mem.eql(u8, builtin_name, "@intCast")) {3412 if (expected != params.len) {
3084 return simpleCast(mod, scope, rl, call, .intcast);3413 const s = if (expected == 1) "" else "s";
3085 } else if (mem.eql(u8, builtin_name, "@bitCast")) {3414 return mod.failTok(scope, builtin_token, "expected {d} parameter{s}, found {d}", .{
3086 return bitCast(mod, scope, rl, call);3415 expected, s, params.len,
3087 } else if (mem.eql(u8, builtin_name, "@TypeOf")) {3416 });
3088 return typeOf(mod, scope, rl, call);3417 }
3089 } else if (mem.eql(u8, builtin_name, "@breakpoint")) {3418 }
3090 const src = tree.token_locs[call.builtin_token].start;3419 const src = token_starts[builtin_token];
3091 return rvalue(mod, scope, rl, try addZIRNoOp(mod, scope, src, .breakpoint));3420
3092 } else if (mem.eql(u8, builtin_name, "@import")) {3421 switch (info.tag) {
3093 return rvalue(mod, scope, rl, try import(mod, scope, call));3422 .ptr_to_int => {
3094 } else if (mem.eql(u8, builtin_name, "@compileError")) {3423 const operand = try expr(mod, scope, .none, params[0]);
3095 return compileError(mod, scope, call);3424 const result = try addZIRUnOp(mod, scope, src, .ptrtoint, operand);
3096 } else if (mem.eql(u8, builtin_name, "@setEvalBranchQuota")) {3425 return rvalue(mod, scope, rl, result);
3097 return setEvalBranchQuota(mod, scope, call);3426 },
3098 } else if (mem.eql(u8, builtin_name, "@compileLog")) {3427 .float_cast => {
3099 return compileLog(mod, scope, call);3428 const dest_type = try typeExpr(mod, scope, params[0]);
3100 } else if (mem.eql(u8, builtin_name, "@field")) {3429 const rhs = try expr(mod, scope, .none, params[1]);
3101 return namedField(mod, scope, rl, call);3430 const result = try addZIRBinOp(mod, scope, src, .floatcast, dest_type, rhs);
3102 } else {3431 return rvalue(mod, scope, rl, result);
3103 return mod.failTok(scope, call.builtin_token, "invalid builtin function: '{s}'", .{builtin_name});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 }),
3104 }3585 }
3105}3586}
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
3108 const tree = scope.tree();3598 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();3602 const lhs = try expr(mod, scope, .none, call.ast.fn_expr);
3112 const args = try scope.getGenZIR().arena.alloc(*zir.Inst, param_nodes.len);3603
3113 for (param_nodes) |param_node, i| {3604 const args = try scope.getGenZIR().arena.alloc(*zir.Inst, call.ast.params.len);
3114 const param_src = tree.token_locs[param_node.firstToken()].start;3605 for (call.ast.params) |param_node, i| {
3606 const param_src = token_starts[tree.firstToken(param_node)];
3115 const param_type = try addZIRInst(mod, scope, param_src, zir.Inst.ParamType, .{3607 const param_type = try addZIRInst(mod, scope, param_src, zir.Inst.ParamType, .{
3116 .func = lhs,3608 .func = lhs,
3117 .arg_index = i,3609 .arg_index = i,
...@@ -3119,7 +3611,7 @@ fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Call) In...@@ -3119,7 +3611,7 @@ fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Call) In
3119 args[i] = try expr(mod, scope, .{ .ty = param_type }, param_node);3611 args[i] = try expr(mod, scope, .{ .ty = param_type }, param_node);
3120 }3612 }
31213613
3122 const src = tree.token_locs[node.lhs.firstToken()].start;3614 const src = token_starts[call.ast.lparen];
3123 const result = try addZIRInst(mod, scope, src, zir.Inst.Call, .{3615 const result = try addZIRInst(mod, scope, src, zir.Inst.Call, .{
3124 .func = lhs,3616 .func = lhs,
3125 .args = args,3617 .args = args,
...@@ -3128,288 +3620,244 @@ fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Call) In...@@ -3128,288 +3620,244 @@ fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Call) In
3128 return rvalue(mod, scope, rl, result);3620 return rvalue(mod, scope, rl, result);
3129}3621}
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 {
3132 const tree = scope.tree();3652 const tree = scope.tree();
3133 const src = tree.token_locs[unreach_node.token].start;3653 const node_tags = tree.nodes.items(.tag);
3134 return addZIRNoOp(mod, scope, src, .unreachable_safe);3654 const node_datas = tree.nodes.items(.data);
3135}3655 const main_tokens = tree.nodes.items(.main_token);
31363656 const token_tags = tree.tokens.items(.tag);
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}
31733657
3174fn nodeMayNeedMemoryLocation(start_node: *ast.Node, scope: *Scope) bool {
3175 var node = start_node;3658 var node = start_node;
3176 while (true) {3659 while (true) {
3177 switch (node.tag) {3660 switch (node_tags[node]) {
3178 .Root,3661 .root,
3179 .Use,3662 .@"usingnamespace",
3180 .TestDecl,3663 .test_decl,
3181 .DocComment,3664 .switch_case,
3182 .SwitchCase,3665 .switch_case_one,
3183 .SwitchElse,3666 .container_field_init,
3184 .Else,3667 .container_field_align,
3185 .Payload,3668 .container_field,
3186 .PointerPayload,3669 .asm_output,
3187 .PointerIndexPayload,3670 .asm_input,
3188 .ContainerField,
3189 .ErrorTag,
3190 .FieldInitializer,
3191 => unreachable,3671 => unreachable,
31923672
3193 .Return,3673 .@"return",
3194 .Break,3674 .@"break",
3195 .Continue,3675 .@"continue",
3196 .BitNot,3676 .bit_not,
3197 .BoolNot,3677 .bool_not,
3198 .VarDecl,3678 .global_var_decl,
3199 .Defer,3679 .local_var_decl,
3200 .AddressOf,3680 .simple_var_decl,
3201 .OptionalType,3681 .aligned_var_decl,
3202 .Negation,3682 .@"defer",
3203 .NegationWrap,3683 .@"errdefer",
3204 .Resume,3684 .address_of,
3205 .ArrayType,3685 .optional_type,
3206 .ArrayTypeSentinel,3686 .negation,
3207 .PtrType,3687 .negation_wrap,
3208 .SliceType,3688 .@"resume",
3209 .Suspend,3689 .array_type,
3210 .AnyType,3690 .array_type_sentinel,
3211 .ErrorType,3691 .ptr_type_aligned,
3212 .FnProto,3692 .ptr_type_sentinel,
3213 .AnyFrameType,3693 .ptr_type,
3214 .IntegerLiteral,3694 .ptr_type_bit_range,
3215 .FloatLiteral,3695 .@"suspend",
3216 .EnumLiteral,3696 .@"anytype",
3217 .StringLiteral,3697 .fn_proto_simple,
3218 .MultilineStringLiteral,3698 .fn_proto_multi,
3219 .CharLiteral,3699 .fn_proto_one,
3220 .BoolLiteral,3700 .fn_proto,
3221 .NullLiteral,3701 .fn_decl,
3222 .UndefinedLiteral,3702 .anyframe_type,
3223 .Unreachable,3703 .anyframe_literal,
3224 .Identifier,3704 .integer_literal,
3225 .ErrorSetDecl,3705 .float_literal,
3226 .ContainerDecl,3706 .enum_literal,
3227 .Asm,3707 .string_literal,
3228 .Add,3708 .multiline_string_literal,
3229 .AddWrap,3709 .char_literal,
3230 .ArrayCat,3710 .true_literal,
3231 .ArrayMult,3711 .false_literal,
3232 .Assign,3712 .null_literal,
3233 .AssignBitAnd,3713 .undefined_literal,
3234 .AssignBitOr,3714 .unreachable_literal,
3235 .AssignBitShiftLeft,3715 .identifier,
3236 .AssignBitShiftRight,3716 .error_set_decl,
3237 .AssignBitXor,3717 .container_decl,
3238 .AssignDiv,3718 .container_decl_trailing,
3239 .AssignSub,3719 .container_decl_two,
3240 .AssignSubWrap,3720 .container_decl_two_trailing,
3241 .AssignMod,3721 .container_decl_arg,
3242 .AssignAdd,3722 .container_decl_arg_trailing,
3243 .AssignAddWrap,3723 .tagged_union,
3244 .AssignMul,3724 .tagged_union_trailing,
3245 .AssignMulWrap,3725 .tagged_union_two,
3246 .BangEqual,3726 .tagged_union_two_trailing,
3247 .BitAnd,3727 .tagged_union_enum_tag,
3248 .BitOr,3728 .tagged_union_enum_tag_trailing,
3249 .BitShiftLeft,3729 .@"asm",
3250 .BitShiftRight,3730 .asm_simple,
3251 .BitXor,3731 .add,
3252 .BoolAnd,3732 .add_wrap,
3253 .BoolOr,3733 .array_cat,
3254 .Div,3734 .array_mult,
3255 .EqualEqual,3735 .assign,
3256 .ErrorUnion,3736 .assign_bit_and,
3257 .GreaterOrEqual,3737 .assign_bit_or,
3258 .GreaterThan,3738 .assign_bit_shift_left,
3259 .LessOrEqual,3739 .assign_bit_shift_right,
3260 .LessThan,3740 .assign_bit_xor,
3261 .MergeErrorSets,3741 .assign_div,
3262 .Mod,3742 .assign_sub,
3263 .Mul,3743 .assign_sub_wrap,
3264 .MulWrap,3744 .assign_mod,
3265 .Range,3745 .assign_add,
3266 .Period,3746 .assign_add_wrap,
3267 .Sub,3747 .assign_mul,
3268 .SubWrap,3748 .assign_mul_wrap,
3269 .Slice,3749 .bang_equal,
3270 .Deref,3750 .bit_and,
3271 .ArrayAccess,3751 .bit_or,
3272 .Block,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.
3273 => return false,3782 => return false,
32743783
3275 // Forward the question to a sub-expression.3784 // Forward the question to the LHS sub-expression.
3276 .GroupedExpression => node = node.castTag(.GroupedExpression).?.expr,3785 .grouped_expression,
3277 .Try => node = node.castTag(.Try).?.rhs,3786 .@"try",
3278 .Await => node = node.castTag(.Await).?.rhs,3787 .@"await",
3279 .Catch => node = node.castTag(.Catch).?.rhs,3788 .@"comptime",
3280 .OrElse => node = node.castTag(.OrElse).?.rhs,3789 .@"nosuspend",
3281 .Comptime => node = node.castTag(.Comptime).?.expr,3790 .unwrap_optional,
3282 .Nosuspend => node = node.castTag(.Nosuspend).?.expr,3791 => node = node_datas[node].lhs,
3283 .UnwrapOptional => node = node.castTag(.UnwrapOptional).?.lhs,3792
3793 // Forward the question to the RHS sub-expression.
3794 .@"catch",
3795 .@"orelse",
3796 => node = node_datas[node].rhs,
32843797
3285 // True because these are exactly the expressions we need memory locations for.3798 // True because these are exactly the expressions we need memory locations for.
3286 .ArrayInitializer,3799 .array_init_one,
3287 .ArrayInitializerDot,3800 .array_init_one_comma,
3288 .StructInitializer,3801 .array_init_dot_two,
3289 .StructInitializerDot,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,
3290 => return true,3815 => return true,
32913816
3292 // True because depending on comptime conditions, sub-expressions3817 // True because depending on comptime conditions, sub-expressions
3293 // may be the kind that need memory locations.3818 // may be the kind that need memory locations.
3294 .While,3819 .@"while", // This variant always has an else expression.
3295 .For,3820 .@"if", // This variant always has an else expression.
3296 .Switch,3821 .@"for", // This variant always has an else expression.
3297 .Call,3822 .@"switch",
3298 .LabeledBlock,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,
3299 => return true,3832 => return true,
33003833
3301 .BuiltinCall => {3834 .block_two,
3302 @setEvalBranchQuota(5000);3835 .block_two_semicolon,
3303 const builtin_needs_mem_loc = std.ComptimeStringMap(bool, .{3836 .block,
3304 .{ "@addWithOverflow", false },3837 .block_semicolon,
3305 .{ "@alignCast", false },3838 => {
3306 .{ "@alignOf", false },3839 const lbrace = main_tokens[node];
3307 .{ "@as", true },3840 if (token_tags[lbrace - 1] == .colon) {
3308 .{ "@asyncCall", false },3841 // Labeled blocks may need a memory location to forward
3309 .{ "@atomicLoad", false },3842 // to their break statements.
3310 .{ "@atomicRmw", false },3843 return true;
3311 .{ "@atomicStore", false },3844 } else {
3312 .{ "@bitCast", true },3845 return false;
3313 .{ "@bitOffsetOf", false },3846 }
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).?;
3409 },3847 },
34103848
3411 // Depending on AST properties, they may need memory locations.3849 .builtin_call,
3412 .If => return node.castTag(.If).?.@"else" != null,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 },
3413 }3861 }
3414 }3862 }
3415}3863}
...@@ -3450,8 +3898,18 @@ fn rvalue(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerEr...@@ -3450,8 +3898,18 @@ fn rvalue(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerEr
3450 }3898 }
3451}3899}
34523900
3453fn rvalueVoid(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node, result: void) InnerError!*zir.Inst {3901/// TODO when reworking ZIR memory layout, make the void value correspond to a hard coded
3454 const src = scope.tree().token_locs[node.firstToken()].start;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)];
3455 const void_inst = try addZIRInstConst(mod, scope, src, .{3913 const void_inst = try addZIRInstConst(mod, scope, src, .{
3456 .ty = Type.initTag(.void),3914 .ty = Type.initTag(.void),
3457 .val = Value.initTag(.void_value),3915 .val = Value.initTag(.void_value),
...@@ -3547,6 +4005,29 @@ pub fn addZirInstTag(...@@ -3547,6 +4005,29 @@ pub fn addZirInstTag(
3547 return &inst.base;4005 return &inst.base;
3548}4006}
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
3550pub fn addZIRInstSpecial(4031pub fn addZIRInstSpecial(
3551 mod: *Module,4032 mod: *Module,
3552 scope: *Scope,4033 scope: *Scope,
src/clang.zig+7-6
...@@ -127,6 +127,9 @@ pub const APSInt = opaque {...@@ -127,6 +127,9 @@ pub const APSInt = opaque {
127127
128 pub const getNumWords = ZigClangAPSInt_getNumWords;128 pub const getNumWords = ZigClangAPSInt_getNumWords;
129 extern fn ZigClangAPSInt_getNumWords(*const APSInt) c_uint;129 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;
130};133};
131134
132pub const ASTContext = opaque {135pub const ASTContext = opaque {
...@@ -270,12 +273,12 @@ pub const CompoundAssignOperator = opaque {...@@ -270,12 +273,12 @@ pub const CompoundAssignOperator = opaque {
270273
271pub const CompoundStmt = opaque {274pub const CompoundStmt = opaque {
272 pub const body_begin = ZigClangCompoundStmt_body_begin;275 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
275 pub const body_end = ZigClangCompoundStmt_body_end;278 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;
279};282};
280283
281pub const ConditionalOperator = opaque {};284pub const ConditionalOperator = opaque {};
...@@ -407,7 +410,7 @@ pub const Expr = opaque {...@@ -407,7 +410,7 @@ pub const Expr = opaque {
407 pub const getBeginLoc = ZigClangExpr_getBeginLoc;410 pub const getBeginLoc = ZigClangExpr_getBeginLoc;
408 extern fn ZigClangExpr_getBeginLoc(*const Expr) SourceLocation;411 extern fn ZigClangExpr_getBeginLoc(*const Expr) SourceLocation;
409412
410 pub const EvaluateAsConstantExpr = ZigClangExpr_EvaluateAsConstantExpr;413 pub const evaluateAsConstantExpr = ZigClangExpr_EvaluateAsConstantExpr;
411 extern fn ZigClangExpr_EvaluateAsConstantExpr(*const Expr, *ExprEvalResult, Expr_ConstExprUsage, *const ASTContext) bool;414 extern fn ZigClangExpr_EvaluateAsConstantExpr(*const Expr, *ExprEvalResult, Expr_ConstExprUsage, *const ASTContext) bool;
412};415};
413416
...@@ -694,8 +697,6 @@ pub const ReturnStmt = opaque {...@@ -694,8 +697,6 @@ pub const ReturnStmt = opaque {
694 extern fn ZigClangReturnStmt_getRetValue(*const ReturnStmt) ?*const Expr;697 extern fn ZigClangReturnStmt_getRetValue(*const ReturnStmt) ?*const Expr;
695};698};
696699
697pub const SkipFunctionBodiesScope = opaque {};
698
699pub const SourceManager = opaque {700pub const SourceManager = opaque {
700 pub const getSpellingLoc = ZigClangSourceManager_getSpellingLoc;701 pub const getSpellingLoc = ZigClangSourceManager_getSpellingLoc;
701 extern fn ZigClangSourceManager_getSpellingLoc(*const SourceManager, Loc: SourceLocation) SourceLocation;702 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 {...@@ -451,11 +451,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
451451
452 const src_data: struct { lbrace_src: usize, rbrace_src: usize, source: []const u8 } = blk: {452 const src_data: struct { lbrace_src: usize, rbrace_src: usize, source: []const u8 } = blk: {
453 const container_scope = module_fn.owner_decl.container;453 const container_scope = module_fn.owner_decl.container;
454 const tree = container_scope.file_scope.contents.tree;454 const tree = container_scope.file_scope.tree;
455 const fn_proto = tree.root_node.decls()[module_fn.owner_decl.src_index].castTag(.FnProto).?;455 const node_tags = tree.nodes.items(.tag);
456 const block = fn_proto.getBodyNode().?.castTag(.Block).?;456 const node_datas = tree.nodes.items(.data);
457 const lbrace_src = tree.token_locs[block.lbrace].start;457 const token_starts = tree.tokens.items(.start);
458 const rbrace_src = tree.token_locs[block.rbrace].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)];
459 break :blk .{464 break :blk .{
460 .lbrace_src = lbrace_src,465 .lbrace_src = lbrace_src,
461 .rbrace_src = rbrace_src,466 .rbrace_src = rbrace_src,
src/codegen/c.zig+56-2
...@@ -1,4 +1,5 @@...@@ -1,4 +1,5 @@
1const std = @import("std");1const std = @import("std");
2const assert = std.debug.assert;
2const mem = std.mem;3const mem = std.mem;
3const log = std.log.scoped(.c);4const log = std.log.scoped(.c);
45
...@@ -42,7 +43,7 @@ pub const Object = struct {...@@ -42,7 +43,7 @@ pub const Object = struct {
42 next_arg_index: usize = 0,43 next_arg_index: usize = 0,
43 next_local_index: usize = 0,44 next_local_index: usize = 0,
44 next_block_index: usize = 0,45 next_block_index: usize = 0,
45 indent_writer: std.io.AutoIndentingStream(std.ArrayList(u8).Writer),46 indent_writer: IndentWriter(std.ArrayList(u8).Writer),
4647
47 fn resolveInst(o: *Object, inst: *Inst) !CValue {48 fn resolveInst(o: *Object, inst: *Inst) !CValue {
48 if (inst.value()) |_| {49 if (inst.value()) |_| {
...@@ -63,7 +64,7 @@ pub const Object = struct {...@@ -63,7 +64,7 @@ pub const Object = struct {
63 return local_value;64 return local_value;
64 }65 }
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 {
67 return o.indent_writer.writer();68 return o.indent_writer.writer();
68 }69 }
6970
...@@ -796,3 +797,56 @@ fn genAsm(o: *Object, as: *Inst.Assembly) !CValue {...@@ -796,3 +797,56 @@ fn genAsm(o: *Object, as: *Inst.Assembly) !CValue {
796797
797 return o.dg.fail(o.dg.decl.src(), "TODO: C backend: inline asm expression result used", .{});798 return o.dg.fail(o.dg.decl.src(), "TODO: C backend: inline asm expression result used", .{});
798}799}
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 {...@@ -317,6 +317,7 @@ pub const Inst = struct {
317 pub const base_tag = Tag.arg;317 pub const base_tag = Tag.arg;
318318
319 base: Inst,319 base: Inst,
320 /// This exists to be emitted into debug info.
320 name: [*:0]const u8,321 name: [*:0]const u8,
321322
322 pub fn operandCount(self: *const Arg) usize {323 pub fn operandCount(self: *const Arg) usize {
src/link.zig+2-2
...@@ -550,11 +550,11 @@ pub const File = struct {...@@ -550,11 +550,11 @@ pub const File = struct {
550 id_symlink_basename,550 id_symlink_basename,
551 &prev_digest_buf,551 &prev_digest_buf,
552 ) catch |err| b: {552 ) 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) });
554 break :b prev_digest_buf[0..0];554 break :b prev_digest_buf[0..0];
555 };555 };
556 if (mem.eql(u8, prev_digest, &digest)) {556 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});
558 base.lock = man.toOwnedLock();558 base.lock = man.toOwnedLock();
559 return;559 return;
560 }560 }
src/link/C.zig+1-1
...@@ -97,7 +97,7 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {...@@ -97,7 +97,7 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
97 .value_map = codegen.CValueMap.init(module.gpa),97 .value_map = codegen.CValueMap.init(module.gpa),
98 .indent_writer = undefined, // set later so we can get a pointer to object.code98 .indent_writer = undefined, // set later so we can get a pointer to object.code
99 };99 };
100 object.indent_writer = std.io.autoIndentingStream(4, object.code.writer());100 object.indent_writer = .{ .underlying_writer = object.code.writer() };
101 defer object.value_map.deinit();101 defer object.value_map.deinit();
102 defer object.code.deinit();102 defer object.code.deinit();
103 defer object.dg.fwd_decl.deinit();103 defer object.dg.fwd_decl.deinit();
src/link/Coff.zig+3-3
...@@ -892,17 +892,17 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {...@@ -892,17 +892,17 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
892 id_symlink_basename,892 id_symlink_basename,
893 &prev_digest_buf,893 &prev_digest_buf,
894 ) catch |err| blk: {894 ) 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) });
896 // Handle this as a cache miss.896 // Handle this as a cache miss.
897 break :blk prev_digest_buf[0..0];897 break :blk prev_digest_buf[0..0];
898 };898 };
899 if (mem.eql(u8, prev_digest, &digest)) {899 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});
901 // Hot diggity dog! The output binary is already there.901 // Hot diggity dog! The output binary is already there.
902 self.base.lock = man.toOwnedLock();902 self.base.lock = man.toOwnedLock();
903 return;903 return;
904 }904 }
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
907 // We are about to change the output file to be different, so we invalidate the build hash now.907 // We are about to change the output file to be different, so we invalidate the build hash now.
908 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {908 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 {...@@ -1365,17 +1365,17 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
1365 id_symlink_basename,1365 id_symlink_basename,
1366 &prev_digest_buf,1366 &prev_digest_buf,
1367 ) catch |err| blk: {1367 ) 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) });
1369 // Handle this as a cache miss.1369 // Handle this as a cache miss.
1370 break :blk prev_digest_buf[0..0];1370 break :blk prev_digest_buf[0..0];
1371 };1371 };
1372 if (mem.eql(u8, prev_digest, &digest)) {1372 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});
1374 // Hot diggity dog! The output binary is already there.1374 // Hot diggity dog! The output binary is already there.
1375 self.base.lock = man.toOwnedLock();1375 self.base.lock = man.toOwnedLock();
1376 return;1376 return;
1377 }1377 }
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
1380 // We are about to change the output file to be different, so we invalidate the build hash now.1380 // We are about to change the output file to be different, so we invalidate the build hash now.
1381 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {1381 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 {...@@ -2223,13 +2223,19 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
2223 try dbg_line_buffer.ensureCapacity(26);2223 try dbg_line_buffer.ensureCapacity(26);
22242224
2225 const line_off: u28 = blk: {2225 const line_off: u28 = blk: {
2226 const tree = decl.container.file_scope.contents.tree;2226 const tree = decl.container.file_scope.tree;
2227 const file_ast_decls = tree.root_node.decls();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();
2228 // TODO Look into improving the performance here by adding a token-index-to-line2232 // TODO Look into improving the performance here by adding a token-index-to-line
2229 // lookup table. Currently this involves scanning over the source code for newlines.2233 // lookup table. Currently this involves scanning over the source code for newlines.
2230 const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?;2234 const fn_decl = file_ast_decls[decl.src_index];
2231 const block = fn_proto.getBodyNode().?.castTag(.Block).?;2235 assert(node_tags[fn_decl] == .fn_decl);
2232 const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start);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]);
2233 break :blk @intCast(u28, line_delta);2239 break :blk @intCast(u28, line_delta);
2234 };2240 };
22352241
...@@ -2744,13 +2750,19 @@ pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Dec...@@ -2744,13 +2750,19 @@ pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Dec
27442750
2745 if (self.llvm_ir_module) |_| return;2751 if (self.llvm_ir_module) |_| return;
27462752
2747 const tree = decl.container.file_scope.contents.tree;2753 const tree = decl.container.file_scope.tree;
2748 const file_ast_decls = tree.root_node.decls();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();
2749 // TODO Look into improving the performance here by adding a token-index-to-line2759 // TODO Look into improving the performance here by adding a token-index-to-line
2750 // lookup table. Currently this involves scanning over the source code for newlines.2760 // lookup table. Currently this involves scanning over the source code for newlines.
2751 const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?;2761 const fn_decl = file_ast_decls[decl.src_index];
2752 const block = fn_proto.getBodyNode().?.castTag(.Block).?;2762 assert(node_tags[fn_decl] == .fn_decl);
2753 const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start);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]);
2754 const casted_line_off = @intCast(u28, line_delta);2766 const casted_line_off = @intCast(u28, line_delta);
27552767
2756 const shdr = &self.sections.items[self.debug_line_section_index.?];2768 const shdr = &self.sections.items[self.debug_line_section_index.?];
...@@ -3025,7 +3037,7 @@ const min_nop_size = 2;...@@ -3025,7 +3037,7 @@ const min_nop_size = 2;
30253037
3026/// Writes to the file a buffer, prefixed and suffixed by the specified number of3038/// Writes to the file a buffer, prefixed and suffixed by the specified number of
3027/// bytes of NOPs. Asserts each padding size is at least `min_nop_size` and total padding bytes3039/// 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 be3040/// are less than 1044480 bytes (if this limit is ever reached, this function can be
3029/// improved to make more than one pwritev call, or the limit can be raised by a fixed3041/// improved to make more than one pwritev call, or the limit can be raised by a fixed
3030/// amount by increasing the length of `vecs`).3042/// amount by increasing the length of `vecs`).
3031fn pwriteDbgLineNops(3043fn pwriteDbgLineNops(
...@@ -3040,7 +3052,7 @@ fn pwriteDbgLineNops(...@@ -3040,7 +3052,7 @@ fn pwriteDbgLineNops(
30403052
3041 const page_of_nops = [1]u8{DW.LNS_negate_stmt} ** 4096;3053 const page_of_nops = [1]u8{DW.LNS_negate_stmt} ** 4096;
3042 const three_byte_nop = [3]u8{ DW.LNS_advance_pc, 0b1000_0000, 0 };3054 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;
3044 var vec_index: usize = 0;3056 var vec_index: usize = 0;
3045 {3057 {
3046 var padding_left = prev_padding_size;3058 var padding_left = prev_padding_size;
src/link/MachO.zig+3-3
...@@ -556,17 +556,17 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {...@@ -556,17 +556,17 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
556 id_symlink_basename,556 id_symlink_basename,
557 &prev_digest_buf,557 &prev_digest_buf,
558 ) catch |err| blk: {558 ) 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) });
560 // Handle this as a cache miss.560 // Handle this as a cache miss.
561 break :blk prev_digest_buf[0..0];561 break :blk prev_digest_buf[0..0];
562 };562 };
563 if (mem.eql(u8, prev_digest, &digest)) {563 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});
565 // Hot diggity dog! The output binary is already there.565 // Hot diggity dog! The output binary is already there.
566 self.base.lock = man.toOwnedLock();566 self.base.lock = man.toOwnedLock();
567 return;567 return;
568 }568 }
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
571 // We are about to change the output file to be different, so we invalidate the build hash now.571 // We are about to change the output file to be different, so we invalidate the build hash now.
572 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {572 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...@@ -904,13 +904,19 @@ pub fn updateDeclLineNumber(self: *DebugSymbols, module: *Module, decl: *const M
904 const tracy = trace(@src());904 const tracy = trace(@src());
905 defer tracy.end();905 defer tracy.end();
906906
907 const tree = decl.container.file_scope.contents.tree;907 const tree = decl.container.file_scope.tree;
908 const file_ast_decls = tree.root_node.decls();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();
909 // TODO Look into improving the performance here by adding a token-index-to-line913 // TODO Look into improving the performance here by adding a token-index-to-line
910 // lookup table. Currently this involves scanning over the source code for newlines.914 // lookup table. Currently this involves scanning over the source code for newlines.
911 const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?;915 const fn_decl = file_ast_decls[decl.src_index];
912 const block = fn_proto.getBodyNode().?.castTag(.Block).?;916 assert(node_tags[fn_decl] == .fn_decl);
913 const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start);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]);
914 const casted_line_off = @intCast(u28, line_delta);920 const casted_line_off = @intCast(u28, line_delta);
915921
916 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;922 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
...@@ -948,13 +954,19 @@ pub fn initDeclDebugBuffers(...@@ -948,13 +954,19 @@ pub fn initDeclDebugBuffers(
948 try dbg_line_buffer.ensureCapacity(26);954 try dbg_line_buffer.ensureCapacity(26);
949955
950 const line_off: u28 = blk: {956 const line_off: u28 = blk: {
951 const tree = decl.container.file_scope.contents.tree;957 const tree = decl.container.file_scope.tree;
952 const file_ast_decls = tree.root_node.decls();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();
953 // TODO Look into improving the performance here by adding a token-index-to-line963 // TODO Look into improving the performance here by adding a token-index-to-line
954 // lookup table. Currently this involves scanning over the source code for newlines.964 // lookup table. Currently this involves scanning over the source code for newlines.
955 const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?;965 const fn_decl = file_ast_decls[decl.src_index];
956 const block = fn_proto.getBodyNode().?.castTag(.Block).?;966 assert(node_tags[fn_decl] == .fn_decl);
957 const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start);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]);
958 break :blk @intCast(u28, line_delta);970 break :blk @intCast(u28, line_delta);
959 };971 };
960972
src/link/Wasm.zig+3-3
...@@ -391,17 +391,17 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {...@@ -391,17 +391,17 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
391 id_symlink_basename,391 id_symlink_basename,
392 &prev_digest_buf,392 &prev_digest_buf,
393 ) catch |err| blk: {393 ) 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) });
395 // Handle this as a cache miss.395 // Handle this as a cache miss.
396 break :blk prev_digest_buf[0..0];396 break :blk prev_digest_buf[0..0];
397 };397 };
398 if (mem.eql(u8, prev_digest, &digest)) {398 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});
400 // Hot diggity dog! The output binary is already there.400 // Hot diggity dog! The output binary is already there.
401 self.base.lock = man.toOwnedLock();401 self.base.lock = man.toOwnedLock();
402 return;402 return;
403 }403 }
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
406 // We are about to change the output file to be different, so we invalidate the build hash now.406 // We are about to change the output file to be different, so we invalidate the build hash now.
407 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {407 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...@@ -2158,7 +2158,7 @@ fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !voi
2158 const c_headers_dir_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{"include"});2158 const c_headers_dir_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{"include"});
2159 const c_headers_dir_path_z = try arena.dupeZ(u8, c_headers_dir_path);2159 const c_headers_dir_path_z = try arena.dupeZ(u8, c_headers_dir_path);
2160 var clang_errors: []translate_c.ClangErrMsg = &[0]translate_c.ClangErrMsg{};2160 var clang_errors: []translate_c.ClangErrMsg = &[0]translate_c.ClangErrMsg{};
2161 const tree = translate_c.translate(2161 var tree = translate_c.translate(
2162 comp.gpa,2162 comp.gpa,
2163 new_argv.ptr,2163 new_argv.ptr,
2164 new_argv.ptr + new_argv.len,2164 new_argv.ptr + new_argv.len,
...@@ -2179,7 +2179,7 @@ fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !voi...@@ -2179,7 +2179,7 @@ fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !voi
2179 process.exit(1);2179 process.exit(1);
2180 },2180 },
2181 };2181 };
2182 defer tree.deinit();2182 defer tree.deinit(comp.gpa);
21832183
2184 if (out_dep_path) |dep_file_path| {2184 if (out_dep_path) |dep_file_path| {
2185 const dep_basename = std.fs.path.basename(dep_file_path);2185 const dep_basename = std.fs.path.basename(dep_file_path);
...@@ -2193,16 +2193,21 @@ fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !voi...@@ -2193,16 +2193,21 @@ fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !voi
21932193
2194 const digest = man.final();2194 const digest = man.final();
2195 const o_sub_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest });2195 const o_sub_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest });
2196
2196 var o_dir = try comp.local_cache_directory.handle.makeOpenPath(o_sub_path, .{});2197 var o_dir = try comp.local_cache_directory.handle.makeOpenPath(o_sub_path, .{});
2197 defer o_dir.close();2198 defer o_dir.close();
2199
2198 var zig_file = try o_dir.createFile(translated_zig_basename, .{});2200 var zig_file = try o_dir.createFile(translated_zig_basename, .{});
2199 defer zig_file.close();2201 defer zig_file.close();
22002202
2201 var bw = io.bufferedWriter(zig_file.writer());2203 const formatted = try tree.render(comp.gpa);
2202 _ = try std.zig.render(comp.gpa, bw.writer(), tree);2204 defer comp.gpa.free(formatted);
2203 try bw.flush();
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
2207 break :digest digest;2212 break :digest digest;
2208 };2213 };
...@@ -2689,10 +2694,10 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {...@@ -2689,10 +2694,10 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
2689 const source_code = try stdin.readAllAlloc(gpa, max_src_size);2694 const source_code = try stdin.readAllAlloc(gpa, max_src_size);
2690 defer gpa.free(source_code);2695 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| {
2693 fatal("error parsing stdin: {s}", .{err});2698 fatal("error parsing stdin: {s}", .{err});
2694 };2699 };
2695 defer tree.deinit();2700 defer tree.deinit(gpa);
26962701
2697 for (tree.errors) |parse_error| {2702 for (tree.errors) |parse_error| {
2698 try printErrMsgToFile(gpa, parse_error, tree, "<stdin>", stderr_file, color);2703 try printErrMsgToFile(gpa, parse_error, tree, "<stdin>", stderr_file, color);
...@@ -2700,16 +2705,15 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {...@@ -2700,16 +2705,15 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
2700 if (tree.errors.len != 0) {2705 if (tree.errors.len != 0) {
2701 process.exit(1);2706 process.exit(1);
2702 }2707 }
2708 const formatted = try tree.render(gpa);
2709 defer gpa.free(formatted);
2710
2703 if (check_flag) {2711 if (check_flag) {
2704 const anything_changed = try std.zig.render(gpa, io.null_writer, tree);2712 const code: u8 = @boolToInt(mem.eql(u8, formatted, source_code));
2705 const code = if (anything_changed) @as(u8, 1) else @as(u8, 0);
2706 process.exit(code);2713 process.exit(code);
2707 }2714 }
27082715
2709 var bw = io.bufferedWriter(io.getStdOut().writer());2716 return io.getStdOut().writeAll(formatted);
2710 _ = try std.zig.render(gpa, bw.writer(), tree);
2711 try bw.flush();
2712 return;
2713 }2717 }
27142718
2715 if (input_files.items.len == 0) {2719 if (input_files.items.len == 0) {
...@@ -2846,8 +2850,8 @@ fn fmtPathFile(...@@ -2846,8 +2850,8 @@ fn fmtPathFile(
2846 // Add to set after no longer possible to get error.IsDir.2850 // Add to set after no longer possible to get error.IsDir.
2847 if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;2851 if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;
28482852
2849 const tree = try std.zig.parse(fmt.gpa, source_code);2853 var tree = try std.zig.parse(fmt.gpa, source_code);
2850 defer tree.deinit();2854 defer tree.deinit(fmt.gpa);
28512855
2852 for (tree.errors) |parse_error| {2856 for (tree.errors) |parse_error| {
2853 try printErrMsgToFile(fmt.gpa, parse_error, tree, file_path, std.io.getStdErr(), fmt.color);2857 try printErrMsgToFile(fmt.gpa, parse_error, tree, file_path, std.io.getStdErr(), fmt.color);
...@@ -2857,22 +2861,19 @@ fn fmtPathFile(...@@ -2857,22 +2861,19 @@ fn fmtPathFile(
2857 return;2861 return;
2858 }2862 }
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
2860 if (check_mode) {2872 if (check_mode) {
2861 const anything_changed = try std.zig.render(fmt.gpa, io.null_writer, tree);2873 const stdout = io.getStdOut().writer();
2862 if (anything_changed) {2874 try stdout.print("{s}\n", .{file_path});
2863 const stdout = io.getStdOut().writer();2875 fmt.any_error = true;
2864 try stdout.print("{s}\n", .{file_path});
2865 fmt.any_error = true;
2866 }
2867 } else {2876 } 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
2876 var af = try dir.atomicFile(sub_path, .{ .mode = stat.mode });2877 var af = try dir.atomicFile(sub_path, .{ .mode = stat.mode });
2877 defer af.deinit();2878 defer af.deinit();
28782879
...@@ -2886,7 +2887,7 @@ fn fmtPathFile(...@@ -2886,7 +2887,7 @@ fn fmtPathFile(
2886fn printErrMsgToFile(2887fn printErrMsgToFile(
2887 gpa: *mem.Allocator,2888 gpa: *mem.Allocator,
2888 parse_error: ast.Error,2889 parse_error: ast.Error,
2889 tree: *ast.Tree,2890 tree: ast.Tree,
2890 path: []const u8,2891 path: []const u8,
2891 file: fs.File,2892 file: fs.File,
2892 color: Color,2893 color: Color,
...@@ -2896,19 +2897,17 @@ fn printErrMsgToFile(...@@ -2896,19 +2897,17 @@ fn printErrMsgToFile(
2896 .on => true,2897 .on => true,
2897 .off => false,2898 .off => false,
2898 };2899 };
2899 const lok_token = parse_error.loc();2900 const lok_token = parse_error.token;
2900 const span_first = lok_token;
2901 const span_last = lok_token;
29022901
2903 const first_token = tree.token_locs[span_first];2902 const token_starts = tree.tokens.items(.start);
2904 const last_token = tree.token_locs[span_last];2903 const token_tags = tree.tokens.items(.tag);
2905 const start_loc = tree.tokenLocationLoc(0, first_token);2904 const first_token_start = token_starts[lok_token];
2906 const end_loc = tree.tokenLocationLoc(first_token.end, last_token);2905 const start_loc = tree.tokenLocation(0, lok_token);
29072906
2908 var text_buf = std.ArrayList(u8).init(gpa);2907 var text_buf = std.ArrayList(u8).init(gpa);
2909 defer text_buf.deinit();2908 defer text_buf.deinit();
2910 const writer = text_buf.writer();2909 const writer = text_buf.writer();
2911 try parse_error.render(tree.token_ids, writer);2910 try tree.renderError(parse_error, writer);
2912 const text = text_buf.items;2911 const text = text_buf.items;
29132912
2914 const stream = file.writer();2913 const stream = file.writer();
...@@ -2925,8 +2924,12 @@ fn printErrMsgToFile(...@@ -2925,8 +2924,12 @@ fn printErrMsgToFile(
2925 }2924 }
2926 try stream.writeByte('\n');2925 try stream.writeByte('\n');
2927 try stream.writeByteNTimes(' ', start_loc.column);2926 try stream.writeByteNTimes(' ', start_loc.column);
2928 try stream.writeByteNTimes('~', last_token.end - first_token.start);2927 if (token_tags[lok_token].lexeme()) |lexeme| {
2929 try stream.writeByte('\n');2928 try stream.writeByteNTimes('~', lexeme.len);
2929 try stream.writeByte('\n');
2930 } else {
2931 try stream.writeAll("^\n");
2932 }
2930}2933}
29312934
2932pub const info_zen =2935pub const info_zen =
src/test.zig+13-11
...@@ -155,7 +155,7 @@ pub const TestContext = struct {...@@ -155,7 +155,7 @@ pub const TestContext = struct {
155 self.updates.append(.{155 self.updates.append(.{
156 .src = src,156 .src = src,
157 .case = .{ .Header = result },157 .case = .{ .Header = result },
158 }) catch unreachable;158 }) catch @panic("out of memory");
159 }159 }
160160
161 /// Adds a subcase in which the module is updated with `src`, compiled,161 /// Adds a subcase in which the module is updated with `src`, compiled,
...@@ -164,7 +164,7 @@ pub const TestContext = struct {...@@ -164,7 +164,7 @@ pub const TestContext = struct {
164 self.updates.append(.{164 self.updates.append(.{
165 .src = src,165 .src = src,
166 .case = .{ .Execution = result },166 .case = .{ .Execution = result },
167 }) catch unreachable;167 }) catch @panic("out of memory");
168 }168 }
169169
170 /// Adds a subcase in which the module is updated with `src`, compiled,170 /// Adds a subcase in which the module is updated with `src`, compiled,
...@@ -173,7 +173,7 @@ pub const TestContext = struct {...@@ -173,7 +173,7 @@ pub const TestContext = struct {
173 self.updates.append(.{173 self.updates.append(.{
174 .src = src,174 .src = src,
175 .case = .{ .CompareObjectFile = result },175 .case = .{ .CompareObjectFile = result },
176 }) catch unreachable;176 }) catch @panic("out of memory");
177 }177 }
178178
179 /// Adds a subcase in which the module is updated with `src`, which179 /// Adds a subcase in which the module is updated with `src`, which
...@@ -181,7 +181,7 @@ pub const TestContext = struct {...@@ -181,7 +181,7 @@ pub const TestContext = struct {
181 /// for the expected reasons, given in sequential order in `errors` in181 /// for the expected reasons, given in sequential order in `errors` in
182 /// the form `:line:column: error: message`.182 /// the form `:line:column: error: message`.
183 pub fn addError(self: *Case, src: [:0]const u8, errors: []const []const u8) void {183 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");
185 for (errors) |err_msg_line, i| {185 for (errors) |err_msg_line, i| {
186 if (std.mem.startsWith(u8, err_msg_line, "error: ")) {186 if (std.mem.startsWith(u8, err_msg_line, "error: ")) {
187 array[i] = .{187 array[i] = .{
...@@ -224,7 +224,7 @@ pub const TestContext = struct {...@@ -224,7 +224,7 @@ pub const TestContext = struct {
224 },224 },
225 };225 };
226 }226 }
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");
228 }228 }
229229
230 /// Adds a subcase in which the module is updated with `src`, and230 /// Adds a subcase in which the module is updated with `src`, and
...@@ -247,7 +247,7 @@ pub const TestContext = struct {...@@ -247,7 +247,7 @@ pub const TestContext = struct {
247 .output_mode = .Exe,247 .output_mode = .Exe,
248 .extension = extension,248 .extension = extension,
249 .files = std.ArrayList(File).init(ctx.cases.allocator),249 .files = std.ArrayList(File).init(ctx.cases.allocator),
250 }) catch unreachable;250 }) catch @panic("out of memory");
251 return &ctx.cases.items[ctx.cases.items.len - 1];251 return &ctx.cases.items[ctx.cases.items.len - 1];
252 }252 }
253253
...@@ -262,15 +262,17 @@ pub const TestContext = struct {...@@ -262,15 +262,17 @@ pub const TestContext = struct {
262 }262 }
263263
264 pub fn exeFromCompiledC(ctx: *TestContext, name: []const u8, target: CrossTarget) *Case {264 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");
265 ctx.cases.append(Case{267 ctx.cases.append(Case{
266 .name = name,268 .name = prefixed_name,
267 .target = target,269 .target = target,
268 .updates = std.ArrayList(Update).init(ctx.cases.allocator),270 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
269 .output_mode = .Exe,271 .output_mode = .Exe,
270 .extension = .Zig,272 .extension = .Zig,
271 .object_format = .c,273 .object_format = .c,
272 .files = std.ArrayList(File).init(ctx.cases.allocator),274 .files = std.ArrayList(File).init(ctx.cases.allocator),
273 }) catch unreachable;275 }) catch @panic("out of memory");
274 return &ctx.cases.items[ctx.cases.items.len - 1];276 return &ctx.cases.items[ctx.cases.items.len - 1];
275 }277 }
276278
...@@ -285,7 +287,7 @@ pub const TestContext = struct {...@@ -285,7 +287,7 @@ pub const TestContext = struct {
285 .extension = .Zig,287 .extension = .Zig,
286 .files = std.ArrayList(File).init(ctx.cases.allocator),288 .files = std.ArrayList(File).init(ctx.cases.allocator),
287 .llvm_backend = true,289 .llvm_backend = true,
288 }) catch unreachable;290 }) catch @panic("out of memory");
289 return &ctx.cases.items[ctx.cases.items.len - 1];291 return &ctx.cases.items[ctx.cases.items.len - 1];
290 }292 }
291293
...@@ -302,7 +304,7 @@ pub const TestContext = struct {...@@ -302,7 +304,7 @@ pub const TestContext = struct {
302 .output_mode = .Obj,304 .output_mode = .Obj,
303 .extension = extension,305 .extension = extension,
304 .files = std.ArrayList(File).init(ctx.cases.allocator),306 .files = std.ArrayList(File).init(ctx.cases.allocator),
305 }) catch unreachable;307 }) catch @panic("out of memory");
306 return &ctx.cases.items[ctx.cases.items.len - 1];308 return &ctx.cases.items[ctx.cases.items.len - 1];
307 }309 }
308310
...@@ -326,7 +328,7 @@ pub const TestContext = struct {...@@ -326,7 +328,7 @@ pub const TestContext = struct {
326 .extension = ext,328 .extension = ext,
327 .object_format = .c,329 .object_format = .c,
328 .files = std.ArrayList(File).init(ctx.cases.allocator),330 .files = std.ArrayList(File).init(ctx.cases.allocator),
329 }) catch unreachable;331 }) catch @panic("out of memory");
330 return &ctx.cases.items[ctx.cases.items.len - 1];332 return &ctx.cases.items[ctx.cases.items.len - 1];
331 }333 }
332334
src/translate_c.zig+1790-3687
...@@ -3,23 +3,24 @@...@@ -3,23 +3,24 @@
33
4const std = @import("std");4const std = @import("std");
5const assert = std.debug.assert;5const assert = std.debug.assert;
6const ast = std.zig.ast;
7const Token = std.zig.Token;
8const clang = @import("clang.zig");6const clang = @import("clang.zig");
9const ctok = std.c.tokenizer;7const ctok = std.c.tokenizer;
10const CToken = std.c.Token;8const CToken = std.c.Token;
11const mem = std.mem;9const mem = std.mem;
12const math = std.math;10const math = std.math;
11const ast = @import("translate_c/ast.zig");
12const Node = ast.Node;
13const Tag = Node.Tag;
1314
14const CallingConvention = std.builtin.CallingConvention;15const CallingConvention = std.builtin.CallingConvention;
1516
16pub const ClangErrMsg = clang.Stage2ErrorMsg;17pub const ClangErrMsg = clang.Stage2ErrorMsg;
1718
18pub const Error = error{OutOfMemory};19pub const Error = std.mem.Allocator.Error;
19const TypeError = Error || error{UnsupportedType};20const TypeError = Error || error{UnsupportedType};
20const TransError = TypeError || error{UnsupportedTranslation};21const TransError = TypeError || error{UnsupportedTranslation};
2122
22const SymbolTable = std.StringArrayHashMap(*ast.Node);23const SymbolTable = std.StringArrayHashMap(Node);
23const AliasList = std.ArrayList(struct {24const AliasList = std.ArrayList(struct {
24 alias: []const u8,25 alias: []const u8,
25 name: []const u8,26 name: []const u8,
...@@ -30,23 +31,11 @@ const Scope = struct {...@@ -30,23 +31,11 @@ const Scope = struct {
30 parent: ?*Scope,31 parent: ?*Scope,
3132
32 const Id = enum {33 const Id = enum {
33 Switch,34 block,
34 Block,35 root,
35 Root,36 condition,
36 Condition,37 loop,
37 Loop,38 do_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,
50 };39 };
5140
52 /// Used for the scope of condition expressions, for example `if (cond)`.41 /// Used for the scope of condition expressions, for example `if (cond)`.
...@@ -67,16 +56,15 @@ const Scope = struct {...@@ -67,16 +56,15 @@ const Scope = struct {
67 }56 }
68 };57 };
6958
70 /// Represents an in-progress ast.Node.Block. This struct is stack-allocated.59 /// Represents an in-progress Node.Block. This struct is stack-allocated.
71 /// When it is deinitialized, it produces an ast.Node.Block which is allocated60 /// When it is deinitialized, it produces an Node.Block which is allocated
72 /// into the main arena.61 /// into the main arena.
73 const Block = struct {62 const Block = struct {
74 base: Scope,63 base: Scope,
75 statements: std.ArrayList(*ast.Node),64 statements: std.ArrayList(Node),
76 variables: AliasList,65 variables: AliasList,
77 label: ?ast.TokenIndex,
78 mangle_count: u32 = 0,66 mangle_count: u32 = 0,
79 lbrace: ast.TokenIndex,67 label: ?[]const u8 = null,
8068
81 /// When the block corresponds to a function, keep track of the return type69 /// When the block corresponds to a function, keep track of the return type
82 /// so that the return expression can be cast, if necessary70 /// so that the return expression can be cast, if necessary
...@@ -85,17 +73,14 @@ const Scope = struct {...@@ -85,17 +73,14 @@ const Scope = struct {
85 fn init(c: *Context, parent: *Scope, labeled: bool) !Block {73 fn init(c: *Context, parent: *Scope, labeled: bool) !Block {
86 var blk = Block{74 var blk = Block{
87 .base = .{75 .base = .{
88 .id = .Block,76 .id = .block,
89 .parent = parent,77 .parent = parent,
90 },78 },
91 .statements = std.ArrayList(*ast.Node).init(c.gpa),79 .statements = std.ArrayList(Node).init(c.gpa),
92 .variables = AliasList.init(c.gpa),80 .variables = AliasList.init(c.gpa),
93 .label = null,
94 .lbrace = try appendToken(c, .LBrace, "{"),
95 };81 };
96 if (labeled) {82 if (labeled) {
97 blk.label = try appendIdentifier(c, try blk.makeMangledName(c, "blk"));83 blk.label = try blk.makeMangledName(c, "blk");
98 _ = try appendToken(c, .Colon, ":");
99 }84 }
100 return blk;85 return blk;
101 }86 }
...@@ -106,31 +91,24 @@ const Scope = struct {...@@ -106,31 +91,24 @@ const Scope = struct {
106 self.* = undefined;91 self.* = undefined;
107 }92 }
10893
109 fn complete(self: *Block, c: *Context) !*ast.Node {94 fn complete(self: *Block, c: *Context) !Node {
110 // We reserve 1 extra statement if the parent is a Loop. This is in case of95 if (self.base.parent.?.id == .do_loop) {
111 // do while, we want to put `if (cond) break;` at the end.96 // We reserve 1 extra statement if the parent is a do_loop. This is in case of
112 const alloc_len = self.statements.items.len + @boolToInt(self.base.parent.?.id == .Loop);97 // do while, we want to put `if (cond) break;` at the end.
113 const rbrace = try appendToken(c, .RBrace, "}");98 const alloc_len = self.statements.items.len + @boolToInt(self.base.parent.?.id == .do_loop);
114 if (self.label) |label| {99 var stmts = try c.arena.alloc(Node, alloc_len);
115 const node = try ast.Node.LabeledBlock.alloc(c.arena, alloc_len);100 stmts.len = self.statements.items.len;
116 node.* = .{101 mem.copy(Node, stmts, self.statements.items);
117 .statements_len = self.statements.items.len,102 return Tag.block.create(c.arena, .{
118 .lbrace = self.lbrace,103 .label = self.label,
119 .rbrace = rbrace,104 .stmts = stmts,
120 .label = label,105 });
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;
133 }106 }
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 });
134 }112 }
135113
136 /// Given the desired name, return a name that does not shadow anything from outer scopes.114 /// Given the desired name, return a name that does not shadow anything from outer scopes.
...@@ -174,19 +152,27 @@ const Scope = struct {...@@ -174,19 +152,27 @@ const Scope = struct {
174 sym_table: SymbolTable,152 sym_table: SymbolTable,
175 macro_table: SymbolTable,153 macro_table: SymbolTable,
176 context: *Context,154 context: *Context,
155 nodes: std.ArrayList(Node),
177156
178 fn init(c: *Context) Root {157 fn init(c: *Context) Root {
179 return .{158 return .{
180 .base = .{159 .base = .{
181 .id = .Root,160 .id = .root,
182 .parent = null,161 .parent = null,
183 },162 },
184 .sym_table = SymbolTable.init(c.arena),163 .sym_table = SymbolTable.init(c.gpa),
185 .macro_table = SymbolTable.init(c.arena),164 .macro_table = SymbolTable.init(c.gpa),
186 .context = c,165 .context = c,
166 .nodes = std.ArrayList(Node).init(c.gpa),
187 };167 };
188 }168 }
189169
170 fn deinit(scope: *Root) void {
171 scope.sym_table.deinit();
172 scope.macro_table.deinit();
173 scope.nodes.deinit();
174 }
175
190 /// Check if the global scope contains this name, without looking into the "future", e.g.176 /// Check if the global scope contains this name, without looking into the "future", e.g.
191 /// ignore the preprocessed decl and macro names.177 /// ignore the preprocessed decl and macro names.
192 fn containsNow(scope: *Root, name: []const u8) bool {178 fn containsNow(scope: *Root, name: []const u8) bool {
...@@ -205,20 +191,20 @@ const Scope = struct {...@@ -205,20 +191,20 @@ const Scope = struct {
205 var scope = inner;191 var scope = inner;
206 while (true) {192 while (true) {
207 switch (scope.id) {193 switch (scope.id) {
208 .Root => unreachable,194 .root => unreachable,
209 .Block => return @fieldParentPtr(Block, "base", scope),195 .block => return @fieldParentPtr(Block, "base", scope),
210 .Condition => return @fieldParentPtr(Condition, "base", scope).getBlockScope(c),196 .condition => return @fieldParentPtr(Condition, "base", scope).getBlockScope(c),
211 else => scope = scope.parent.?,197 else => scope = scope.parent.?,
212 }198 }
213 }199 }
214 }200 }
215201
216 fn findBlockReturnType(inner: *Scope, c: *Context) ?clang.QualType {202 fn findBlockReturnType(inner: *Scope, c: *Context) clang.QualType {
217 var scope = inner;203 var scope = inner;
218 while (true) {204 while (true) {
219 switch (scope.id) {205 switch (scope.id) {
220 .Root => return null,206 .root => unreachable,
221 .Block => {207 .block => {
222 const block = @fieldParentPtr(Block, "base", scope);208 const block = @fieldParentPtr(Block, "base", scope);
223 if (block.return_type) |qt| return qt;209 if (block.return_type) |qt| return qt;
224 scope = scope.parent.?;210 scope = scope.parent.?;
...@@ -230,17 +216,17 @@ const Scope = struct {...@@ -230,17 +216,17 @@ const Scope = struct {
230216
231 fn getAlias(scope: *Scope, name: []const u8) []const u8 {217 fn getAlias(scope: *Scope, name: []const u8) []const u8 {
232 return switch (scope.id) {218 return switch (scope.id) {
233 .Root => return name,219 .root => return name,
234 .Block => @fieldParentPtr(Block, "base", scope).getAlias(name),220 .block => @fieldParentPtr(Block, "base", scope).getAlias(name),
235 .Switch, .Loop, .Condition => scope.parent.?.getAlias(name),221 .loop, .do_loop, .condition => scope.parent.?.getAlias(name),
236 };222 };
237 }223 }
238224
239 fn contains(scope: *Scope, name: []const u8) bool {225 fn contains(scope: *Scope, name: []const u8) bool {
240 return switch (scope.id) {226 return switch (scope.id) {
241 .Root => @fieldParentPtr(Root, "base", scope).contains(name),227 .root => @fieldParentPtr(Root, "base", scope).contains(name),
242 .Block => @fieldParentPtr(Block, "base", scope).contains(name),228 .block => @fieldParentPtr(Block, "base", scope).contains(name),
243 .Switch, .Loop, .Condition => scope.parent.?.contains(name),229 .loop, .do_loop, .condition => scope.parent.?.contains(name),
244 };230 };
245 }231 }
246232
...@@ -248,20 +234,26 @@ const Scope = struct {...@@ -248,20 +234,26 @@ const Scope = struct {
248 var scope = inner;234 var scope = inner;
249 while (true) {235 while (true) {
250 switch (scope.id) {236 switch (scope.id) {
251 .Root => unreachable,237 .root => unreachable,
252 .Switch => return scope,238 .loop, .do_loop => return scope,
253 .Loop => return scope,
254 else => scope = scope.parent.?,239 else => scope = scope.parent.?,
255 }240 }
256 }241 }
257 }242 }
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 {
260 var scope = inner;246 var scope = inner;
261 while (true) {247 while (true) {
262 switch (scope.id) {248 switch (scope.id) {
263 .Root => unreachable,249 .root => {
264 .Switch => return @fieldParentPtr(Switch, "base", scope),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 },
265 else => scope = scope.parent.?,257 else => scope = scope.parent.?,
266 }258 }
267 }259 }
...@@ -271,18 +263,12 @@ const Scope = struct {...@@ -271,18 +263,12 @@ const Scope = struct {
271pub const Context = struct {263pub const Context = struct {
272 gpa: *mem.Allocator,264 gpa: *mem.Allocator,
273 arena: *mem.Allocator,265 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,
279 source_manager: *clang.SourceManager,266 source_manager: *clang.SourceManager,
280 decl_table: std.AutoArrayHashMapUnmanaged(usize, []const u8) = .{},267 decl_table: std.AutoArrayHashMapUnmanaged(usize, []const u8) = .{},
281 alias_list: AliasList,268 alias_list: AliasList,
282 global_scope: *Scope.Root,269 global_scope: *Scope.Root,
283 clang_context: *clang.ASTContext,270 clang_context: *clang.ASTContext,
284 mangle_count: u32 = 0,271 mangle_count: u32 = 0,
285 root_decls: std.ArrayListUnmanaged(*ast.Node) = .{},
286 opaque_demotes: std.AutoHashMapUnmanaged(usize, void) = .{},272 opaque_demotes: std.AutoHashMapUnmanaged(usize, void) = .{},
287273
288 /// This one is different than the root scope's name table. This contains274 /// This one is different than the root scope's name table. This contains
...@@ -311,90 +297,15 @@ pub const Context = struct {...@@ -311,90 +297,15 @@ pub const Context = struct {
311 const column = c.source_manager.getSpellingColumnNumber(spelling_loc);297 const column = c.source_manager.getSpellingColumnNumber(spelling_loc);
312 return std.fmt.allocPrint(c.arena, "{s}:{d}:{d}", .{ filename, line, column });298 return std.fmt.allocPrint(c.arena, "{s}:{d}:{d}", .{ filename, line, column });
313 }299 }
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 }
348};300};
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
391pub fn translate(302pub fn translate(
392 gpa: *mem.Allocator,303 gpa: *mem.Allocator,
393 args_begin: [*]?[*]const u8,304 args_begin: [*]?[*]const u8,
394 args_end: [*]?[*]const u8,305 args_end: [*]?[*]const u8,
395 errors: *[]ClangErrMsg,306 errors: *[]ClangErrMsg,
396 resources_path: [*:0]const u8,307 resources_path: [*:0]const u8,
397) !*ast.Tree {308) !std.zig.ast.Tree {
398 const ast_unit = clang.LoadFromCommandLine(309 const ast_unit = clang.LoadFromCommandLine(
399 args_begin,310 args_begin,
400 args_end,311 args_end,
...@@ -407,9 +318,6 @@ pub fn translate(...@@ -407,9 +318,6 @@ pub fn translate(
407 };318 };
408 defer ast_unit.delete();319 defer ast_unit.delete();
409320
410 var source_buffer = std.ArrayList(u8).init(gpa);
411 defer source_buffer.deinit();
412
413 // For memory that has the same lifetime as the Tree that we return321 // For memory that has the same lifetime as the Tree that we return
414 // from this function.322 // from this function.
415 var arena = std.heap.ArenaAllocator.init(gpa);323 var arena = std.heap.ArenaAllocator.init(gpa);
...@@ -418,9 +326,7 @@ pub fn translate(...@@ -418,9 +326,7 @@ pub fn translate(
418 var context = Context{326 var context = Context{
419 .gpa = gpa,327 .gpa = gpa,
420 .arena = &arena.allocator,328 .arena = &arena.allocator,
421 .source_buffer = &source_buffer,
422 .source_manager = ast_unit.getSourceManager(),329 .source_manager = ast_unit.getSourceManager(),
423 .err = undefined,
424 .alias_list = AliasList.init(gpa),330 .alias_list = AliasList.init(gpa),
425 .global_scope = try arena.allocator.create(Scope.Root),331 .global_scope = try arena.allocator.create(Scope.Root),
426 .clang_context = ast_unit.getASTContext(),332 .clang_context = ast_unit.getASTContext(),
...@@ -429,20 +335,17 @@ pub fn translate(...@@ -429,20 +335,17 @@ pub fn translate(
429 defer {335 defer {
430 context.decl_table.deinit(gpa);336 context.decl_table.deinit(gpa);
431 context.alias_list.deinit();337 context.alias_list.deinit();
432 context.token_ids.deinit(gpa);
433 context.token_locs.deinit(gpa);
434 context.errors.deinit(gpa);
435 context.global_names.deinit(gpa);338 context.global_names.deinit(gpa);
436 context.root_decls.deinit(gpa);
437 context.opaque_demotes.deinit(gpa);339 context.opaque_demotes.deinit(gpa);
340 context.global_scope.deinit();
438 }341 }
439342
440 try addCBuiltinsNamespace(&context);343 try context.global_scope.nodes.append(Tag.usingnamespace_builtins.init());
441344
442 try prepopulateGlobalNameTable(ast_unit, &context);345 try prepopulateGlobalNameTable(ast_unit, &context);
443346
444 if (!ast_unit.visitLocalTopLevelDecls(&context, declVisitorC)) {347 if (!ast_unit.visitLocalTopLevelDecls(&context, declVisitorC)) {
445 return context.err;348 return error.OutOfMemory;
446 }349 }
447350
448 try transPreprocessorEntities(&context, ast_unit);351 try transPreprocessorEntities(&context, ast_unit);
...@@ -450,38 +353,17 @@ pub fn translate(...@@ -450,38 +353,17 @@ pub fn translate(
450 try addMacros(&context);353 try addMacros(&context);
451 for (context.alias_list.items) |alias| {354 for (context.alias_list.items) |alias| {
452 if (!context.global_scope.sym_table.contains(alias.alias)) {355 if (!context.global_scope.sym_table.contains(alias.alias)) {
453 try createAlias(&context, alias);356 const node = try Tag.alias.create(context.arena, .{ .actual = alias.alias, .mangled = alias.name });
454 }357 try addTopLevelDecl(&context, alias.alias, node);
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});
465 }358 }
466 }359 }
467360
468 const tree = try arena.allocator.create(ast.Tree);361 return ast.render(gpa, context.global_scope.nodes.items);
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;
480}362}
481363
482fn prepopulateGlobalNameTable(ast_unit: *clang.ASTUnit, c: *Context) !void {364fn prepopulateGlobalNameTable(ast_unit: *clang.ASTUnit, c: *Context) !void {
483 if (!ast_unit.visitLocalTopLevelDecls(c, declVisitorNamesOnlyC)) {365 if (!ast_unit.visitLocalTopLevelDecls(c, declVisitorNamesOnlyC)) {
484 return c.err;366 return error.OutOfMemory;
485 }367 }
486368
487 // TODO if we see #undef, delete it from the table369 // TODO if we see #undef, delete it from the table
...@@ -504,19 +386,13 @@ fn prepopulateGlobalNameTable(ast_unit: *clang.ASTUnit, c: *Context) !void {...@@ -504,19 +386,13 @@ fn prepopulateGlobalNameTable(ast_unit: *clang.ASTUnit, c: *Context) !void {
504386
505fn declVisitorNamesOnlyC(context: ?*c_void, decl: *const clang.Decl) callconv(.C) bool {387fn declVisitorNamesOnlyC(context: ?*c_void, decl: *const clang.Decl) callconv(.C) bool {
506 const c = @ptrCast(*Context, @alignCast(@alignOf(Context), context));388 const c = @ptrCast(*Context, @alignCast(@alignOf(Context), context));
507 declVisitorNamesOnly(c, decl) catch |err| {389 declVisitorNamesOnly(c, decl) catch return false;
508 c.err = err;
509 return false;
510 };
511 return true;390 return true;
512}391}
513392
514fn declVisitorC(context: ?*c_void, decl: *const clang.Decl) callconv(.C) bool {393fn declVisitorC(context: ?*c_void, decl: *const clang.Decl) callconv(.C) bool {
515 const c = @ptrCast(*Context, @alignCast(@alignOf(Context), context));394 const c = @ptrCast(*Context, @alignCast(@alignOf(Context), context));
516 declVisitor(c, decl) catch |err| {395 declVisitor(c, decl) catch return false;
517 c.err = err;
518 return false;
519 };
520 return true;396 return true;
521}397}
522398
...@@ -533,13 +409,13 @@ fn declVisitor(c: *Context, decl: *const clang.Decl) Error!void {...@@ -533,13 +409,13 @@ fn declVisitor(c: *Context, decl: *const clang.Decl) Error!void {
533 return visitFnDecl(c, @ptrCast(*const clang.FunctionDecl, decl));409 return visitFnDecl(c, @ptrCast(*const clang.FunctionDecl, decl));
534 },410 },
535 .Typedef => {411 .Typedef => {
536 _ = try transTypeDef(c, @ptrCast(*const clang.TypedefNameDecl, decl), true);412 try transTypeDef(c, &c.global_scope.base, @ptrCast(*const clang.TypedefNameDecl, decl));
537 },413 },
538 .Enum => {414 .Enum => {
539 _ = try transEnumDecl(c, @ptrCast(*const clang.EnumDecl, decl));415 try transEnumDecl(c, &c.global_scope.base, @ptrCast(*const clang.EnumDecl, decl));
540 },416 },
541 .Record => {417 .Record => {
542 _ = try transRecordDecl(c, @ptrCast(*const clang.RecordDecl, decl));418 try transRecordDecl(c, &c.global_scope.base, @ptrCast(*const clang.RecordDecl, decl));
543 },419 },
544 .Var => {420 .Var => {
545 return visitVarDecl(c, @ptrCast(*const clang.VarDecl, decl), null);421 return visitVarDecl(c, @ptrCast(*const clang.VarDecl, decl), null);
...@@ -549,7 +425,7 @@ fn declVisitor(c: *Context, decl: *const clang.Decl) Error!void {...@@ -549,7 +425,7 @@ fn declVisitor(c: *Context, decl: *const clang.Decl) Error!void {
549 },425 },
550 else => {426 else => {
551 const decl_name = try c.str(decl.getDeclKindName());427 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});
553 },429 },
554 }430 }
555}431}
...@@ -565,7 +441,6 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void {...@@ -565,7 +441,6 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void {
565 return visitFnDecl(c, def);441 return visitFnDecl(c, def);
566 }442 }
567443
568 const rp = makeRestorePoint(c);
569 const fn_decl_loc = fn_decl.getLocation();444 const fn_decl_loc = fn_decl.getLocation();
570 const has_body = fn_decl.hasBody();445 const has_body = fn_decl.hasBody();
571 const storage_class = fn_decl.getStorageClass();446 const storage_class = fn_decl.getStorageClass();
...@@ -609,9 +484,9 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void {...@@ -609,9 +484,9 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void {
609 decl_ctx.has_body = false;484 decl_ctx.has_body = false;
610 decl_ctx.storage_class = .Extern;485 decl_ctx.storage_class = .Extern;
611 decl_ctx.is_export = false;486 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", .{});
613 }488 }
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) {
615 error.UnsupportedType => {490 error.UnsupportedType => {
616 return failDecl(c, fn_decl_loc, fn_name, "unable to resolve prototype of function", .{});491 return failDecl(c, fn_decl_loc, fn_name, "unable to resolve prototype of function", .{});
617 },492 },
...@@ -620,7 +495,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void {...@@ -620,7 +495,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void {
620 },495 },
621 .FunctionNoProto => blk: {496 .FunctionNoProto => blk: {
622 const fn_no_proto_type = @ptrCast(*const clang.FunctionType, fn_type);497 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) {
624 error.UnsupportedType => {499 error.UnsupportedType => {
625 return failDecl(c, fn_decl_loc, fn_name, "unable to resolve prototype of function", .{});500 return failDecl(c, fn_decl_loc, fn_name, "unable to resolve prototype of function", .{});
626 },501 },
...@@ -631,124 +506,99 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void {...@@ -631,124 +506,99 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void {
631 };506 };
632507
633 if (!decl_ctx.has_body) {508 if (!decl_ctx.has_body) {
634 const semi_tok = try appendToken(c, .Semicolon, ";");509 return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base));
635 return addTopLevelDecl(c, fn_name, &proto_node.base);
636 }510 }
637511
638 // actual function definition with body512 // actual function definition with body
639 const body_stmt = fn_decl.getBody();513 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);
641 block_scope.return_type = return_qt;515 block_scope.return_type = return_qt;
642 defer block_scope.deinit();516 defer block_scope.deinit();
643517
644 var scope = &block_scope.base;518 var scope = &block_scope.base;
645519
646 var param_id: c_uint = 0;520 var param_id: c_uint = 0;
647 for (proto_node.params()) |*param, i| {521 for (proto_node.data.params) |*param, i| {
648 const param_name = if (param.name_token) |name_tok|522 const param_name = param.name orelse {
649 tokenSlice(c, name_tok)523 proto_node.data.is_extern = true;
650 else524 proto_node.data.is_export = false;
651 return failDecl(c, fn_decl_loc, fn_name, "function {s} parameter has no name", .{fn_name});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
653 const c_param = fn_decl.getParamDecl(param_id);529 const c_param = fn_decl.getParamDecl(param_id);
654 const qual_type = c_param.getOriginalType();530 const qual_type = c_param.getOriginalType();
655 const is_const = qual_type.isConstQualified();531 const is_const = qual_type.isConstQualified();
656532
657 const mangled_param_name = try block_scope.makeMangledName(c, param_name);533 const mangled_param_name = try block_scope.makeMangledName(c, param_name);
534 param.name = mangled_param_name;
658535
659 if (!is_const) {536 if (!is_const) {
660 const bare_arg_name = try std.fmt.allocPrint(c.arena, "arg_{s}", .{mangled_param_name});537 const bare_arg_name = try std.fmt.allocPrint(c.arena, "arg_{s}", .{mangled_param_name});
661 const arg_name = try block_scope.makeMangledName(c, bare_arg_name);538 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");541 const redecl_node = try Tag.arg_redecl.create(c.arena, .{ .actual = mangled_param_name, .mangled = arg_name });
664 const name_tok = try appendIdentifier(c, mangled_param_name);542 try block_scope.statements.append(redecl_node);
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, ":");
679 }543 }
680544
681 param_id += 1;545 param_id += 1;
682 }546 }
683547
684 const casted_body = @ptrCast(*const clang.CompoundStmt, body_stmt);548 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) {
686 error.OutOfMemory => |e| return e,550 error.OutOfMemory => |e| return e,
687 error.UnsupportedTranslation,551 error.UnsupportedTranslation,
688 error.UnsupportedType,552 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 },
690 };559 };
691 // add return statement if the function didn't have one560 // add return statement if the function didn't have one
692 blk: {561 blk: {
693 if (fn_ty.getNoReturnAttr()) break :blk;562 const maybe_body = try block_scope.complete(c);
694 if (isCVoid(return_qt)) break :blk;563 if (fn_ty.getNoReturnAttr() or isCVoid(return_qt) or maybe_body.isNoreturn(false)) {
695564 proto_node.data.body = maybe_body;
696 if (block_scope.statements.items.len > 0) {565 break :blk;
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 }
711 }566 }
712567
713 const return_expr = try ast.Node.ControlFlowExpression.create(rp.c.arena, .{568 const rhs = transZeroInitExpr(c, scope, fn_decl_loc, return_qt.getTypePtr()) catch |err| switch (err) {
714 .ltoken = try appendToken(rp.c, .Keyword_return, "return"),569 error.OutOfMemory => |e| return e,
715 .tag = .Return,570 error.UnsupportedTranslation,
716 }, .{571 error.UnsupportedType,
717 .rhs = transZeroInitExpr(rp, scope, fn_decl_loc, return_qt.getTypePtr()) catch |err| switch (err) {572 => {
718 error.OutOfMemory => |e| return e,573 proto_node.data.is_extern = true;
719 error.UnsupportedTranslation,574 proto_node.data.is_export = false;
720 error.UnsupportedType,575 try warn(c, &c.global_scope.base, fn_decl_loc, "unable to create a return value for function, demoted to extern", .{});
721 => return failDecl(c, fn_decl_loc, fn_name, "unable to create a return value for function", .{}),576 return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base));
722 },577 },
723 });578 };
724 _ = try appendToken(rp.c, .Semicolon, ";");579 const ret = try Tag.@"return".create(c.arena, rhs);
725 try block_scope.statements.append(&return_expr.base);580 try block_scope.statements.append(ret);
581 proto_node.data.body = try block_scope.complete(c);
726 }582 }
727583
728 const body_node = try block_scope.complete(rp.c);584 return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base));
729 proto_node.setBodyNode(body_node);
730 return addTopLevelDecl(c, fn_name, &proto_node.base);
731}585}
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 {
734 return if (decl_init) |init_expr|588 return if (decl_init) |init_expr|
735 transQualTypeInitialized(rp, qt, init_expr, loc)589 transQualTypeInitialized(c, scope, qt, init_expr, loc)
736 else590 else
737 transQualType(rp, qt, loc);591 transQualType(c, scope, qt, loc);
738}592}
593
739/// if mangled_name is not null, this var decl was declared in a block scope.594/// if mangled_name is not null, this var decl was declared in a block scope.
740fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]const u8) Error!void {595fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]const u8) Error!void {
741 const var_name = mangled_name orelse try c.str(@ptrCast(*const clang.NamedDecl, var_decl).getName_bytes_begin());596 const var_name = mangled_name orelse try c.str(@ptrCast(*const clang.NamedDecl, var_decl).getName_bytes_begin());
742 if (c.global_scope.sym_table.contains(var_name))597 if (c.global_scope.sym_table.contains(var_name))
743 return; // Avoid processing this decl twice598 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;
752 const scope = &c.global_scope.base;602 const scope = &c.global_scope.base;
753603
754 // TODO https://github.com/ziglang/zig/issues/3756604 // TODO https://github.com/ziglang/zig/issues/3756
...@@ -767,211 +617,148 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co...@@ -767,211 +617,148 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co
767 // does the same as:617 // does the same as:
768 // extern int foo;618 // extern int foo;
769 // int foo = 2;619 // int foo = 2;
770 const extern_tok = if (storage_class == .Extern and !has_init)620 var is_extern = storage_class == .Extern and !has_init;
771 try appendToken(c, .Keyword_extern, "extern")621 var is_export = !is_extern and storage_class != .Static;
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");
781622
782 const name_tok = try appendIdentifier(c, checked_name);623 const type_node = transQualTypeMaybeInitialized(c, scope, qual_type, decl_init, var_decl_loc) catch |err| switch (err) {
783
784 _ = try appendToken(c, .Colon, ":");
785
786 const type_node = transQualTypeMaybeInitialized(rp, qual_type, decl_init, var_decl_loc) catch |err| switch (err) {
787 error.UnsupportedTranslation, error.UnsupportedType => {624 error.UnsupportedTranslation, error.UnsupportedType => {
788 return failDecl(c, var_decl_loc, checked_name, "unable to resolve variable type", .{});625 return failDecl(c, var_decl_loc, checked_name, "unable to resolve variable type", .{});
789 },626 },
790 error.OutOfMemory => |e| return e,627 error.OutOfMemory => |e| return e,
791 };628 };
792629
793 var eq_tok: ast.TokenIndex = undefined;630 var init_node: ?Node = null;
794 var init_node: ?*ast.Node = null;
795631
796 // If the initialization expression is not present, initialize with undefined.632 // If the initialization expression is not present, initialize with undefined.
797 // If it is an integer literal, we can skip the @as since it will be redundant633 // If it is an integer literal, we can skip the @as since it will be redundant
798 // with the variable type.634 // with the variable type.
799 if (has_init) {635 if (has_init) trans_init: {
800 eq_tok = try appendToken(c, .Equal, "=");
801 if (decl_init) |expr| {636 if (decl_init) |expr| {
802 const node_or_error = if (expr.getStmtClass() == .StringLiteralClass)637 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)
804 else639 else
805 transExprCoercing(rp, scope, expr, .used, .r_value);640 transExprCoercing(c, scope, expr, .used);
806 init_node = node_or_error catch |err| switch (err) {641 init_node = node_or_error catch |err| switch (err) {
807 error.UnsupportedTranslation,642 error.UnsupportedTranslation,
808 error.UnsupportedType,643 error.UnsupportedType,
809 => {644 => {
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;
811 },649 },
812 error.OutOfMemory => |e| return e,650 error.OutOfMemory => |e| return e,
813 };651 };
652 if (!qualTypeIsBoolean(qual_type) and isBoolRes(init_node.?)) {
653 init_node = try Tag.bool_to_int.create(c.arena, init_node.?);
654 }
814 } else {655 } else {
815 init_node = try transCreateNodeUndefinedLiteral(c);656 init_node = Tag.undefined_literal.init();
816 }657 }
817 } else if (storage_class != .Extern) {658 } else if (storage_class != .Extern) {
818 eq_tok = try appendToken(c, .Equal, "=");
819 // The C language specification states that variables with static or threadlocal659 // The C language specification states that variables with static or threadlocal
820 // storage without an initializer are initialized to a zero value.660 // storage without an initializer are initialized to a zero value.
821661
822 // @import("std").mem.zeroes(T)662 // @import("std").mem.zeroes(T)
823 const import_fn_call = try c.createBuiltinCall("@import", 1);663 init_node = try Tag.std_mem_zeroes.create(c.arena, type_node);
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;
835 }664 }
836665
837 const linksection_expr = blk: {666 const linksection_string = blk: {
838 var str_len: usize = undefined;667 var str_len: usize = undefined;
839 if (var_decl.getSectionAttribute(&str_len)) |str_ptr| {668 if (var_decl.getSectionAttribute(&str_len)) |str_ptr| {
840 _ = try appendToken(rp.c, .Keyword_linksection, "linksection");669 break :blk str_ptr[0..str_len];
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;
849 }670 }
850 break :blk null;671 break :blk null;
851 };672 };
852673
853 const align_expr = blk: {674 const alignment = blk: {
854 const alignment = var_decl.getAlignedAttribute(rp.c.clang_context);675 const alignment = var_decl.getAlignedAttribute(c.clang_context);
855 if (alignment != 0) {676 if (alignment != 0) {
856 _ = try appendToken(rp.c, .Keyword_align, "align");
857 _ = try appendToken(rp.c, .LParen, "(");
858 // Clang reports the alignment in bits677 // Clang reports the alignment in bits
859 const expr = try transCreateNodeInt(rp.c, alignment / 8);678 break :blk alignment / 8;
860 _ = try appendToken(rp.c, .RParen, ")");
861
862 break :blk expr;
863 }679 }
864 break :blk null;680 break :blk null;
865 };681 };
866682
867 const node = try ast.Node.VarDecl.create(c.arena, .{683 const node = try Tag.var_decl.create(c.arena, .{
868 .name_token = name_tok,684 .is_pub = is_pub,
869 .mut_token = mut_tok,685 .is_const = is_const,
870 .semicolon_token = try appendToken(c, .Semicolon, ";"),686 .is_extern = is_extern,
871 }, .{687 .is_export = is_export,
872 .visib_token = visib_tok,688 .is_threadlocal = is_threadlocal,
873 .thread_local_token = thread_local_token,689 .linksection_string = linksection_string,
874 .eq_token = eq_tok,690 .alignment = alignment,
875 .extern_export_token = extern_tok,691 .name = checked_name,
876 .type_node = type_node,692 .type = type_node,
877 .align_node = align_expr,693 .init = init_node,
878 .section_node = linksection_expr,
879 .init_node = init_node,
880 });694 });
881 return addTopLevelDecl(c, checked_name, &node.base);695 return addTopLevelDecl(c, checked_name, node);
882}696}
883697
884fn transTypeDefAsBuiltin(c: *Context, typedef_decl: *const clang.TypedefNameDecl, builtin_name: []const u8) !*ast.Node {698const builtin_typedef_map = std.ComptimeStringMap([]const u8, .{
885 _ = try c.decl_table.put(c.gpa, @ptrToInt(typedef_decl.getCanonicalDecl()), builtin_name);699 .{ "uint8_t", "u8" },
886 return transCreateNodeIdentifier(c, builtin_name);700 .{ "int8_t", "i8" },
887}701 .{ "uint16_t", "u16" },
888702 .{ "int16_t", "i16" },
889fn checkForBuiltinTypedef(checked_name: []const u8) ?[]const u8 {703 .{ "uint32_t", "u32" },
890 const table = [_][2][]const u8{704 .{ "int32_t", "i32" },
891 .{ "uint8_t", "u8" },705 .{ "uint64_t", "u64" },
892 .{ "int8_t", "i8" },706 .{ "int64_t", "i64" },
893 .{ "uint16_t", "u16" },707 .{ "intptr_t", "isize" },
894 .{ "int16_t", "i16" },708 .{ "uintptr_t", "usize" },
895 .{ "uint32_t", "u32" },709 .{ "ssize_t", "isize" },
896 .{ "int32_t", "i32" },710 .{ "size_t", "usize" },
897 .{ "uint64_t", "u64" },711});
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}
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 {
915 if (c.decl_table.get(@ptrToInt(typedef_decl.getCanonicalDecl()))) |name|714 if (c.decl_table.get(@ptrToInt(typedef_decl.getCanonicalDecl()))) |name|
916 return transCreateNodeIdentifier(c, name); // Avoid processing this decl twice715 return; // Avoid processing this decl twice
917 const rp = makeRestorePoint(c);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
921 // TODO https://github.com/ziglang/zig/issues/3756721 // TODO https://github.com/ziglang/zig/issues/3756
922 // TODO https://github.com/ziglang/zig/issues/1802722 // 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;723 var name: []const u8 = if (isZigPrimitiveType(bare_name)) try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ bare_name, c.getMangle() }) else bare_name;
924 if (checkForBuiltinTypedef(checked_name)) |builtin| {724 if (builtin_typedef_map.get(name)) |builtin| {
925 return transTypeDefAsBuiltin(c, typedef_decl, builtin);725 return c.decl_table.putNoClobber(c.gpa, @ptrToInt(typedef_decl.getCanonicalDecl()), builtin);
926 }
927
928 if (!top_level_visit) {
929 return transCreateNodeIdentifier(c, checked_name);
930 }726 }
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, "=");
948 const child_qt = typedef_decl.getUnderlyingType();730 const child_qt = typedef_decl.getUnderlyingType();
949 const typedef_loc = typedef_decl.getLocation();731 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) {
951 error.UnsupportedType => {733 error.UnsupportedType => {
952 try failDecl(rp.c, typedef_loc, checked_name, "unable to resolve typedef child type", .{});734 return failDecl(c, typedef_loc, name, "unable to resolve typedef child type", .{});
953 return null;
954 },735 },
955 error.OutOfMemory => |e| return e,736 error.OutOfMemory => |e| return e,
956 };737 };
957 const semicolon_token = try appendToken(rp.c, .Semicolon, ";");738
958739 const payload = try c.arena.create(ast.Payload.SimpleVarDecl);
959 const node = try ast.Node.VarDecl.create(rp.c.arena, .{740 payload.* = .{
960 .name_token = name_tok,741 .base = .{ .tag = ([2]Tag{ .var_simple, .pub_var_simple })[@boolToInt(toplevel)] },
961 .mut_token = mut_tok,742 .data = .{
962 .semicolon_token = semicolon_token,743 .name = name,
963 }, .{744 .init = init_node,
964 .visib_token = visib_tok,745 },
965 .eq_token = eq_token,746 };
966 .init_node = init_node,747 const node = Node.initPayload(&payload.base);
967 });748
968 return &node.base;749 if (toplevel) {
750 try addTopLevelDecl(c, name, node);
751 } else {
752 try scope.appendNode(node);
753 }
969}754}
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 {
972 if (c.decl_table.get(@ptrToInt(record_decl.getCanonicalDecl()))) |name|757 if (c.decl_table.get(@ptrToInt(record_decl.getCanonicalDecl()))) |name|
973 return try transCreateNodeIdentifier(c, name); // Avoid processing this decl twice758 return; // Avoid processing this decl twice
974 const record_loc = record_decl.getLocation();759 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
976 var bare_name = try c.str(@ptrCast(*const clang.NamedDecl, record_decl).getName_bytes_begin());763 var bare_name = try c.str(@ptrCast(*const clang.NamedDecl, record_decl).getName_bytes_begin());
977 var is_unnamed = false;764 var is_unnamed = false;
...@@ -983,46 +770,31 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as...@@ -983,46 +770,31 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as
983 }770 }
984771
985 var container_kind_name: []const u8 = undefined;772 var container_kind_name: []const u8 = undefined;
986 var container_kind: std.zig.Token.Id = undefined;773 var is_union = false;
987 if (record_decl.isUnion()) {774 if (record_decl.isUnion()) {
988 container_kind_name = "union";775 container_kind_name = "union";
989 container_kind = .Keyword_union;776 is_union = true;
990 } else if (record_decl.isStruct()) {777 } else if (record_decl.isStruct()) {
991 container_kind_name = "struct";778 container_kind_name = "struct";
992 container_kind = .Keyword_struct;
993 } else {779 } else {
994 try emitWarning(c, record_loc, "record {s} is not a struct or union", .{bare_name});780 try c.decl_table.putNoClobber(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), bare_name);
995 return null;781 return failDecl(c, record_loc, bare_name, "record {s} is not a struct or union", .{bare_name});
996 }782 }
997783
998 const name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ container_kind_name, bare_name });784 var name: []const u8 = 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);785 if (!toplevel) name = try bs.makeMangledName(c, name);
1000786 try c.decl_table.putNoClobber(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), name);
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, "=");
1006787
1007 var semicolon: ast.TokenIndex = undefined;788 const is_pub = toplevel and !is_unnamed;
1008 const init_node = blk: {789 const init_node = blk: {
1009 const rp = makeRestorePoint(c);
1010 const record_def = record_decl.getDefinition() orelse {790 const record_def = record_decl.getDefinition() orelse {
1011 _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {});791 _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {});
1012 const opaque_type = try transCreateNodeOpaqueType(c);792 break :blk Tag.opaque_literal.init();
1013 semicolon = try appendToken(c, .Semicolon, ";");
1014 break :blk opaque_type;
1015 };793 };
1016794
1017 const layout_tok = try if (record_decl.getPackedAttribute())795 const is_packed = record_decl.getPackedAttribute();
1018 appendToken(c, .Keyword_packed, "packed")796 var fields = std.ArrayList(ast.Payload.Record.Field).init(c.gpa);
1019 else797 defer fields.deinit();
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();
1026798
1027 var unnamed_field_count: u32 = 0;799 var unnamed_field_count: u32 = 0;
1028 var it = record_def.field_begin();800 var it = record_def.field_begin();
...@@ -1034,111 +806,88 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as...@@ -1034,111 +806,88 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as
1034806
1035 if (field_decl.isBitField()) {807 if (field_decl.isBitField()) {
1036 _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {});808 _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {});
1037 const opaque_type = try transCreateNodeOpaqueType(c);809 try warn(c, scope, field_loc, "{s} demoted to opaque type - has bitfield", .{container_kind_name});
1038 semicolon = try appendToken(c, .Semicolon, ";");810 break :blk Tag.opaque_literal.init();
1039 try emitWarning(c, field_loc, "{s} demoted to opaque type - has bitfield", .{container_kind_name});
1040 break :blk opaque_type;
1041 }811 }
1042812
1043 if (qualTypeCanon(field_qt).isIncompleteOrZeroLengthArrayType(c.clang_context)) {813 if (qualTypeCanon(field_qt).isIncompleteOrZeroLengthArrayType(c.clang_context)) {
1044 _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {});814 _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {});
1045 const opaque_type = try transCreateNodeOpaqueType(c);815 try warn(c, scope, field_loc, "{s} demoted to opaque type - has variable length array", .{container_kind_name});
1046 semicolon = try appendToken(c, .Semicolon, ";");816 break :blk Tag.opaque_literal.init();
1047 try emitWarning(c, field_loc, "{s} demoted to opaque type - has variable length array", .{container_kind_name});
1048 break :blk opaque_type;
1049 }817 }
1050818
1051 var is_anon = false;819 var is_anon = false;
1052 var raw_name = try c.str(@ptrCast(*const clang.NamedDecl, field_decl).getName_bytes_begin());820 var field_name = try c.str(@ptrCast(*const clang.NamedDecl, field_decl).getName_bytes_begin());
1053 if (field_decl.isAnonymousStructOrUnion() or raw_name.len == 0) {821 if (field_decl.isAnonymousStructOrUnion() or field_name.len == 0) {
1054 // Context.getMangle() is not used here because doing so causes unpredictable field names for anonymous fields.822 // 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});
1056 unnamed_field_count += 1;824 unnamed_field_count += 1;
1057 is_anon = true;825 is_anon = true;
1058 }826 }
1059 const field_name = try appendIdentifier(c, raw_name);827 const field_type = transQualType(c, scope, field_qt, field_loc) catch |err| switch (err) {
1060 _ = try appendToken(c, .Colon, ":");
1061 const field_type = transQualType(rp, field_qt, field_loc) catch |err| switch (err) {
1062 error.UnsupportedType => {828 error.UnsupportedType => {
1063 _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {});829 _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {});
1064 const opaque_type = try transCreateNodeOpaqueType(c);830 try warn(c, scope, record_loc, "{s} demoted to opaque type - unable to translate type of field {s}", .{ container_kind_name, field_name });
1065 semicolon = try appendToken(c, .Semicolon, ";");831 break :blk Tag.opaque_literal.init();
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;
1068 },832 },
1069 else => |e| return e,833 else => |e| return e,
1070 };834 };
1071835
1072 const align_expr = blk_2: {836 const alignment = blk_2: {
1073 const alignment = field_decl.getAlignedAttribute(c.clang_context);837 const alignment = field_decl.getAlignedAttribute(c.clang_context);
1074 if (alignment != 0) {838 if (alignment != 0) {
1075 _ = try appendToken(c, .Keyword_align, "align");
1076 _ = try appendToken(c, .LParen, "(");
1077 // Clang reports the alignment in bits839 // Clang reports the alignment in bits
1078 const expr = try transCreateNodeInt(c, alignment / 8);840 break :blk_2 alignment / 8;
1079 _ = try appendToken(c, .RParen, ")");
1080
1081 break :blk_2 expr;
1082 }841 }
1083 break :blk_2 null;842 break :blk_2 null;
1084 };843 };
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
1096 if (is_anon) {845 if (is_anon) {
1097 _ = try c.decl_table.put(846 try c.decl_table.putNoClobber(c.gpa, @ptrToInt(field_decl.getCanonicalDecl()), field_name);
1098 c.gpa,
1099 @ptrToInt(field_decl.getCanonicalDecl()),
1100 raw_name,
1101 );
1102 }847 }
1103848
1104 try fields_and_decls.append(&field_node.base);849 try fields.append(.{
1105 _ = try appendToken(c, .Comma, ",");850 .name = field_name,
851 .type = field_type,
852 .alignment = alignment,
853 });
1106 }854 }
1107 const container_node = try ast.Node.ContainerDecl.alloc(c.arena, fields_and_decls.items.len);855
1108 container_node.* = .{856 const record_payload = try c.arena.create(ast.Payload.Record);
1109 .layout_token = layout_tok,857 record_payload.* = .{
1110 .kind_token = container_tok,858 .base = .{ .tag = ([2]Tag{ .@"struct", .@"union" })[@boolToInt(is_union)] },
1111 .init_arg_expr = .None,859 .data = .{
1112 .fields_and_decls_len = fields_and_decls.items.len,860 .is_packed = is_packed,
1113 .lbrace_token = lbrace_token,861 .fields = try c.arena.dupe(ast.Payload.Record.Field, fields.items),
1114 .rbrace_token = try appendToken(c, .RBrace, "}"),862 },
1115 };863 };
1116 mem.copy(*ast.Node, container_node.fieldsAndDecls(), fields_and_decls.items);864 break :blk Node.initPayload(&record_payload.base);
1117 semicolon = try appendToken(c, .Semicolon, ";");
1118 break :blk &container_node.base;
1119 };865 };
1120866
1121 const node = try ast.Node.VarDecl.create(c.arena, .{867 const payload = try c.arena.create(ast.Payload.SimpleVarDecl);
1122 .name_token = name_tok,868 payload.* = .{
1123 .mut_token = mut_tok,869 .base = .{ .tag = ([2]Tag{ .var_simple, .pub_var_simple })[@boolToInt(is_pub)] },
1124 .semicolon_token = semicolon,870 .data = .{
1125 }, .{871 .name = name,
1126 .visib_token = visib_tok,872 .init = init_node,
1127 .eq_token = eq_token,873 },
1128 .init_node = init_node,874 };
1129 });
1130875
1131 try addTopLevelDecl(c, name, &node.base);876 if (toplevel) {
1132 if (!is_unnamed)877 try addTopLevelDecl(c, name, Node.initPayload(&payload.base));
1133 try c.alias_list.append(.{ .alias = bare_name, .name = name });878 if (!is_unnamed)
1134 return transCreateNodeIdentifier(c, name);879 try c.alias_list.append(.{ .alias = bare_name, .name = name });
880 } else {
881 try scope.appendNode(Node.initPayload(&payload.base));
882 }
1135}883}
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 {
1138 if (c.decl_table.get(@ptrToInt(enum_decl.getCanonicalDecl()))) |name|886 if (c.decl_table.get(@ptrToInt(enum_decl.getCanonicalDecl()))) |name|
1139 return try transCreateNodeIdentifier(c, name); // Avoid processing this decl twice887 return; // Avoid processing this decl twice
1140 const rp = makeRestorePoint(c);
1141 const enum_loc = enum_decl.getLocation();888 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
1143 var bare_name = try c.str(@ptrCast(*const clang.NamedDecl, enum_decl).getName_bytes_begin());892 var bare_name = try c.str(@ptrCast(*const clang.NamedDecl, enum_decl).getName_bytes_begin());
1144 var is_unnamed = false;893 var is_unnamed = false;
...@@ -1147,13 +896,13 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node...@@ -1147,13 +896,13 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node
1147 is_unnamed = true;896 is_unnamed = true;
1148 }897 }
1149898
1150 const name = try std.fmt.allocPrint(c.arena, "enum_{s}", .{bare_name});899 var name: []const u8 = try std.fmt.allocPrint(c.arena, "enum_{s}", .{bare_name});
1151 _ = try c.decl_table.put(c.gpa, @ptrToInt(enum_decl.getCanonicalDecl()), 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;903 const is_pub = toplevel and !is_unnamed;
1154 const mut_tok = try appendToken(c, .Keyword_const, "const");904 var redecls = std.ArrayList(Tag.enum_redecl.Data()).init(c.gpa);
1155 const name_tok = try appendIdentifier(c, name);905 defer redecls.deinit();
1156 const eq_token = try appendToken(c, .Equal, "=");
1157906
1158 const init_node = if (enum_decl.getDefinition()) |enum_def| blk: {907 const init_node = if (enum_decl.getDefinition()) |enum_def| blk: {
1159 var pure_enum = true;908 var pure_enum = true;
...@@ -1167,11 +916,8 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node...@@ -1167,11 +916,8 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node
1167 }916 }
1168 }917 }
1169918
1170 const extern_tok = try appendToken(c, .Keyword_extern, "extern");919 var fields = std.ArrayList(ast.Payload.Enum.Field).init(c.gpa);
1171 const container_tok = try appendToken(c, .Keyword_enum, "enum");920 defer fields.deinit();
1172
1173 var fields_and_decls = std.ArrayList(*ast.Node).init(c.gpa);
1174 defer fields_and_decls.deinit();
1175921
1176 const int_type = enum_decl.getIntegerType();922 const int_type = enum_decl.getIntegerType();
1177 // The underlying type may be null in case of forward-declared enum923 // 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...@@ -1179,30 +925,22 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node
1179 // default to the usual integer type used for all the enums.925 // default to the usual integer type used for all the enums.
1180926
1181 // default to c_int since msvc and gcc default to different types927 // default to c_int since msvc and gcc default to different types
1182 _ = try appendToken(c, .LParen, "(");928 const init_arg_expr = if (int_type.ptr != null and
1183 const init_arg_expr = ast.Node.ContainerDecl.InitArg{929 !isCBuiltinType(int_type, .UInt) and
1184 .Type = if (int_type.ptr != null and930 !isCBuiltinType(int_type, .Int))
1185 !isCBuiltinType(int_type, .UInt) and931 transQualType(c, scope, int_type, enum_loc) catch |err| switch (err) {
1186 !isCBuiltinType(int_type, .Int))932 error.UnsupportedType => {
1187 transQualType(rp, int_type, enum_loc) catch |err| switch (err) {933 return failDecl(c, enum_loc, name, "unable to translate enum tag type", .{});
1188 error.UnsupportedType => {934 },
1189 try failDecl(c, enum_loc, name, "unable to translate enum tag type", .{});935 else => |e| return e,
1190 return null;936 }
1191 },937 else
1192 else => |e| return e,938 try Tag.type.create(c.arena, "c_int");
1193 }
1194 else
1195 try transCreateNodeIdentifier(c, "c_int"),
1196 };
1197 _ = try appendToken(c, .RParen, ")");
1198
1199 const lbrace_token = try appendToken(c, .LBrace, "{");
1200939
1201 it = enum_def.enumerator_begin();940 it = enum_def.enumerator_begin();
1202 end_it = enum_def.enumerator_end();941 end_it = enum_def.enumerator_end();
1203 while (it.neq(end_it)) : (it = it.next()) {942 while (it.neq(end_it)) : (it = it.next()) {
1204 const enum_const = it.deref();943 const enum_const = it.deref();
1205
1206 const enum_val_name = try c.str(@ptrCast(*const clang.NamedDecl, enum_const).getName_bytes_begin());944 const enum_val_name = try c.str(@ptrCast(*const clang.NamedDecl, enum_const).getName_bytes_begin());
1207945
1208 const field_name = if (!is_unnamed and mem.startsWith(u8, enum_val_name, bare_name))946 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...@@ -1210,123 +948,62 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node
1210 else948 else
1211 enum_val_name;949 enum_val_name;
1212950
1213 const field_name_tok = try appendIdentifier(c, field_name);951 const int_node = if (!pure_enum)
1214952 try transCreateNodeAPInt(c, enum_const.getInitVal())
1215 const int_node = if (!pure_enum) blk_2: {953 else
1216 _ = try appendToken(c, .Colon, "=");
1217 break :blk_2 try transCreateNodeAPInt(c, enum_const.getInitVal());
1218 } else
1219 null;954 null;
1220955
1221 const field_node = try c.arena.create(ast.Node.ContainerField);956 try fields.append(.{
1222 field_node.* = .{957 .name = field_name,
1223 .doc_comments = null,958 .value = int_node,
1224 .comptime_token = null,959 });
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, ",");
1233960
1234 // In C each enum value is in the global namespace. So we put them there too.961 // In C each enum value is in the global namespace. So we put them there too.
1235 // At this point we can rely on the enum emitting successfully.962 // At this point we can rely on the enum emitting successfully.
1236 const tld_visib_tok = try appendToken(c, .Keyword_pub, "pub");963 try redecls.append(.{
1237 const tld_mut_tok = try appendToken(c, .Keyword_const, "const");964 .enum_val_name = enum_val_name,
1238 const tld_name_tok = try appendIdentifier(c, enum_val_name);965 .field_name = field_name,
1239 const tld_eq_token = try appendToken(c, .Equal, "=");966 .enum_name = name,
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,
1263 });967 });
1264 try addTopLevelDecl(c, field_name, &tld_node.base);
1265 }968 }
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);970 break :blk try Tag.@"enum".create(c.arena, .{
1278 _ = try appendToken(c, .Comma, ",");971 .int_type = init_arg_expr,
1279 const container_node = try ast.Node.ContainerDecl.alloc(c.arena, fields_and_decls.items.len);972 .fields = try c.arena.dupe(ast.Payload.Enum.Field, fields.items),
1280 container_node.* = .{973 });
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;
1290 } else blk: {974 } else blk: {
1291 _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(enum_decl.getCanonicalDecl()), {});975 _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(enum_decl.getCanonicalDecl()), {});
1292 break :blk try transCreateNodeOpaqueType(c);976 break :blk Tag.opaque_literal.init();
1293 };977 };
1294978
1295 const semicolon_token = try appendToken(c, .Semicolon, ";");979 const payload = try c.arena.create(ast.Payload.SimpleVarDecl);
1296 const node = try ast.Node.VarDecl.create(c.arena, .{980 payload.* = .{
1297 .name_token = name_tok,981 .base = .{ .tag = ([2]Tag{ .var_simple, .pub_var_simple })[@boolToInt(is_pub)] },
1298 .mut_token = mut_tok,982 .data = .{
1299 .semicolon_token = semicolon_token,983 .name = name,
1300 }, .{984 .init = init_node,
1301 .visib_token = visib_tok,985 },
1302 .eq_token = eq_token,986 };
1303 .init_node = init_node,
1304 });
1305987
1306 try addTopLevelDecl(c, name, &node.base);988 if (toplevel) {
1307 if (!is_unnamed)989 try addTopLevelDecl(c, name, Node.initPayload(&payload.base));
1308 try c.alias_list.append(.{ .alias = bare_name, .name = name });990 if (!is_unnamed)
1309 return transCreateNodeIdentifier(c, name);991 try c.alias_list.append(.{ .alias = bare_name, .name = name });
1310}992 } else {
1311993 try scope.appendNode(Node.initPayload(&payload.base));
1312fn createAlias(c: *Context, alias: anytype) !void {994 }
1313 const visib_tok = try appendToken(c, .Keyword_pub, "pub");995
1314 const mut_tok = try appendToken(c, .Keyword_const, "const");996 for (redecls.items) |redecl| {
1315 const name_tok = try appendIdentifier(c, alias.alias);997 if (toplevel) {
1316 const eq_token = try appendToken(c, .Equal, "=");998 try addTopLevelDecl(c, redecl.field_name, try Tag.pub_enum_redecl.create(c.arena, redecl));
1317 const init_node = try transCreateNodeIdentifier(c, alias.name);999 } else {
1318 const semicolon_token = try appendToken(c, .Semicolon, ";");1000 try scope.appendNode(try Tag.enum_redecl.create(c.arena, .{
13191001 .enum_val_name = try bs.makeMangledName(c, redecl.enum_val_name),
1320 const node = try ast.Node.VarDecl.create(c.arena, .{1002 .field_name = redecl.field_name,
1321 .name_token = name_tok,1003 .enum_name = redecl.enum_name,
1322 .mut_token = mut_tok,1004 }));
1323 .semicolon_token = semicolon_token,1005 }
1324 }, .{1006 }
1325 .visib_token = visib_tok,
1326 .eq_token = eq_token,
1327 .init_node = init_node,
1328 });
1329 return addTopLevelDecl(c, alias.alias, &node.base);
1330}1007}
13311008
1332const ResultUsed = enum {1009const ResultUsed = enum {
...@@ -1334,317 +1011,252 @@ const ResultUsed = enum {...@@ -1334,317 +1011,252 @@ const ResultUsed = enum {
1334 unused,1011 unused,
1335};1012};
13361013
1337const LRValue = enum {
1338 l_value,
1339 r_value,
1340};
1341
1342fn transStmt(1014fn transStmt(
1343 rp: RestorePoint,1015 c: *Context,
1344 scope: *Scope,1016 scope: *Scope,
1345 stmt: *const clang.Stmt,1017 stmt: *const clang.Stmt,
1346 result_used: ResultUsed,1018 result_used: ResultUsed,
1347 lrvalue: LRValue,1019) TransError!Node {
1348) TransError!*ast.Node {
1349 const sc = stmt.getStmtClass();1020 const sc = stmt.getStmtClass();
1350 switch (sc) {1021 switch (sc) {
1351 .BinaryOperatorClass => return transBinaryOperator(rp, scope, @ptrCast(*const clang.BinaryOperator, stmt), result_used),1022 .BinaryOperatorClass => return transBinaryOperator(c, scope, @ptrCast(*const clang.BinaryOperator, stmt), result_used),
1352 .CompoundStmtClass => return transCompoundStmt(rp, scope, @ptrCast(*const clang.CompoundStmt, stmt)),1023 .CompoundStmtClass => return transCompoundStmt(c, scope, @ptrCast(*const clang.CompoundStmt, stmt)),
1353 .CStyleCastExprClass => return transCStyleCastExprClass(rp, scope, @ptrCast(*const clang.CStyleCastExpr, stmt), result_used, lrvalue),1024 .CStyleCastExprClass => return transCStyleCastExprClass(c, scope, @ptrCast(*const clang.CStyleCastExpr, stmt), result_used),
1354 .DeclStmtClass => return transDeclStmt(rp, scope, @ptrCast(*const clang.DeclStmt, stmt)),1025 .DeclStmtClass => return transDeclStmt(c, scope, @ptrCast(*const clang.DeclStmt, stmt)),
1355 .DeclRefExprClass => return transDeclRefExpr(rp, scope, @ptrCast(*const clang.DeclRefExpr, stmt), lrvalue),1026 .DeclRefExprClass => return transDeclRefExpr(c, scope, @ptrCast(*const clang.DeclRefExpr, stmt)),
1356 .ImplicitCastExprClass => return transImplicitCastExpr(rp, scope, @ptrCast(*const clang.ImplicitCastExpr, stmt), result_used),1027 .ImplicitCastExprClass => return transImplicitCastExpr(c, scope, @ptrCast(*const clang.ImplicitCastExpr, stmt), result_used),
1357 .IntegerLiteralClass => return transIntegerLiteral(rp, scope, @ptrCast(*const clang.IntegerLiteral, stmt), result_used, .with_as),1028 .IntegerLiteralClass => return transIntegerLiteral(c, scope, @ptrCast(*const clang.IntegerLiteral, stmt), result_used, .with_as),
1358 .ReturnStmtClass => return transReturnStmt(rp, scope, @ptrCast(*const clang.ReturnStmt, stmt)),1029 .ReturnStmtClass => return transReturnStmt(c, scope, @ptrCast(*const clang.ReturnStmt, stmt)),
1359 .StringLiteralClass => return transStringLiteral(rp, scope, @ptrCast(*const clang.StringLiteral, stmt), result_used),1030 .StringLiteralClass => return transStringLiteral(c, scope, @ptrCast(*const clang.StringLiteral, stmt), result_used),
1360 .ParenExprClass => {1031 .ParenExprClass => {
1361 const expr = try transExpr(rp, scope, @ptrCast(*const clang.ParenExpr, stmt).getSubExpr(), .used, lrvalue);1032 const expr = try transExpr(c, scope, @ptrCast(*const clang.ParenExpr, stmt).getSubExpr(), .used);
1362 if (expr.tag == .GroupedExpression) return maybeSuppressResult(rp, scope, result_used, expr);1033 return maybeSuppressResult(c, scope, result_used, expr);
1363 const node = try rp.c.arena.create(ast.Node.GroupedExpression);1034 },
1364 node.* = .{1035 .InitListExprClass => return transInitListExpr(c, scope, @ptrCast(*const clang.InitListExpr, stmt), result_used),
1365 .lparen = try appendToken(rp.c, .LParen, "("),1036 .ImplicitValueInitExprClass => return transImplicitValueInitExpr(c, scope, @ptrCast(*const clang.Expr, stmt), result_used),
1366 .expr = expr,1037 .IfStmtClass => return transIfStmt(c, scope, @ptrCast(*const clang.IfStmt, stmt)),
1367 .rparen = try appendToken(rp.c, .RParen, ")"),1038 .WhileStmtClass => return transWhileLoop(c, scope, @ptrCast(*const clang.WhileStmt, stmt)),
1368 };1039 .DoStmtClass => return transDoWhileLoop(c, scope, @ptrCast(*const clang.DoStmt, stmt)),
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)),
1376 .NullStmtClass => {1040 .NullStmtClass => {
1377 const block = try rp.c.createBlock(0);1041 return Tag.empty_block.init();
1378 block.rbrace = try appendToken(rp.c, .RBrace, "}");1042 },
1379 return &block.base;1043 .ContinueStmtClass => return Tag.@"continue".init(),
1380 },1044 .BreakStmtClass => return Tag.@"break".init(),
1381 .ContinueStmtClass => return try transCreateNodeContinue(rp.c),1045 .ForStmtClass => return transForLoop(c, scope, @ptrCast(*const clang.ForStmt, stmt)),
1382 .BreakStmtClass => return transBreak(rp, scope),1046 .FloatingLiteralClass => return transFloatingLiteral(c, scope, @ptrCast(*const clang.FloatingLiteral, stmt), result_used),
1383 .ForStmtClass => return transForLoop(rp, scope, @ptrCast(*const clang.ForStmt, stmt)),
1384 .FloatingLiteralClass => return transFloatingLiteral(rp, scope, @ptrCast(*const clang.FloatingLiteral, stmt), result_used),
1385 .ConditionalOperatorClass => {1047 .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);
1387 },1049 },
1388 .BinaryConditionalOperatorClass => {1050 .BinaryConditionalOperatorClass => {
1389 return transBinaryConditionalOperator(rp, scope, @ptrCast(*const clang.BinaryConditionalOperator, stmt), result_used);1051 return transBinaryConditionalOperator(c, scope, @ptrCast(*const clang.BinaryConditionalOperator, stmt), result_used);
1390 },1052 },
1391 .SwitchStmtClass => return transSwitch(rp, scope, @ptrCast(*const clang.SwitchStmt, stmt)),1053 .SwitchStmtClass => return transSwitch(c, scope, @ptrCast(*const clang.SwitchStmt, stmt)),
1392 .CaseStmtClass => return transCase(rp, scope, @ptrCast(*const clang.CaseStmt, stmt)),1054 .CaseStmtClass, .DefaultStmtClass => {
1393 .DefaultStmtClass => return transDefault(rp, scope, @ptrCast(*const clang.DefaultStmt, stmt)),1055 return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "TODO complex switch", .{});
1394 .ConstantExprClass => return transConstantExpr(rp, scope, @ptrCast(*const clang.Expr, stmt), result_used),1056 },
1395 .PredefinedExprClass => return transPredefinedExpr(rp, scope, @ptrCast(*const clang.PredefinedExpr, stmt), result_used),1057 .ConstantExprClass => return transConstantExpr(c, scope, @ptrCast(*const clang.Expr, stmt), result_used),
1396 .CharacterLiteralClass => return transCharLiteral(rp, scope, @ptrCast(*const clang.CharacterLiteral, stmt), result_used, .with_as),1058 .PredefinedExprClass => return transPredefinedExpr(c, scope, @ptrCast(*const clang.PredefinedExpr, stmt), result_used),
1397 .StmtExprClass => return transStmtExpr(rp, scope, @ptrCast(*const clang.StmtExpr, stmt), result_used),1059 .CharacterLiteralClass => return transCharLiteral(c, scope, @ptrCast(*const clang.CharacterLiteral, stmt), result_used, .with_as),
1398 .MemberExprClass => return transMemberExpr(rp, scope, @ptrCast(*const clang.MemberExpr, stmt), result_used),1060 .StmtExprClass => return transStmtExpr(c, scope, @ptrCast(*const clang.StmtExpr, stmt), result_used),
1399 .ArraySubscriptExprClass => return transArrayAccess(rp, scope, @ptrCast(*const clang.ArraySubscriptExpr, stmt), result_used),1061 .MemberExprClass => return transMemberExpr(c, scope, @ptrCast(*const clang.MemberExpr, stmt), result_used),
1400 .CallExprClass => return transCallExpr(rp, scope, @ptrCast(*const clang.CallExpr, stmt), result_used),1062 .ArraySubscriptExprClass => return transArrayAccess(c, scope, @ptrCast(*const clang.ArraySubscriptExpr, stmt), result_used),
1401 .UnaryExprOrTypeTraitExprClass => return transUnaryExprOrTypeTraitExpr(rp, scope, @ptrCast(*const clang.UnaryExprOrTypeTraitExpr, stmt), result_used),1063 .CallExprClass => return transCallExpr(c, scope, @ptrCast(*const clang.CallExpr, stmt), result_used),
1402 .UnaryOperatorClass => return transUnaryOperator(rp, scope, @ptrCast(*const clang.UnaryOperator, stmt), result_used),1064 .UnaryExprOrTypeTraitExprClass => return transUnaryExprOrTypeTraitExpr(c, scope, @ptrCast(*const clang.UnaryExprOrTypeTraitExpr, stmt), result_used),
1403 .CompoundAssignOperatorClass => return transCompoundAssignOperator(rp, scope, @ptrCast(*const clang.CompoundAssignOperator, 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),
1404 .OpaqueValueExprClass => {1067 .OpaqueValueExprClass => {
1405 const source_expr = @ptrCast(*const clang.OpaqueValueExpr, stmt).getSourceExpr().?;1068 const source_expr = @ptrCast(*const clang.OpaqueValueExpr, stmt).getSourceExpr().?;
1406 const expr = try transExpr(rp, scope, source_expr, .used, lrvalue);1069 const expr = try transExpr(c, scope, source_expr, .used);
1407 if (expr.tag == .GroupedExpression) return maybeSuppressResult(rp, scope, result_used, expr);1070 return maybeSuppressResult(c, 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);
1415 },1071 },
1416 else => {1072 else => {
1417 return revertAndWarn(1073 return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "TODO implement translation of stmt class {s}", .{@tagName(sc)});
1418 rp,
1419 error.UnsupportedTranslation,
1420 stmt.getBeginLoc(),
1421 "TODO implement translation of stmt class {s}",
1422 .{@tagName(sc)},
1423 );
1424 },1074 },
1425 }1075 }
1426}1076}
14271077
1428fn transBinaryOperator(1078fn transBinaryOperator(
1429 rp: RestorePoint,1079 c: *Context,
1430 scope: *Scope,1080 scope: *Scope,
1431 stmt: *const clang.BinaryOperator,1081 stmt: *const clang.BinaryOperator,
1432 result_used: ResultUsed,1082 result_used: ResultUsed,
1433) TransError!*ast.Node {1083) TransError!Node {
1434 const op = stmt.getOpcode();1084 const op = stmt.getOpcode();
1435 const qt = stmt.getType();1085 const qt = stmt.getType();
1436 var op_token: ast.TokenIndex = undefined;
1437 var op_id: ast.Node.Tag = undefined;
1438 switch (op) {1086 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()),
1440 .Comma => {1088 .Comma => {
1441 var block_scope = try Scope.Block.init(rp.c, scope, true);1089 var block_scope = try Scope.Block.init(c, scope, true);
1442 const lparen = try appendToken(rp.c, .LParen, "(");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);
1445 try block_scope.statements.append(lhs);1093 try block_scope.statements.append(lhs);
14461094
1447 const rhs = try transExpr(rp, &block_scope.base, stmt.getRHS(), .used, .r_value);1095 const rhs = try transExpr(c, &block_scope.base, stmt.getRHS(), .used);
1448 _ = try appendToken(rp.c, .Semicolon, ";");1096 const break_node = try Tag.break_val.create(c.arena, .{
1449 const break_node = try transCreateNodeBreak(rp.c, block_scope.label, rhs);1097 .label = block_scope.label,
1450 try block_scope.statements.append(&break_node.base);1098 .val = rhs,
1451 const block_node = try block_scope.complete(rp.c);1099 });
1452 const rparen = try appendToken(rp.c, .RParen, ")");1100 try block_scope.statements.append(break_node);
1453 const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);1101 const block_node = try block_scope.complete(c);
1454 grouped_expr.* = .{1102 return maybeSuppressResult(c, scope, result_used, block_node);
1455 .lparen = lparen,
1456 .expr = block_node,
1457 .rparen = rparen,
1458 };
1459 return maybeSuppressResult(rp, scope, result_used, &grouped_expr.base);
1460 },1103 },
1461 .Div => {1104 .Div => {
1462 if (cIsSignedInteger(qt)) {1105 if (cIsSignedInteger(qt)) {
1463 // signed integer division uses @divTrunc1106 // signed integer division uses @divTrunc
1464 const div_trunc_node = try rp.c.createBuiltinCall("@divTrunc", 2);1107 const lhs = try transExpr(c, scope, stmt.getLHS(), .used);
1465 div_trunc_node.params()[0] = try transExpr(rp, scope, stmt.getLHS(), .used, .l_value);1108 const rhs = try transExpr(c, scope, stmt.getRHS(), .used);
1466 _ = try appendToken(rp.c, .Comma, ",");1109 const div_trunc = try Tag.div_trunc.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
1467 const rhs = try transExpr(rp, scope, stmt.getRHS(), .used, .r_value);1110 return maybeSuppressResult(c, scope, result_used, div_trunc);
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);
1471 }1111 }
1472 },1112 },
1473 .Rem => {1113 .Rem => {
1474 if (cIsSignedInteger(qt)) {1114 if (cIsSignedInteger(qt)) {
1475 // signed integer division uses @rem1115 // signed integer division uses @rem
1476 const rem_node = try rp.c.createBuiltinCall("@rem", 2);1116 const lhs = try transExpr(c, scope, stmt.getLHS(), .used);
1477 rem_node.params()[0] = try transExpr(rp, scope, stmt.getLHS(), .used, .l_value);1117 const rhs = try transExpr(c, scope, stmt.getRHS(), .used);
1478 _ = try appendToken(rp.c, .Comma, ",");1118 const rem = try Tag.rem.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
1479 const rhs = try transExpr(rp, scope, stmt.getRHS(), .used, .r_value);1119 return maybeSuppressResult(c, scope, result_used, rem);
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);
1483 }1120 }
1484 },1121 },
1485 .Shl => {1122 .Shl => {
1486 const node = try transCreateNodeShiftOp(rp, scope, stmt, .BitShiftLeft, .AngleBracketAngleBracketLeft, "<<");1123 return transCreateNodeShiftOp(c, scope, stmt, .shl, result_used);
1487 return maybeSuppressResult(rp, scope, result_used, node);
1488 },1124 },
1489 .Shr => {1125 .Shr => {
1490 const node = try transCreateNodeShiftOp(rp, scope, stmt, .BitShiftRight, .AngleBracketAngleBracketRight, ">>");1126 return transCreateNodeShiftOp(c, scope, stmt, .shr, result_used);
1491 return maybeSuppressResult(rp, scope, result_used, node);
1492 },1127 },
1493 .LAnd => {1128 .LAnd => {
1494 const node = try transCreateNodeBoolInfixOp(rp, scope, stmt, .BoolAnd, result_used, true);1129 return transCreateNodeBoolInfixOp(c, scope, stmt, .@"and", result_used);
1495 return maybeSuppressResult(rp, scope, result_used, node);
1496 },1130 },
1497 .LOr => {1131 .LOr => {
1498 const node = try transCreateNodeBoolInfixOp(rp, scope, stmt, .BoolOr, result_used, true);1132 return transCreateNodeBoolInfixOp(c, scope, stmt, .@"or", result_used);
1499 return maybeSuppressResult(rp, scope, result_used, node);
1500 },1133 },
1501 else => {},1134 else => {},
1502 }1135 }
1503 const lhs_node = try transExpr(rp, scope, stmt.getLHS(), .used, .l_value);1136 var op_id: Tag = undefined;
1504 switch (op) {1137 switch (op) {
1505 .Add => {1138 .Add => {
1506 if (cIsUnsignedInteger(qt)) {1139 if (cIsUnsignedInteger(qt)) {
1507 op_token = try appendToken(rp.c, .PlusPercent, "+%");1140 op_id = .add_wrap;
1508 op_id = .AddWrap;
1509 } else {1141 } else {
1510 op_token = try appendToken(rp.c, .Plus, "+");1142 op_id = .add;
1511 op_id = .Add;
1512 }1143 }
1513 },1144 },
1514 .Sub => {1145 .Sub => {
1515 if (cIsUnsignedInteger(qt)) {1146 if (cIsUnsignedInteger(qt)) {
1516 op_token = try appendToken(rp.c, .MinusPercent, "-%");1147 op_id = .sub_wrap;
1517 op_id = .SubWrap;
1518 } else {1148 } else {
1519 op_token = try appendToken(rp.c, .Minus, "-");1149 op_id = .sub;
1520 op_id = .Sub;
1521 }1150 }
1522 },1151 },
1523 .Mul => {1152 .Mul => {
1524 if (cIsUnsignedInteger(qt)) {1153 if (cIsUnsignedInteger(qt)) {
1525 op_token = try appendToken(rp.c, .AsteriskPercent, "*%");1154 op_id = .mul_wrap;
1526 op_id = .MulWrap;
1527 } else {1155 } else {
1528 op_token = try appendToken(rp.c, .Asterisk, "*");1156 op_id = .mul;
1529 op_id = .Mul;
1530 }1157 }
1531 },1158 },
1532 .Div => {1159 .Div => {
1533 // unsigned/float division uses the operator1160 // unsigned/float division uses the operator
1534 op_id = .Div;1161 op_id = .div;
1535 op_token = try appendToken(rp.c, .Slash, "/");
1536 },1162 },
1537 .Rem => {1163 .Rem => {
1538 // unsigned/float division uses the operator1164 // unsigned/float division uses the operator
1539 op_id = .Mod;1165 op_id = .mod;
1540 op_token = try appendToken(rp.c, .Percent, "%");
1541 },1166 },
1542 .LT => {1167 .LT => {
1543 op_id = .LessThan;1168 op_id = .less_than;
1544 op_token = try appendToken(rp.c, .AngleBracketLeft, "<");
1545 },1169 },
1546 .GT => {1170 .GT => {
1547 op_id = .GreaterThan;1171 op_id = .greater_than;
1548 op_token = try appendToken(rp.c, .AngleBracketRight, ">");
1549 },1172 },
1550 .LE => {1173 .LE => {
1551 op_id = .LessOrEqual;1174 op_id = .less_than_equal;
1552 op_token = try appendToken(rp.c, .AngleBracketLeftEqual, "<=");
1553 },1175 },
1554 .GE => {1176 .GE => {
1555 op_id = .GreaterOrEqual;1177 op_id = .greater_than_equal;
1556 op_token = try appendToken(rp.c, .AngleBracketRightEqual, ">=");
1557 },1178 },
1558 .EQ => {1179 .EQ => {
1559 op_id = .EqualEqual;1180 op_id = .equal;
1560 op_token = try appendToken(rp.c, .EqualEqual, "==");
1561 },1181 },
1562 .NE => {1182 .NE => {
1563 op_id = .BangEqual;1183 op_id = .not_equal;
1564 op_token = try appendToken(rp.c, .BangEqual, "!=");
1565 },1184 },
1566 .And => {1185 .And => {
1567 op_id = .BitAnd;1186 op_id = .bit_and;
1568 op_token = try appendToken(rp.c, .Ampersand, "&");
1569 },1187 },
1570 .Xor => {1188 .Xor => {
1571 op_id = .BitXor;1189 op_id = .bit_xor;
1572 op_token = try appendToken(rp.c, .Caret, "^");
1573 },1190 },
1574 .Or => {1191 .Or => {
1575 op_id = .BitOr;1192 op_id = .bit_or;
1576 op_token = try appendToken(rp.c, .Pipe, "|");
1577 },1193 },
1578 else => unreachable,1194 else => unreachable,
1579 }1195 }
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: {1200 const lhs = if (isBoolRes(lhs_uncasted))
1584 const cast_node = try rp.c.createBuiltinCall("@boolToInt", 1);1201 try Tag.bool_to_int.create(c.arena, lhs_uncasted)
1585 cast_node.params()[0] = lhs_node;1202 else
1586 cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");1203 lhs_uncasted;
1587 break :init &cast_node.base;
1588 } else lhs_node;
15891204
1590 const rhs = if (isBoolRes(rhs_node)) init: {1205 const rhs = if (isBoolRes(rhs_uncasted))
1591 const cast_node = try rp.c.createBuiltinCall("@boolToInt", 1);1206 try Tag.bool_to_int.create(c.arena, rhs_uncasted)
1592 cast_node.params()[0] = rhs_node;1207 else
1593 cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");1208 rhs_uncasted;
1594 break :init &cast_node.base;
1595 } else rhs_node;
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);
1598}1211}
15991212
1600fn transCompoundStmtInline(1213fn transCompoundStmtInline(
1601 rp: RestorePoint,1214 c: *Context,
1602 parent_scope: *Scope,
1603 stmt: *const clang.CompoundStmt,1215 stmt: *const clang.CompoundStmt,
1604 block: *Scope.Block,1216 block: *Scope.Block,
1605) TransError!void {1217) TransError!void {
1606 var it = stmt.body_begin();1218 var it = stmt.body_begin();
1607 const end_it = stmt.body_end();1219 const end_it = stmt.body_end();
1608 while (it != end_it) : (it += 1) {1220 while (it != end_it) : (it += 1) {
1609 const result = try transStmt(rp, parent_scope, it[0], .unused, .r_value);1221 const result = try transStmt(c, &block.base, it[0], .unused);
1610 try block.statements.append(result);1222 switch (result.tag()) {
1223 .declaration, .empty_block => {},
1224 else => try block.statements.append(result),
1225 }
1611 }1226 }
1612}1227}
16131228
1614fn transCompoundStmt(rp: RestorePoint, scope: *Scope, stmt: *const clang.CompoundStmt) TransError!*ast.Node {1229fn transCompoundStmt(c: *Context, scope: *Scope, stmt: *const clang.CompoundStmt) TransError!Node {
1615 var block_scope = try Scope.Block.init(rp.c, scope, false);1230 var block_scope = try Scope.Block.init(c, scope, false);
1616 defer block_scope.deinit();1231 defer block_scope.deinit();
1617 try transCompoundStmtInline(rp, &block_scope.base, stmt, &block_scope);1232 try transCompoundStmtInline(c, stmt, &block_scope);
1618 return try block_scope.complete(rp.c);1233 return try block_scope.complete(c);
1619}1234}
16201235
1621fn transCStyleCastExprClass(1236fn transCStyleCastExprClass(
1622 rp: RestorePoint,1237 c: *Context,
1623 scope: *Scope,1238 scope: *Scope,
1624 stmt: *const clang.CStyleCastExpr,1239 stmt: *const clang.CStyleCastExpr,
1625 result_used: ResultUsed,1240 result_used: ResultUsed,
1626 lrvalue: LRValue,1241) TransError!Node {
1627) TransError!*ast.Node {
1628 const sub_expr = stmt.getSubExpr();1242 const sub_expr = stmt.getSubExpr();
1629 const cast_node = (try transCCast(1243 const cast_node = (try transCCast(
1630 rp,1244 c,
1631 scope,1245 scope,
1632 stmt.getBeginLoc(),1246 stmt.getBeginLoc(),
1633 stmt.getType(),1247 stmt.getType(),
1634 sub_expr.getType(),1248 sub_expr.getType(),
1635 try transExpr(rp, scope, sub_expr, .used, lrvalue),1249 try transExpr(c, scope, sub_expr, .used),
1636 ));1250 ));
1637 return maybeSuppressResult(rp, scope, result_used, cast_node);1251 return maybeSuppressResult(c, scope, result_used, cast_node);
1638}1252}
16391253
1640fn transDeclStmtOne(1254fn transDeclStmtOne(
1641 rp: RestorePoint,1255 c: *Context,
1642 scope: *Scope,1256 scope: *Scope,
1643 decl: *const clang.Decl,1257 decl: *const clang.Decl,
1644 block_scope: *Scope.Block,1258 block_scope: *Scope.Block,
1645) TransError!*ast.Node {1259) TransError!void {
1646 const c = rp.c;
1647
1648 switch (decl.getKind()) {1260 switch (decl.getKind()) {
1649 .Var => {1261 .Var => {
1650 const var_decl = @ptrCast(*const clang.VarDecl, decl);1262 const var_decl = @ptrCast(*const clang.VarDecl, decl);
...@@ -1658,62 +1270,51 @@ fn transDeclStmtOne(...@@ -1658,62 +1270,51 @@ fn transDeclStmtOne(
1658 .Extern, .Static => {1270 .Extern, .Static => {
1659 // This is actually a global variable, put it in the global scope and reference it.1271 // This is actually a global variable, put it in the global scope and reference it.
1660 // `_ = mangled_name;`1272 // `_ = mangled_name;`
1661 try visitVarDecl(rp.c, var_decl, mangled_name);1273 return visitVarDecl(c, var_decl, mangled_name);
1662 return try maybeSuppressResult(rp, scope, .unused, try transCreateNodeIdentifier(rp.c, mangled_name));
1663 },1274 },
1664 else => {},1275 else => {},
1665 }1276 }
16661277
1667 const mut_tok = if (qual_type.isConstQualified())1278 const is_const = 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);
16721279
1673 _ = try appendToken(c, .Colon, ":");
1674 const loc = decl.getLocation();1280 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, "=");
1678 var init_node = if (decl_init) |expr|1283 var init_node = if (decl_init) |expr|
1679 if (expr.getStmtClass() == .StringLiteralClass)1284 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))
1681 else1286 else
1682 try transExprCoercing(rp, scope, expr, .used, .r_value)1287 try transExprCoercing(c, scope, expr, .used)
1683 else1288 else
1684 try transCreateNodeUndefinedLiteral(c);1289 Tag.undefined_literal.init();
1685 if (!qualTypeIsBoolean(qual_type) and isBoolRes(init_node)) {1290 if (!qualTypeIsBoolean(qual_type) and isBoolRes(init_node)) {
1686 const builtin_node = try rp.c.createBuiltinCall("@boolToInt", 1);1291 init_node = try Tag.bool_to_int.create(c.arena, init_node);
1687 builtin_node.params()[0] = init_node;
1688 builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
1689 init_node = &builtin_node.base;
1690 }1292 }
1691 const semicolon_token = try appendToken(c, .Semicolon, ";");1293 const node = try Tag.var_decl.create(c.arena, .{
1692 const node = try ast.Node.VarDecl.create(c.arena, .{1294 .is_pub = false,
1693 .name_token = name_tok,1295 .is_const = is_const,
1694 .mut_token = mut_tok,1296 .is_extern = false,
1695 .semicolon_token = semicolon_token,1297 .is_export = false,
1696 }, .{1298 .is_threadlocal = false,
1697 .eq_token = eq_token,1299 .linksection_string = null,
1698 .type_node = type_node,1300 .alignment = null,
1699 .init_node = init_node,1301 .name = mangled_name,
1302 .type = type_node,
1303 .init = init_node,
1700 });1304 });
1701 return &node.base;1305 try block_scope.statements.append(node);
1702 },1306 },
1703 .Typedef => {1307 .Typedef => {
1704 const typedef_decl = @ptrCast(*const clang.TypedefNameDecl, decl);1308 try transTypeDef(c, scope, @ptrCast(*const clang.TypedefNameDecl, decl));
1705 const name = try c.str(@ptrCast(*const clang.NamedDecl, typedef_decl).getName_bytes_begin());1309 },
17061310 .Record => {
1707 const underlying_qual = typedef_decl.getUnderlyingType();1311 try transRecordDecl(c, scope, @ptrCast(*const clang.RecordDecl, decl));
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;
1714 },1312 },
1715 else => |kind| return revertAndWarn(1313 .Enum => {
1716 rp,1314 try transEnumDecl(c, scope, @ptrCast(*const clang.EnumDecl, decl));
1315 },
1316 else => |kind| return fail(
1317 c,
1717 error.UnsupportedTranslation,1318 error.UnsupportedTranslation,
1718 decl.getLocation(),1319 decl.getLocation(),
1719 "TODO implement translation of DeclStmt kind {s}",1320 "TODO implement translation of DeclStmt kind {s}",
...@@ -1722,96 +1323,86 @@ fn transDeclStmtOne(...@@ -1722,96 +1323,86 @@ fn transDeclStmtOne(
1722 }1323 }
1723}1324}
17241325
1725fn transDeclStmt(rp: RestorePoint, scope: *Scope, stmt: *const clang.DeclStmt) TransError!*ast.Node {1326fn transDeclStmt(c: *Context, scope: *Scope, stmt: *const clang.DeclStmt) TransError!Node {
1726 const block_scope = scope.findBlockScope(rp.c) catch unreachable;1327 const block_scope = try scope.findBlockScope(c);
17271328
1728 var it = stmt.decl_begin();1329 var it = stmt.decl_begin();
1729 const end_it = stmt.decl_end();1330 const end_it = stmt.decl_end();
1730 assert(it != end_it);1331 while (it != end_it) : (it += 1) {
1731 while (true) : (it += 1) {1332 try transDeclStmtOne(c, scope, it[0], block_scope);
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 }
1739 }1333 }
1740 unreachable;1334 return Tag.declaration.init();
1741}1335}
17421336
1743fn transDeclRefExpr(1337fn transDeclRefExpr(
1744 rp: RestorePoint,1338 c: *Context,
1745 scope: *Scope,1339 scope: *Scope,
1746 expr: *const clang.DeclRefExpr,1340 expr: *const clang.DeclRefExpr,
1747 lrvalue: LRValue,1341) TransError!Node {
1748) TransError!*ast.Node {
1749 const value_decl = expr.getDecl();1342 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());
1751 const mangled_name = scope.getAlias(name);1344 const mangled_name = scope.getAlias(name);
1752 return transCreateNodeIdentifier(rp.c, mangled_name);1345 return Tag.identifier.create(c.arena, mangled_name);
1753}1346}
17541347
1755fn transImplicitCastExpr(1348fn transImplicitCastExpr(
1756 rp: RestorePoint,1349 c: *Context,
1757 scope: *Scope,1350 scope: *Scope,
1758 expr: *const clang.ImplicitCastExpr,1351 expr: *const clang.ImplicitCastExpr,
1759 result_used: ResultUsed,1352 result_used: ResultUsed,
1760) TransError!*ast.Node {1353) TransError!Node {
1761 const c = rp.c;
1762 const sub_expr = expr.getSubExpr();1354 const sub_expr = expr.getSubExpr();
1763 const dest_type = getExprQualType(c, @ptrCast(*const clang.Expr, expr));1355 const dest_type = getExprQualType(c, @ptrCast(*const clang.Expr, expr));
1764 const src_type = getExprQualType(c, sub_expr);1356 const src_type = getExprQualType(c, sub_expr);
1765 switch (expr.getCastKind()) {1357 switch (expr.getCastKind()) {
1766 .BitCast, .FloatingCast, .FloatingToIntegral, .IntegralToFloating, .IntegralCast, .PointerToIntegral, .IntegralToPointer => {1358 .BitCast, .FloatingCast, .FloatingToIntegral, .IntegralToFloating, .IntegralCast, .PointerToIntegral, .IntegralToPointer => {
1767 const sub_expr_node = try transExpr(rp, scope, sub_expr, .used, .r_value);1359 const sub_expr_node = try transExpr(c, scope, sub_expr, .used);
1768 return try transCCast(rp, scope, expr.getBeginLoc(), dest_type, src_type, sub_expr_node);1360 const casted = try transCCast(c, scope, expr.getBeginLoc(), dest_type, src_type, sub_expr_node);
1361 return maybeSuppressResult(c, scope, result_used, casted);
1769 },1362 },
1770 .LValueToRValue, .NoOp, .FunctionToPointerDecay => {1363 .LValueToRValue, .NoOp, .FunctionToPointerDecay => {
1771 const sub_expr_node = try transExpr(rp, scope, sub_expr, .used, .r_value);1364 const sub_expr_node = try transExpr(c, scope, sub_expr, .used);
1772 return maybeSuppressResult(rp, scope, result_used, sub_expr_node);1365 return maybeSuppressResult(c, scope, result_used, sub_expr_node);
1773 },1366 },
1774 .ArrayToPointerDecay => {1367 .ArrayToPointerDecay => {
1775 if (exprIsNarrowStringLiteral(sub_expr)) {1368 if (exprIsNarrowStringLiteral(sub_expr)) {
1776 const sub_expr_node = try transExpr(rp, scope, sub_expr, .used, .r_value);1369 const sub_expr_node = try transExpr(c, scope, sub_expr, .used);
1777 return maybeSuppressResult(rp, scope, result_used, sub_expr_node);1370 return maybeSuppressResult(c, scope, result_used, sub_expr_node);
1778 }1371 }
17791372
1780 const prefix_op = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&");1373 const addr = try Tag.address_of.create(c.arena, try transExpr(c, scope, sub_expr, .used));
1781 prefix_op.rhs = try transExpr(rp, scope, sub_expr, .used, .r_value);1374 return maybeSuppressResult(c, scope, result_used, addr);
1782
1783 return maybeSuppressResult(rp, scope, result_used, &prefix_op.base);
1784 },1375 },
1785 .NullToPointer => {1376 .NullToPointer => {
1786 return try transCreateNodeNullLiteral(rp.c);1377 return Tag.null_literal.init();
1787 },1378 },
1788 .PointerToBoolean => {1379 .PointerToBoolean => {
1789 // @ptrToInt(val) != 01380 // @ptrToInt(val) != 0
1790 const ptr_to_int = try rp.c.createBuiltinCall("@ptrToInt", 1);1381 const ptr_to_int = try Tag.ptr_to_int.create(c.arena, try transExpr(c, scope, sub_expr, .used));
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, ")");
17931382
1794 const op_token = try appendToken(rp.c, .BangEqual, "!=");1383 const ne = try Tag.not_equal.create(c.arena, .{ .lhs = ptr_to_int, .rhs = Tag.zero_literal.init() });
1795 const rhs_node = try transCreateNodeInt(rp.c, 0);1384 return maybeSuppressResult(c, scope, result_used, ne);
1796 return transCreateNodeInfixOp(rp, scope, &ptr_to_int.base, .BangEqual, op_token, rhs_node, result_used, false);
1797 },1385 },
1798 .IntegralToBoolean => {1386 .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
1801 // The expression is already a boolean one, return it as-is1389 // The expression is already a boolean one, return it as-is
1802 if (isBoolRes(sub_expr_node))1390 if (isBoolRes(sub_expr_node))
1803 return sub_expr_node;1391 return maybeSuppressResult(c, scope, result_used, sub_expr_node);
18041392
1805 // val != 01393 // val != 0
1806 const op_token = try appendToken(rp.c, .BangEqual, "!=");1394 const ne = try Tag.not_equal.create(c.arena, .{ .lhs = sub_expr_node, .rhs = Tag.zero_literal.init() });
1807 const rhs_node = try transCreateNodeInt(rp.c, 0);1395 return maybeSuppressResult(c, scope, result_used, ne);
1808 return transCreateNodeInfixOp(rp, scope, sub_expr_node, .BangEqual, op_token, rhs_node, result_used, false);
1809 },1396 },
1810 .BuiltinFnToFnPtr => {1397 .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);
1812 },1403 },
1813 else => |kind| return revertAndWarn(1404 else => |kind| return fail(
1814 rp,1405 c,
1815 error.UnsupportedTranslation,1406 error.UnsupportedTranslation,
1816 @ptrCast(*const clang.Stmt, expr).getBeginLoc(),1407 @ptrCast(*const clang.Stmt, expr).getBeginLoc(),
1817 "TODO implement translation of CastKind {s}",1408 "TODO implement translation of CastKind {s}",
...@@ -1821,52 +1412,28 @@ fn transImplicitCastExpr(...@@ -1821,52 +1412,28 @@ fn transImplicitCastExpr(
1821}1412}
18221413
1823fn transBoolExpr(1414fn transBoolExpr(
1824 rp: RestorePoint,1415 c: *Context,
1825 scope: *Scope,1416 scope: *Scope,
1826 expr: *const clang.Expr,1417 expr: *const clang.Expr,
1827 used: ResultUsed,1418 used: ResultUsed,
1828 lrvalue: LRValue,1419) TransError!Node {
1829 grouped: bool,
1830) TransError!*ast.Node {
1831 if (@ptrCast(*const clang.Stmt, expr).getStmtClass() == .IntegerLiteralClass) {1420 if (@ptrCast(*const clang.Stmt, expr).getStmtClass() == .IntegerLiteralClass) {
1832 var is_zero: bool = undefined;1421 var is_zero: bool = undefined;
1833 if (!(@ptrCast(*const clang.IntegerLiteral, expr).isZero(&is_zero, rp.c.clang_context))) {1422 if (!(@ptrCast(*const clang.IntegerLiteral, expr).isZero(&is_zero, c.clang_context))) {
1834 return revertAndWarn(rp, error.UnsupportedTranslation, expr.getBeginLoc(), "invalid integer literal", .{});1423 return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "invalid integer literal", .{});
1835 }1424 }
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)]) };
1837 }1426 }
18381427
1839 const lparen = if (grouped)1428 var res = try transExpr(c, scope, expr, used);
1840 try appendToken(rp.c, .LParen, "(")
1841 else
1842 undefined;
1843 var res = try transExpr(rp, scope, expr, used, lrvalue);
1844
1845 if (isBoolRes(res)) {1429 if (isBoolRes(res)) {
1846 if (!grouped and res.tag == .GroupedExpression) {1430 return maybeSuppressResult(c, scope, used, res);
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;
1853 }1431 }
18541432
1855 const ty = getExprQualType(rp.c, expr).getTypePtr();1433 const ty = getExprQualType(c, expr).getTypePtr();
1856 const node = try finishBoolExpr(rp, scope, expr.getBeginLoc(), ty, res, used);1434 const node = try finishBoolExpr(c, scope, expr.getBeginLoc(), ty, res, used);
18571435
1858 if (grouped) {1436 return maybeSuppressResult(c, scope, used, node);
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 }
1870}1437}
18711438
1872fn exprIsBooleanType(expr: *const clang.Expr) bool {1439fn exprIsBooleanType(expr: *const clang.Expr) bool {
...@@ -1892,34 +1459,32 @@ fn exprIsNarrowStringLiteral(expr: *const clang.Expr) bool {...@@ -1892,34 +1459,32 @@ fn exprIsNarrowStringLiteral(expr: *const clang.Expr) bool {
1892 }1459 }
1893}1460}
18941461
1895fn isBoolRes(res: *ast.Node) bool {1462fn isBoolRes(res: Node) bool {
1896 switch (res.tag) {1463 switch (res.tag()) {
1897 .BoolOr,1464 .@"or",
1898 .BoolAnd,1465 .@"and",
1899 .EqualEqual,1466 .equal,
1900 .BangEqual,1467 .not_equal,
1901 .LessThan,1468 .less_than,
1902 .GreaterThan,1469 .less_than_equal,
1903 .LessOrEqual,1470 .greater_than,
1904 .GreaterOrEqual,1471 .greater_than_equal,
1905 .BoolNot,1472 .not,
1906 .BoolLiteral,1473 .false_literal,
1474 .true_literal,
1907 => return true,1475 => return true,
1908
1909 .GroupedExpression => return isBoolRes(@fieldParentPtr(ast.Node.GroupedExpression, "base", res).expr),
1910
1911 else => return false,1476 else => return false,
1912 }1477 }
1913}1478}
19141479
1915fn finishBoolExpr(1480fn finishBoolExpr(
1916 rp: RestorePoint,1481 c: *Context,
1917 scope: *Scope,1482 scope: *Scope,
1918 loc: clang.SourceLocation,1483 loc: clang.SourceLocation,
1919 ty: *const clang.Type,1484 ty: *const clang.Type,
1920 node: *ast.Node,1485 node: Node,
1921 used: ResultUsed,1486 used: ResultUsed,
1922) TransError!*ast.Node {1487) TransError!Node {
1923 switch (ty.getTypeClass()) {1488 switch (ty.getTypeClass()) {
1924 .Builtin => {1489 .Builtin => {
1925 const builtin_ty = @ptrCast(*const clang.BuiltinType, ty);1490 const builtin_ty = @ptrCast(*const clang.BuiltinType, ty);
...@@ -1951,42 +1516,38 @@ fn finishBoolExpr(...@@ -1951,42 +1516,38 @@ fn finishBoolExpr(
1951 .WChar_S,1516 .WChar_S,
1952 .Float16,1517 .Float16,
1953 => {1518 => {
1954 const op_token = try appendToken(rp.c, .BangEqual, "!=");1519 // node != 0
1955 const rhs_node = try transCreateNodeInt(rp.c, 0);1520 return Tag.not_equal.create(c.arena, .{ .lhs = node, .rhs = Tag.zero_literal.init() });
1956 return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, used, false);
1957 },1521 },
1958 .NullPtr => {1522 .NullPtr => {
1959 const op_token = try appendToken(rp.c, .EqualEqual, "==");1523 // node == null
1960 const rhs_node = try transCreateNodeNullLiteral(rp.c);1524 return Tag.equal.create(c.arena, .{ .lhs = node, .rhs = Tag.null_literal.init() });
1961 return transCreateNodeInfixOp(rp, scope, node, .EqualEqual, op_token, rhs_node, used, false);
1962 },1525 },
1963 else => {},1526 else => {},
1964 }1527 }
1965 },1528 },
1966 .Pointer => {1529 .Pointer => {
1967 const op_token = try appendToken(rp.c, .BangEqual, "!=");1530 // node != null
1968 const rhs_node = try transCreateNodeNullLiteral(rp.c);1531 return Tag.not_equal.create(c.arena, .{ .lhs = node, .rhs = Tag.null_literal.init() });
1969 return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, used, false);
1970 },1532 },
1971 .Typedef => {1533 .Typedef => {
1972 const typedef_ty = @ptrCast(*const clang.TypedefType, ty);1534 const typedef_ty = @ptrCast(*const clang.TypedefType, ty);
1973 const typedef_decl = typedef_ty.getDecl();1535 const typedef_decl = typedef_ty.getDecl();
1974 const underlying_type = typedef_decl.getUnderlyingType();1536 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);
1976 },1538 },
1977 .Enum => {1539 .Enum => {
1978 const op_token = try appendToken(rp.c, .BangEqual, "!=");1540 // node != 0
1979 const rhs_node = try transCreateNodeInt(rp.c, 0);1541 return Tag.not_equal.create(c.arena, .{ .lhs = node, .rhs = Tag.zero_literal.init() });
1980 return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, used, false);
1981 },1542 },
1982 .Elaborated => {1543 .Elaborated => {
1983 const elaborated_ty = @ptrCast(*const clang.ElaboratedType, ty);1544 const elaborated_ty = @ptrCast(*const clang.ElaboratedType, ty);
1984 const named_type = elaborated_ty.getNamedType();1545 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);
1986 },1547 },
1987 else => {},1548 else => {},
1988 }1549 }
1989 return revertAndWarn(rp, error.UnsupportedType, loc, "unsupported bool expression type", .{});1550 return fail(c, error.UnsupportedType, loc, "unsupported bool expression type", .{});
1990}1551}
19911552
1992const SuppressCast = enum {1553const SuppressCast = enum {
...@@ -1994,21 +1555,21 @@ const SuppressCast = enum {...@@ -1994,21 +1555,21 @@ const SuppressCast = enum {
1994 no_as,1555 no_as,
1995};1556};
1996fn transIntegerLiteral(1557fn transIntegerLiteral(
1997 rp: RestorePoint,1558 c: *Context,
1998 scope: *Scope,1559 scope: *Scope,
1999 expr: *const clang.IntegerLiteral,1560 expr: *const clang.IntegerLiteral,
2000 result_used: ResultUsed,1561 result_used: ResultUsed,
2001 suppress_as: SuppressCast,1562 suppress_as: SuppressCast,
2002) TransError!*ast.Node {1563) TransError!Node {
2003 var eval_result: clang.ExprEvalResult = undefined;1564 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)) {
2005 const loc = expr.getBeginLoc();1566 const loc = expr.getBeginLoc();
2006 return revertAndWarn(rp, error.UnsupportedTranslation, loc, "invalid integer literal", .{});1567 return fail(c, error.UnsupportedTranslation, loc, "invalid integer literal", .{});
2007 }1568 }
20081569
2009 if (suppress_as == .no_as) {1570 if (suppress_as == .no_as) {
2010 const int_lit_node = try transCreateNodeAPInt(rp.c, eval_result.Val.getInt());1571 const int_lit_node = try transCreateNodeAPInt(c, eval_result.Val.getInt());
2011 return maybeSuppressResult(rp, scope, result_used, int_lit_node);1572 return maybeSuppressResult(c, scope, result_used, int_lit_node);
2012 }1573 }
20131574
2014 // Integer literals in C have types, and this can matter for several reasons.1575 // Integer literals in C have types, and this can matter for several reasons.
...@@ -2023,115 +1584,61 @@ fn transIntegerLiteral(...@@ -2023,115 +1584,61 @@ fn transIntegerLiteral(
20231584
2024 // @as(T, x)1585 // @as(T, x)
2025 const expr_base = @ptrCast(*const clang.Expr, expr);1586 const expr_base = @ptrCast(*const clang.Expr, expr);
2026 const as_node = try rp.c.createBuiltinCall("@as", 2);1587 const ty_node = try transQualType(c, scope, expr_base.getType(), expr_base.getBeginLoc());
2027 const ty_node = try transQualType(rp, expr_base.getType(), expr_base.getBeginLoc());1588 const rhs = try transCreateNodeAPInt(c, eval_result.Val.getInt());
2028 as_node.params()[0] = ty_node;1589 const as = try Tag.as.create(c.arena, .{ .lhs = ty_node, .rhs = rhs });
2029 _ = try appendToken(rp.c, .Comma, ",");1590 return maybeSuppressResult(c, scope, result_used, as);
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.?);
2042}1591}
20431592
2044fn transReturnStmt(1593fn transReturnStmt(
2045 rp: RestorePoint,1594 c: *Context,
2046 scope: *Scope,1595 scope: *Scope,
2047 expr: *const clang.ReturnStmt,1596 expr: *const clang.ReturnStmt,
2048) TransError!*ast.Node {1597) TransError!Node {
2049 const return_kw = try appendToken(rp.c, .Keyword_return, "return");1598 const val_expr = expr.getRetValue() orelse
2050 var rhs: ?*ast.Node = if (expr.getRetValue()) |val_expr|1599 return Tag.return_void.init();
2051 try transExprCoercing(rp, scope, val_expr, .used, .r_value)1600
2052 else1601 var rhs = try transExprCoercing(c, scope, val_expr, .used);
2053 null;1602 const return_qt = scope.findBlockReturnType(c);
2054 const return_qt = scope.findBlockReturnType(rp.c);1603 if (isBoolRes(rhs) and !qualTypeIsBoolean(return_qt)) {
2055 if (zigShouldCastBooleanReturnToInt(rhs, return_qt)) {1604 rhs = try Tag.bool_to_int.create(c.arena, rhs);
2056 const bool_to_int_node = try rp.c.createBuiltinCall("@boolToInt", 1);1605 }
2057 bool_to_int_node.params()[0] = rhs.?;1606 return Tag.@"return".create(c.arena, 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;
2070}1607}
20711608
2072fn transStringLiteral(1609fn transStringLiteral(
2073 rp: RestorePoint,1610 c: *Context,
2074 scope: *Scope,1611 scope: *Scope,
2075 stmt: *const clang.StringLiteral,1612 stmt: *const clang.StringLiteral,
2076 result_used: ResultUsed,1613 result_used: ResultUsed,
2077) TransError!*ast.Node {1614) TransError!Node {
2078 const kind = stmt.getKind();1615 const kind = stmt.getKind();
2079 switch (kind) {1616 switch (kind) {
2080 .Ascii, .UTF8 => {1617 .Ascii, .UTF8 => {
2081 var len: usize = undefined;1618 var len: usize = undefined;
2082 const bytes_ptr = stmt.getString_bytes_begin_size(&len);1619 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)});1621 const str = try std.fmt.allocPrint(c.arena, "\"{}\"", .{std.zig.fmtEscapes(bytes_ptr[0..len])});
2086 const node = try rp.c.arena.create(ast.Node.OneToken);1622 const node = try Tag.string_literal.create(c.arena, str);
2087 node.* = .{1623 return maybeSuppressResult(c, scope, result_used, node);
2088 .base = .{ .tag = .StringLiteral },
2089 .token = token,
2090 };
2091 return maybeSuppressResult(rp, scope, result_used, &node.base);
2092 },1624 },
2093 .UTF16, .UTF32, .Wide => {1625 .UTF16, .UTF32, .Wide => {
2094 const node = try transWideStringLiteral(rp, scope, stmt);1626 const str_type = @tagName(stmt.getKind());
2095 return maybeSuppressResult(rp, scope, result_used, node);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);
2096 },1634 },
2097 }1635 }
2098}1636}
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
2128/// Parse the size of an array back out from an ast Node.1638/// Parse the size of an array back out from an ast Node.
2129fn zigArraySize(c: *Context, node: *ast.Node) TransError!usize {1639fn zigArraySize(c: *Context, node: Node) TransError!usize {
2130 if (node.castTag(.ArrayType)) |array| {1640 if (node.castTag(.array_type)) |array| {
2131 if (array.len_expr.castTag(.IntegerLiteral)) |int_lit| {1641 return array.data.len;
2132 const tok = tokenSlice(c, int_lit.token);
2133 return std.fmt.parseUnsigned(usize, tok, 10) catch error.UnsupportedTranslation;
2134 }
2135 }1642 }
2136 return error.UnsupportedTranslation;1643 return error.UnsupportedTranslation;
2137}1644}
...@@ -2142,11 +1649,11 @@ fn zigArraySize(c: *Context, node: *ast.Node) TransError!usize {...@@ -2142,11 +1649,11 @@ fn zigArraySize(c: *Context, node: *ast.Node) TransError!usize {
2142/// than the array, truncate the string. If the array is larger than the1649/// than the array, truncate the string. If the array is larger than the
2143/// string literal, pad the array with 0's1650/// string literal, pad the array with 0's
2144fn transStringLiteralAsArray(1651fn transStringLiteralAsArray(
2145 rp: RestorePoint,1652 c: *Context,
2146 scope: *Scope,1653 scope: *Scope,
2147 stmt: *const clang.StringLiteral,1654 stmt: *const clang.StringLiteral,
2148 array_size: usize,1655 array_size: usize,
2149) TransError!*ast.Node {1656) TransError!Node {
2150 if (array_size == 0) return error.UnsupportedType;1657 if (array_size == 0) return error.UnsupportedType;
21511658
2152 const str_length = stmt.getLength();1659 const str_length = stmt.getLength();
...@@ -2155,40 +1662,25 @@ fn transStringLiteralAsArray(...@@ -2155,40 +1662,25 @@ fn transStringLiteralAsArray(
2155 const ty = expr_base.getType().getTypePtr();1662 const ty = expr_base.getType().getTypePtr();
2156 const const_arr_ty = @ptrCast(*const clang.ConstantArrayType, ty);1663 const const_arr_ty = @ptrCast(*const clang.ConstantArrayType, ty);
21571664
2158 const ty_node = try rp.c.arena.create(ast.Node.ArrayType);1665 const elem_type = try transQualType(c, scope, const_arr_ty.getElementType(), expr_base.getBeginLoc());
2159 const op_token = try appendToken(rp.c, .LBracket, "[");1666 const arr_type = try Tag.array_type.create(c.arena, .{ .len = array_size, .elem_type = elem_type });
2160 const len_expr = try transCreateNodeInt(rp.c, array_size);1667 const init_list = try c.arena.alloc(Node, 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();
21761668
2177 var i: c_uint = 0;1669 var i: c_uint = 0;
2178 const kind = stmt.getKind();1670 const kind = stmt.getKind();
2179 const narrow = kind == .Ascii or kind == .UTF8;1671 const narrow = kind == .Ascii or kind == .UTF8;
2180 while (i < str_length and i < array_size) : (i += 1) {1672 while (i < str_length and i < array_size) : (i += 1) {
2181 const code_unit = stmt.getCodeUnit(i);1673 const code_unit = stmt.getCodeUnit(i);
2182 init_list[i] = try transCreateCharLitNode(rp.c, narrow, code_unit);1674 init_list[i] = try transCreateCharLitNode(c, narrow, code_unit);
2183 _ = try appendToken(rp.c, .Comma, ",");
2184 }1675 }
2185 while (i < array_size) : (i += 1) {1676 while (i < array_size) : (i += 1) {
2186 init_list[i] = try transCreateNodeInt(rp.c, 0);1677 init_list[i] = try transCreateNodeNumber(c, 0, .int);
2187 _ = try appendToken(rp.c, .Comma, ",");
2188 }1678 }
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 });
2192}1684}
21931685
2194fn cIsEnum(qt: clang.QualType) bool {1686fn cIsEnum(qt: clang.QualType) bool {
...@@ -2207,199 +1699,164 @@ fn cIntTypeForEnum(enum_qt: clang.QualType) clang.QualType {...@@ -2207,199 +1699,164 @@ fn cIntTypeForEnum(enum_qt: clang.QualType) clang.QualType {
2207}1699}
22081700
2209fn transCCast(1701fn transCCast(
2210 rp: RestorePoint,1702 c: *Context,
2211 scope: *Scope,1703 scope: *Scope,
2212 loc: clang.SourceLocation,1704 loc: clang.SourceLocation,
2213 dst_type: clang.QualType,1705 dst_type: clang.QualType,
2214 src_type: clang.QualType,1706 src_type: clang.QualType,
2215 expr: *ast.Node,1707 expr: Node,
2216) !*ast.Node {1708) !Node {
2217 if (qualTypeCanon(dst_type).isVoidType()) return expr;1709 if (qualTypeCanon(dst_type).isVoidType()) return expr;
2218 if (dst_type.eq(src_type)) return expr;1710 if (dst_type.eq(src_type)) return expr;
2219 if (qualTypeIsPtr(dst_type) and qualTypeIsPtr(src_type))1711 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);
2221 if (cIsInteger(dst_type) and (cIsInteger(src_type) or cIsEnum(src_type))) {1715 if (cIsInteger(dst_type) and (cIsInteger(src_type) or cIsEnum(src_type))) {
2222 // 1. If src_type is an enum, determine the underlying signed int type1716 // 1. If src_type is an enum, determine the underlying signed int type
2223 // 2. Extend or truncate without changing signed-ness.1717 // 2. Extend or truncate without changing signed-ness.
2224 // 3. Bit-cast to correct signed-ness1718 // 3. Bit-cast to correct signed-ness
2225 const src_type_is_signed = cIsSignedInteger(src_type) or cIsEnum(src_type);1719 const src_type_is_signed = cIsSignedInteger(src_type) or cIsEnum(src_type);
2226 const src_int_type = if (cIsInteger(src_type)) src_type else cIntTypeForEnum(src_type);1720 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);1721 var src_int_expr = if (cIsInteger(src_type)) expr else try Tag.enum_to_int.create(c.arena, 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, ",");
22331722
2234 if (isBoolRes(src_int_expr)) {1723 if (isBoolRes(src_int_expr)) {
2235 const bool_to_int_node = try rp.c.createBuiltinCall("@boolToInt", 1);1724 src_int_expr = try Tag.bool_to_int.create(c.arena, src_int_expr);
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;
2239 }1725 }
22401726
2241 switch (cIntTypeCmp(dst_type, src_int_type)) {1727 switch (cIntTypeCmp(dst_type, src_int_type)) {
2242 .lt => {1728 .lt => {
2243 // @truncate(SameSignSmallerInt, src_int_expr)1729 // @truncate(SameSignSmallerInt, src_int_expr)
2244 const trunc_node = try rp.c.createBuiltinCall("@truncate", 2);1730 const ty_node = try transQualTypeIntWidthOf(c, dst_type, src_type_is_signed);
2245 const ty_node = try transQualTypeIntWidthOf(rp.c, dst_type, src_type_is_signed);1731 src_int_expr = try Tag.truncate.create(c.arena, .{ .lhs = ty_node, .rhs = src_int_expr });
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;
2252 },1732 },
2253 .gt => {1733 .gt => {
2254 // @as(SameSignBiggerInt, src_int_expr)1734 // @as(SameSignBiggerInt, src_int_expr)
2255 const as_node = try rp.c.createBuiltinCall("@as", 2);1735 const ty_node = try transQualTypeIntWidthOf(c, dst_type, src_type_is_signed);
2256 const ty_node = try transQualTypeIntWidthOf(rp.c, dst_type, src_type_is_signed);1736 src_int_expr = try Tag.as.create(c.arena, .{ .lhs = ty_node, .rhs = src_int_expr });
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;
2263 },1737 },
2264 .eq => {1738 .eq => {
2265 cast_node.params()[1] = src_int_expr;1739 // src_int_expr = src_int_expr
2266 },1740 },
2267 }1741 }
2268 cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");1742 // @bitCast(dest_type, intermediate_value)
2269 return &cast_node.base;1743 return Tag.bit_cast.create(c.arena, .{ .lhs = dst_node, .rhs = src_int_expr });
2270 }1744 }
2271 if (cIsInteger(dst_type) and qualTypeIsPtr(src_type)) {1745 if (cIsInteger(dst_type) and qualTypeIsPtr(src_type)) {
2272 // @intCast(dest_type, @ptrToInt(val))1746 // @intCast(dest_type, @ptrToInt(val))
2273 const cast_node = try rp.c.createBuiltinCall("@intCast", 2);1747 const ptr_to_int = try Tag.ptr_to_int.create(c.arena, expr);
2274 cast_node.params()[0] = try transQualType(rp, dst_type, loc);1748 return Tag.int_cast.create(c.arena, .{ .lhs = dst_node, .rhs = ptr_to_int });
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;
2282 }1749 }
2283 if (cIsInteger(src_type) and qualTypeIsPtr(dst_type)) {1750 if (cIsInteger(src_type) and qualTypeIsPtr(dst_type)) {
2284 // @intToPtr(dest_type, val)1751 // @intToPtr(dest_type, val)
2285 const builtin_node = try rp.c.createBuiltinCall("@intToPtr", 2);1752 return Tag.int_to_ptr.create(c.arena, .{ .lhs = dst_node, .rhs = expr });
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;
2291 }1753 }
2292 if (cIsFloating(src_type) and cIsFloating(dst_type)) {1754 if (cIsFloating(src_type) and cIsFloating(dst_type)) {
2293 const builtin_node = try rp.c.createBuiltinCall("@floatCast", 2);1755 // @floatCast(dest_type, val)
2294 builtin_node.params()[0] = try transQualType(rp, dst_type, loc);1756 return Tag.float_cast.create(c.arena, .{ .lhs = dst_node, .rhs = expr });
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;
2299 }1757 }
2300 if (cIsFloating(src_type) and !cIsFloating(dst_type)) {1758 if (cIsFloating(src_type) and !cIsFloating(dst_type)) {
2301 const builtin_node = try rp.c.createBuiltinCall("@floatToInt", 2);1759 // @floatToInt(dest_type, val)
2302 builtin_node.params()[0] = try transQualType(rp, dst_type, loc);1760 return Tag.float_to_int.create(c.arena, .{ .lhs = dst_node, .rhs = expr });
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;
2307 }1761 }
2308 if (!cIsFloating(src_type) and cIsFloating(dst_type)) {1762 if (!cIsFloating(src_type) and cIsFloating(dst_type)) {
2309 const builtin_node = try rp.c.createBuiltinCall("@intToFloat", 2);1763 // @intToFloat(dest_type, val)
2310 builtin_node.params()[0] = try transQualType(rp, dst_type, loc);1764 return Tag.int_to_float.create(c.arena, .{ .lhs = dst_node, .rhs = expr });
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;
2315 }1765 }
2316 if (qualTypeIsBoolean(src_type) and !qualTypeIsBoolean(dst_type)) {1766 if (qualTypeIsBoolean(src_type) and !qualTypeIsBoolean(dst_type)) {
2317 // @boolToInt returns either a comptime_int or a u11767 // @boolToInt returns either a comptime_int or a u1
2318 // TODO: if dst_type is 1 bit & signed (bitfield) we need @bitCast1768 // TODO: if dst_type is 1 bit & signed (bitfield) we need @bitCast
2319 // instead of @as1769 // instead of @as
2320 const builtin_node = try rp.c.createBuiltinCall("@boolToInt", 1);1770 const bool_to_int = try Tag.bool_to_int.create(c.arena, expr);
2321 builtin_node.params()[0] = expr;1771 return Tag.as.create(c.arena, .{ .lhs = dst_node, .rhs = bool_to_int });
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;
2331 }1772 }
2332 if (cIsEnum(dst_type)) {1773 if (cIsEnum(dst_type)) {
2333 const builtin_node = try rp.c.createBuiltinCall("@intToEnum", 2);1774 // @intToEnum(dest_type, val)
2334 builtin_node.params()[0] = try transQualType(rp, dst_type, loc);1775 return Tag.int_to_enum.create(c.arena, .{ .lhs = dst_node, .rhs = expr });
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;
2339 }1776 }
2340 if (cIsEnum(src_type) and !cIsEnum(dst_type)) {1777 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);
2342 }1780 }
2343 const cast_node = try rp.c.createBuiltinCall("@as", 2);1781 // @as(dest_type, val)
2344 cast_node.params()[0] = try transQualType(rp, dst_type, loc);1782 return Tag.as.create(c.arena, .{ .lhs = dst_node, .rhs = expr });
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;
2356}1783}
23571784
2358fn transExpr(1785fn transExpr(c: *Context, scope: *Scope, expr: *const clang.Expr, used: ResultUsed) TransError!Node {
2359 rp: RestorePoint,1786 return transStmt(c, scope, @ptrCast(*const clang.Stmt, expr), used);
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);
2366}1787}
23671788
2368/// Same as `transExpr` but with the knowledge that the operand will be type coerced, and therefore1789/// Same as `transExpr` but with the knowledge that the operand will be type coerced, and therefore
2369/// an `@as` would be redundant. This is used to prevent redundant `@as` in integer literals.1790/// an `@as` would be redundant. This is used to prevent redundant `@as` in integer literals.
2370fn transExprCoercing(1791fn transExprCoercing(c: *Context, scope: *Scope, expr: *const clang.Expr, used: ResultUsed) TransError!Node {
2371 rp: RestorePoint,
2372 scope: *Scope,
2373 expr: *const clang.Expr,
2374 used: ResultUsed,
2375 lrvalue: LRValue,
2376) TransError!*ast.Node {
2377 switch (@ptrCast(*const clang.Stmt, expr).getStmtClass()) {1792 switch (@ptrCast(*const clang.Stmt, expr).getStmtClass()) {
2378 .IntegerLiteralClass => {1793 .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);
2380 },1795 },
2381 .CharacterLiteralClass => {1796 .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);
2383 },1798 },
2384 .UnaryOperatorClass => {1799 .UnaryOperatorClass => {
2385 const un_expr = @ptrCast(*const clang.UnaryOperator, expr);1800 const un_expr = @ptrCast(*const clang.UnaryOperator, expr);
2386 if (un_expr.getOpcode() == .Extension) {1801 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 => {},
2388 }1819 }
2389 },1820 },
2390 else => {},1821 else => {},
2391 }1822 }
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 }
2393}1851}
23941852
2395fn transInitListExprRecord(1853fn transInitListExprRecord(
2396 rp: RestorePoint,1854 c: *Context,
2397 scope: *Scope,1855 scope: *Scope,
2398 loc: clang.SourceLocation,1856 loc: clang.SourceLocation,
2399 expr: *const clang.InitListExpr,1857 expr: *const clang.InitListExpr,
2400 ty: *const clang.Type,1858 ty: *const clang.Type,
2401 used: ResultUsed,1859) TransError!Node {
2402) TransError!*ast.Node {
2403 var is_union_type = false;1860 var is_union_type = false;
2404 // Unions and Structs are both represented as RecordDecl1861 // Unions and Structs are both represented as RecordDecl
2405 const record_ty = ty.getAsRecordType() orelse1862 const record_ty = ty.getAsRecordType() orelse
...@@ -2411,13 +1868,11 @@ fn transInitListExprRecord(...@@ -2411,13 +1868,11 @@ fn transInitListExprRecord(
2411 const record_def = record_decl.getDefinition() orelse1868 const record_def = record_decl.getDefinition() orelse
2412 unreachable;1869 unreachable;
24131870
2414 const ty_node = try transType(rp, ty, loc);1871 const ty_node = try transType(c, scope, ty, loc);
2415 const init_count = expr.getNumInits();1872 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);
2417 defer field_inits.deinit();1874 defer field_inits.deinit();
24181875
2419 _ = try appendToken(rp.c, .LBrace, "{");
2420
2421 var init_i: c_uint = 0;1876 var init_i: c_uint = 0;
2422 var it = record_def.field_begin();1877 var it = record_def.field_begin();
2423 const end_it = record_def.field_end();1878 const end_it = record_def.field_end();
...@@ -2435,78 +1890,34 @@ fn transInitListExprRecord(...@@ -2435,78 +1890,34 @@ fn transInitListExprRecord(
24351890
2436 // Generate the field assignment expression:1891 // Generate the field assignment expression:
2437 // .field_name = expr1892 // .field_name = expr
2438 const period_tok = try appendToken(rp.c, .Period, ".");1893 var raw_name = try c.str(@ptrCast(*const clang.NamedDecl, field_decl).getName_bytes_begin());
2439
2440 var raw_name = try rp.c.str(@ptrCast(*const clang.NamedDecl, field_decl).getName_bytes_begin());
2441 if (field_decl.isAnonymousStructOrUnion()) {1894 if (field_decl.isAnonymousStructOrUnion()) {
2442 const name = rp.c.decl_table.get(@ptrToInt(field_decl.getCanonicalDecl())).?;1895 const name = c.decl_table.get(@ptrToInt(field_decl.getCanonicalDecl())).?;
2443 raw_name = try mem.dupe(rp.c.arena, u8, name);1896 raw_name = try mem.dupe(c.arena, u8, name);
2444 }1897 }
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);1899 try field_inits.append(.{
2450 field_init_node.* = .{1900 .name = raw_name,
2451 .period_token = period_tok,1901 .value = try transExpr(c, scope, elem_expr, .used),
2452 .name_token = field_name_tok,1902 });
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, ",");
2458 }1903 }
24591904
2460 const node = try ast.Node.StructInitializer.alloc(rp.c.arena, field_inits.items.len);1905 return Tag.container_init.create(c.arena, .{
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.* = .{
2493 .lhs = ty_node,1906 .lhs = ty_node,
2494 .rtoken = try appendToken(rp.c, .RBrace, "}"),1907 .inits = try c.arena.dupe(ast.Payload.ContainerInit.Initializer, field_inits.items),
2495 .list_len = 0,1908 });
2496 };
2497 return &filler_init_node.base;
2498}1909}
24991910
2500fn transInitListExprArray(1911fn transInitListExprArray(
2501 rp: RestorePoint,1912 c: *Context,
2502 scope: *Scope,1913 scope: *Scope,
2503 loc: clang.SourceLocation,1914 loc: clang.SourceLocation,
2504 expr: *const clang.InitListExpr,1915 expr: *const clang.InitListExpr,
2505 ty: *const clang.Type,1916 ty: *const clang.Type,
2506 used: ResultUsed,1917) TransError!Node {
2507) TransError!*ast.Node {
2508 const arr_type = ty.getAsArrayTypeUnsafe();1918 const arr_type = ty.getAsArrayTypeUnsafe();
2509 const child_qt = arr_type.getElementType();1919 const child_qt = arr_type.getElementType();
1920 const child_type = try transQualType(c, scope, child_qt, loc);
2510 const init_count = expr.getNumInits();1921 const init_count = expr.getNumInits();
2511 assert(@ptrCast(*const clang.Type, arr_type).isConstantArrayType());1922 assert(@ptrCast(*const clang.Type, arr_type).isConstantArrayType());
2512 const const_arr_ty = @ptrCast(*const clang.ConstantArrayType, arr_type);1923 const const_arr_ty = @ptrCast(*const clang.ConstantArrayType, arr_type);
...@@ -2515,125 +1926,83 @@ fn transInitListExprArray(...@@ -2515,125 +1926,83 @@ fn transInitListExprArray(
2515 const leftover_count = all_count - init_count;1926 const leftover_count = all_count - init_count;
25161927
2517 if (all_count == 0) {1928 if (all_count == 0) {
2518 return transCreateEmptyArray(rp, loc, child_qt.getTypePtr());1929 return Tag.empty_array.create(c.arena, child_type);
2519 }1930 }
25201931
2521 var init_node: *ast.Node.ArrayInitializer = undefined;1932 const init_node = if (init_count != 0) blk: {
2522 var cat_tok: ast.TokenIndex = undefined;1933 const init_list = try c.arena.alloc(Node, init_count);
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();
25381934
2539 var i: c_uint = 0;1935 for (init_list) |*init, i| {
2540 while (i < init_count) : (i += 1) {1936 const elem_expr = expr.getInit(@intCast(c_uint, i));
2541 const elem_expr = expr.getInit(i);1937 init.* = try transExprCoercing(c, scope, elem_expr, .used);
2542 init_list[i] = try transExpr(rp, scope, elem_expr, .used, .r_value);
2543 _ = try appendToken(rp.c, .Comma, ",");
2544 }1938 }
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 });
2546 if (leftover_count == 0) {1943 if (leftover_count == 0) {
2547 return &init_node.base;1944 return init_node;
2548 }1945 }
2549 cat_tok = try appendToken(rp.c, .PlusPlus, "++");1946 break :blk init_node;
2550 }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 };
2560 const filler_val_expr = expr.getArrayFiller();1949 const filler_val_expr = expr.getArrayFiller();
2561 filler_init_node.list()[0] = try transExpr(rp, scope, filler_val_expr, .used, .r_value);1950 const filler_node = try Tag.array_filler.create(c.arena, .{
2562 filler_init_node.rtoken = try appendToken(rp.c, .RBrace, "}");1951 .type = child_type,
25631952 .filler = try transExprCoercing(c, scope, filler_val_expr, .used),
2564 const rhs_node = if (leftover_count == 1)1953 .count = leftover_count,
2565 &filler_init_node.base1954 });
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 };
25771955
2578 if (init_count == 0) {1956 if (init_node) |some| {
2579 return rhs_node;1957 return Tag.array_cat.create(c.arena, .{ .lhs = some, .rhs = filler_node });
1958 } else {
1959 return filler_node;
2580 }1960 }
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;
2590}1961}
25911962
2592fn transInitListExpr(1963fn transInitListExpr(
2593 rp: RestorePoint,1964 c: *Context,
2594 scope: *Scope,1965 scope: *Scope,
2595 expr: *const clang.InitListExpr,1966 expr: *const clang.InitListExpr,
2596 used: ResultUsed,1967 used: ResultUsed,
2597) TransError!*ast.Node {1968) TransError!Node {
2598 const qt = getExprQualType(rp.c, @ptrCast(*const clang.Expr, expr));1969 const qt = getExprQualType(c, @ptrCast(*const clang.Expr, expr));
2599 var qual_type = qt.getTypePtr();1970 var qual_type = qt.getTypePtr();
2600 const source_loc = @ptrCast(*const clang.Expr, expr).getBeginLoc();1971 const source_loc = @ptrCast(*const clang.Expr, expr).getBeginLoc();
26011972
2602 if (qual_type.isRecordType()) {1973 if (qual_type.isRecordType()) {
2603 return transInitListExprRecord(1974 return maybeSuppressResult(c, scope, used, try transInitListExprRecord(
2604 rp,1975 c,
2605 scope,1976 scope,
2606 source_loc,1977 source_loc,
2607 expr,1978 expr,
2608 qual_type,1979 qual_type,
2609 used,1980 ));
2610 );
2611 } else if (qual_type.isArrayType()) {1981 } else if (qual_type.isArrayType()) {
2612 return transInitListExprArray(1982 return maybeSuppressResult(c, scope, used, try transInitListExprArray(
2613 rp,1983 c,
2614 scope,1984 scope,
2615 source_loc,1985 source_loc,
2616 expr,1986 expr,
2617 qual_type,1987 qual_type,
2618 used,1988 ));
2619 );
2620 } else {1989 } else {
2621 const type_name = rp.c.str(qual_type.getTypeClassName());1990 const type_name = c.str(qual_type.getTypeClassName());
2622 return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported initlist type: '{s}'", .{type_name});1991 return fail(c, error.UnsupportedType, source_loc, "unsupported initlist type: '{s}'", .{type_name});
2623 }1992 }
2624}1993}
26251994
2626fn transZeroInitExpr(1995fn transZeroInitExpr(
2627 rp: RestorePoint,1996 c: *Context,
2628 scope: *Scope,1997 scope: *Scope,
2629 source_loc: clang.SourceLocation,1998 source_loc: clang.SourceLocation,
2630 ty: *const clang.Type,1999 ty: *const clang.Type,
2631) TransError!*ast.Node {2000) TransError!Node {
2632 switch (ty.getTypeClass()) {2001 switch (ty.getTypeClass()) {
2633 .Builtin => {2002 .Builtin => {
2634 const builtin_ty = @ptrCast(*const clang.BuiltinType, ty);2003 const builtin_ty = @ptrCast(*const clang.BuiltinType, ty);
2635 switch (builtin_ty.getKind()) {2004 switch (builtin_ty.getKind()) {
2636 .Bool => return try transCreateNodeBoolLiteral(rp.c, false),2005 .Bool => return Tag.false_literal.init(),
2637 .Char_U,2006 .Char_U,
2638 .UChar,2007 .UChar,
2639 .Char_S,2008 .Char_S,
...@@ -2654,126 +2023,112 @@ fn transZeroInitExpr(...@@ -2654,126 +2023,112 @@ fn transZeroInitExpr(
2654 .Float128,2023 .Float128,
2655 .Float16,2024 .Float16,
2656 .LongDouble,2025 .LongDouble,
2657 => return transCreateNodeInt(rp.c, 0),2026 => return Tag.zero_literal.init(),
2658 else => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported builtin type", .{}),2027 else => return fail(c, error.UnsupportedType, source_loc, "unsupported builtin type", .{}),
2659 }2028 }
2660 },2029 },
2661 .Pointer => return transCreateNodeNullLiteral(rp.c),2030 .Pointer => return Tag.null_literal.init(),
2662 .Typedef => {2031 .Typedef => {
2663 const typedef_ty = @ptrCast(*const clang.TypedefType, ty);2032 const typedef_ty = @ptrCast(*const clang.TypedefType, ty);
2664 const typedef_decl = typedef_ty.getDecl();2033 const typedef_decl = typedef_ty.getDecl();
2665 return transZeroInitExpr(2034 return transZeroInitExpr(
2666 rp,2035 c,
2667 scope,2036 scope,
2668 source_loc,2037 source_loc,
2669 typedef_decl.getUnderlyingType().getTypePtr(),2038 typedef_decl.getUnderlyingType().getTypePtr(),
2670 );2039 );
2671 },2040 },
2672 else => {},2041 else => return Tag.std_mem_zeroes.create(c.arena, try transType(c, scope, ty, source_loc)),
2673 }2042 }
2674
2675 return revertAndWarn(rp, error.UnsupportedType, source_loc, "type does not have an implicit init value", .{});
2676}2043}
26772044
2678fn transImplicitValueInitExpr(2045fn transImplicitValueInitExpr(
2679 rp: RestorePoint,2046 c: *Context,
2680 scope: *Scope,2047 scope: *Scope,
2681 expr: *const clang.Expr,2048 expr: *const clang.Expr,
2682 used: ResultUsed,2049 used: ResultUsed,
2683) TransError!*ast.Node {2050) TransError!Node {
2684 const source_loc = expr.getBeginLoc();2051 const source_loc = expr.getBeginLoc();
2685 const qt = getExprQualType(rp.c, expr);2052 const qt = getExprQualType(c, expr);
2686 const ty = qt.getTypePtr();2053 const ty = qt.getTypePtr();
2687 return transZeroInitExpr(rp, scope, source_loc, ty);2054 return transZeroInitExpr(c, scope, source_loc, ty);
2688}2055}
26892056
2690fn transIfStmt(2057fn transIfStmt(
2691 rp: RestorePoint,2058 c: *Context,
2692 scope: *Scope,2059 scope: *Scope,
2693 stmt: *const clang.IfStmt,2060 stmt: *const clang.IfStmt,
2694) TransError!*ast.Node {2061) TransError!Node {
2695 // if (c) t2062 // if (c) t
2696 // if (c) t else e2063 // if (c) t else e
2697 const if_node = try transCreateNodeIf(rp.c);
2698
2699 var cond_scope = Scope.Condition{2064 var cond_scope = Scope.Condition{
2700 .base = .{2065 .base = .{
2701 .parent = scope,2066 .parent = scope,
2702 .id = .Condition,2067 .id = .condition,
2703 },2068 },
2704 };2069 };
2705 defer cond_scope.deinit();2070 defer cond_scope.deinit();
2706 const cond_expr = @ptrCast(*const clang.Expr, stmt.getCond());2071 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);2072 const cond = try transBoolExpr(c, &cond_scope.base, cond_expr, .used);
2708 _ = try appendToken(rp.c, .RParen, ")");
27092073
2710 if_node.body = try transStmt(rp, scope, stmt.getThen(), .unused, .r_value);2074 const then_body = try transStmt(c, scope, stmt.getThen(), .unused);
27112075 const else_body = if (stmt.getElse()) |expr|
2712 if (stmt.getElse()) |expr| {2076 try transStmt(c, scope, expr, .unused)
2713 if_node.@"else" = try transCreateNodeElse(rp.c);2077 else
2714 if_node.@"else".?.body = try transStmt(rp, scope, expr, .unused, .r_value);2078 null;
2715 }2079 return Tag.@"if".create(c.arena, .{ .cond = cond, .then = then_body, .@"else" = else_body });
2716 _ = try appendToken(rp.c, .Semicolon, ";");
2717 return &if_node.base;
2718}2080}
27192081
2720fn transWhileLoop(2082fn transWhileLoop(
2721 rp: RestorePoint,2083 c: *Context,
2722 scope: *Scope,2084 scope: *Scope,
2723 stmt: *const clang.WhileStmt,2085 stmt: *const clang.WhileStmt,
2724) TransError!*ast.Node {2086) TransError!Node {
2725 const while_node = try transCreateNodeWhile(rp.c);
2726
2727 var cond_scope = Scope.Condition{2087 var cond_scope = Scope.Condition{
2728 .base = .{2088 .base = .{
2729 .parent = scope,2089 .parent = scope,
2730 .id = .Condition,2090 .id = .condition,
2731 },2091 },
2732 };2092 };
2733 defer cond_scope.deinit();2093 defer cond_scope.deinit();
2734 const cond_expr = @ptrCast(*const clang.Expr, stmt.getCond());2094 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);2095 const cond = try transBoolExpr(c, &cond_scope.base, cond_expr, .used);
2736 _ = try appendToken(rp.c, .RParen, ")");
27372096
2738 var loop_scope = Scope{2097 var loop_scope = Scope{
2739 .parent = scope,2098 .parent = scope,
2740 .id = .Loop,2099 .id = .loop,
2741 };2100 };
2742 while_node.body = try transStmt(rp, &loop_scope, stmt.getBody(), .unused, .r_value);2101 const body = try transStmt(c, &loop_scope, stmt.getBody(), .unused);
2743 _ = try appendToken(rp.c, .Semicolon, ";");2102 return Tag.@"while".create(c.arena, .{ .cond = cond, .body = body, .cont_expr = null });
2744 return &while_node.base;
2745}2103}
27462104
2747fn transDoWhileLoop(2105fn transDoWhileLoop(
2748 rp: RestorePoint,2106 c: *Context,
2749 scope: *Scope,2107 scope: *Scope,
2750 stmt: *const clang.DoStmt,2108 stmt: *const clang.DoStmt,
2751) TransError!*ast.Node {2109) TransError!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;
2757 var loop_scope = Scope{2110 var loop_scope = Scope{
2758 .parent = scope,2111 .parent = scope,
2759 .id = .Loop,2112 .id = .do_loop,
2760 };2113 };
27612114
2762 // if (!cond) break;2115 // if (!cond) break;
2763 const if_node = try transCreateNodeIf(rp.c);
2764 var cond_scope = Scope.Condition{2116 var cond_scope = Scope.Condition{
2765 .base = .{2117 .base = .{
2766 .parent = scope,2118 .parent = scope,
2767 .id = .Condition,2119 .id = .condition,
2768 },2120 },
2769 };2121 };
2770 defer cond_scope.deinit();2122 defer cond_scope.deinit();
2771 const prefix_op = try transCreateNodeSimplePrefixOp(rp.c, .BoolNot, .Bang, "!");2123 const cond = try transBoolExpr(c, &cond_scope.base, @ptrCast(*const clang.Expr, stmt.getCond()), .used);
2772 prefix_op.rhs = try transBoolExpr(rp, &cond_scope.base, @ptrCast(*const clang.Expr, stmt.getCond()), .used, .r_value, true);2124 const if_not_break = switch (cond.tag()) {
2773 _ = try appendToken(rp.c, .RParen, ")");2125 .false_literal => return transStmt(c, scope, stmt.getBody(), .unused),
2774 if_node.condition = &prefix_op.base;2126 .true_literal => {
2775 if_node.body = &(try transCreateNodeBreak(rp.c, null, null)).base;2127 const body_node = try transStmt(c, scope, stmt.getBody(), .unused);
2776 _ = try appendToken(rp.c, .Semicolon, ";");2128 return Tag.while_true.create(c.arena, body_node);
2129 },
2130 else => try Tag.if_not_break.create(c.arena, cond),
2131 };
27772132
2778 const body_node = if (stmt.getBody().getStmtClass() == .CompoundStmtClass) blk: {2133 const body_node = if (stmt.getBody().getStmtClass() == .CompoundStmtClass) blk: {
2779 // there's already a block in C, so we'll append our condition to it.2134 // there's already a block in C, so we'll append our condition to it.
...@@ -2786,8 +2141,11 @@ fn transDoWhileLoop(...@@ -2786,8 +2141,11 @@ fn transDoWhileLoop(
2786 // zig: b;2141 // zig: b;
2787 // zig: if (!cond) break;2142 // zig: if (!cond) break;
2788 // zig: }2143 // zig: }
2789 const node = try transStmt(rp, &loop_scope, stmt.getBody(), .unused, .r_value);2144 const node = try transStmt(c, &loop_scope, stmt.getBody(), .unused);
2790 break :blk node.castTag(.Block).?;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;
2791 } else blk: {2149 } else blk: {
2792 // the C statement is without a block, so we need to create a block to contain it.2150 // the C statement is without a block, so we need to create a block to contain it.
2793 // c: do2151 // c: do
...@@ -2797,387 +2155,356 @@ fn transDoWhileLoop(...@@ -2797,387 +2155,356 @@ fn transDoWhileLoop(
2797 // zig: a;2155 // zig: a;
2798 // zig: if (!cond) break;2156 // zig: if (!cond) break;
2799 // zig: }2157 // zig: }
2800 new = true;2158 const statements = try c.arena.alloc(Node, 2);
2801 const block = try rp.c.createBlock(2);2159 statements[0] = try transStmt(c, &loop_scope, stmt.getBody(), .unused);
2802 block.statements_len = 1; // over-allocated so we can add another below2160 statements[1] = if_not_break;
2803 block.statements()[0] = try transStmt(rp, &loop_scope, stmt.getBody(), .unused, .r_value);2161 break :blk try Tag.block.create(c.arena, .{ .label = null, .stmts = statements });
2804 break :blk block;
2805 };2162 };
28062163 return Tag.while_true.create(c.arena, body_node);
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;
2814}2164}
28152165
2816fn transForLoop(2166fn transForLoop(
2817 rp: RestorePoint,2167 c: *Context,
2818 scope: *Scope,2168 scope: *Scope,
2819 stmt: *const clang.ForStmt,2169 stmt: *const clang.ForStmt,
2820) TransError!*ast.Node {2170) TransError!Node {
2821 var loop_scope = Scope{2171 var loop_scope = Scope{
2822 .parent = scope,2172 .parent = scope,
2823 .id = .Loop,2173 .id = .loop,
2824 };2174 };
28252175
2826 var block_scope: ?Scope.Block = null;2176 var block_scope: ?Scope.Block = null;
2827 defer if (block_scope) |*bs| bs.deinit();2177 defer if (block_scope) |*bs| bs.deinit();
28282178
2829 if (stmt.getInit()) |init| {2179 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);
2831 loop_scope.parent = &block_scope.?.base;2181 loop_scope.parent = &block_scope.?.base;
2832 const init_node = try transStmt(rp, &block_scope.?.base, init, .unused, .r_value);2182 const init_node = try transStmt(c, &block_scope.?.base, init, .unused);
2833 try block_scope.?.statements.append(init_node);2183 if (init_node.tag() != .declaration) try block_scope.?.statements.append(init_node);
2834 }2184 }
2835 var cond_scope = Scope.Condition{2185 var cond_scope = Scope.Condition{
2836 .base = .{2186 .base = .{
2837 .parent = &loop_scope,2187 .parent = &loop_scope,
2838 .id = .Condition,2188 .id = .condition,
2839 },2189 },
2840 };2190 };
2841 defer cond_scope.deinit();2191 defer cond_scope.deinit();
28422192
2843 const while_node = try transCreateNodeWhile(rp.c);2193 const cond = if (stmt.getCond()) |cond|
2844 while_node.condition = if (stmt.getCond()) |cond|2194 try transBoolExpr(c, &cond_scope.base, cond, .used)
2845 try transBoolExpr(rp, &cond_scope.base, cond, .used, .r_value, false)
2846 else2195 else
2847 try transCreateNodeBoolLiteral(rp.c, true);2196 Tag.true_literal.init();
2848 _ = try appendToken(rp.c, .RParen, ")");
28492197
2850 if (stmt.getInc()) |incr| {2198 const cont_expr = if (stmt.getInc()) |incr|
2851 _ = try appendToken(rp.c, .Colon, ":");2199 try transExpr(c, &cond_scope.base, incr, .unused)
2852 _ = try appendToken(rp.c, .LParen, "(");2200 else
2853 while_node.continue_expr = try transExpr(rp, &cond_scope.base, incr, .unused, .r_value);2201 null;
2854 _ = try appendToken(rp.c, .RParen, ")");
2855 }
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 });
2858 if (block_scope) |*bs| {2205 if (block_scope) |*bs| {
2859 try bs.statements.append(&while_node.base);2206 try bs.statements.append(while_node);
2860 return try bs.complete(rp.c);2207 return try bs.complete(c);
2861 } else {2208 } else {
2862 _ = try appendToken(rp.c, .Semicolon, ";");2209 return while_node;
2863 return &while_node.base;
2864 }2210 }
2865}2211}
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
2878fn transSwitch(2213fn transSwitch(
2879 rp: RestorePoint,2214 c: *Context,
2880 scope: *Scope,2215 scope: *Scope,
2881 stmt: *const clang.SwitchStmt,2216 stmt: *const clang.SwitchStmt,
2882) TransError!*ast.Node {2217) TransError!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
2888 var cond_scope = Scope.Condition{2218 var cond_scope = Scope.Condition{
2889 .base = .{2219 .base = .{
2890 .parent = scope,2220 .parent = scope,
2891 .id = .Condition,2221 .id = .condition,
2892 },2222 },
2893 };2223 };
2894 defer cond_scope.deinit();2224 defer cond_scope.deinit();
2895 const switch_expr = try transExpr(rp, &cond_scope.base, stmt.getCond(), .used, .r_value);2225 const switch_expr = try transExpr(c, &cond_scope.base, stmt.getCond(), .used);
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 };
29062226
2907 var switch_scope = Scope.Switch{2227 var cases = std.ArrayList(Node).init(c.gpa);
2908 .base = .{2228 defer cases.deinit();
2909 .id = .Switch,2229 var has_default = false;
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 };
29182230
2919 // tmp block that all statements will go before being picked up by a case or default2231 const body = stmt.getBody();
2920 var block_scope = try Scope.Block.init(rp.c, &switch_scope.base, false);2232 assert(body.getStmtClass() == .CompoundStmtClass);
2921 defer block_scope.deinit();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 field2269 const res = try transSwitchProngStmt(c, scope, sub, it, end_it);
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, ",");
29562270
2957 if (switch_scope.case_index >= switch_scope.cases.len)2271 const switch_else = try Tag.switch_else.create(c.arena, res);
2958 return revertAndWarn(rp, error.UnsupportedTranslation, @ptrCast(*const clang.Stmt, stmt).getBeginLoc(), "TODO complex switch cases", .{});2272 try cases.append(switch_else);
2959 switch_scope.cases[switch_scope.case_index] = &else_prong.base;2273 },
2960 switch_scope.case_index += 1;2274 else => {}, // collected in transSwitchProngStmt
2275 }
2961 }2276 }
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);2278 if (!has_default) {
2998 switch_prong.expr = blk: {2279 const else_prong = try Tag.switch_else.create(c.arena, Tag.empty_block.init());
2999 var br = try CtrlFlow.init(rp.c, .Break, label);2280 try cases.append(else_prong);
3000 break :blk &(try br.finish(null)).base;2281 }
3001 };
3002 _ = try appendToken(rp.c, .Comma, ",");
30032282
3004 if (switch_scope.case_index >= switch_scope.cases.len)2283 return Tag.@"switch".create(c.arena, .{
3005 return revertAndWarn(rp, error.UnsupportedTranslation, @ptrCast(*const clang.Stmt, stmt).getBeginLoc(), "TODO complex switch cases", .{});2284 .cond = switch_expr,
3006 switch_scope.cases[switch_scope.case_index] = &switch_prong.base;2285 .cases = try c.arena.dupe(Node, cases.items),
3007 switch_scope.case_index += 1;2286 });
2287}
30082288
3009 switch_scope.pending_block.label = try appendIdentifier(rp.c, label);2289/// Collects all items for this case, returns the first statement after the labels.
3010 _ = try appendToken(rp.c, .Colon, ":");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 statements2305 if (seen_default) {
3013 try switch_scope.pending_block.statements.appendSlice(block_scope.statements.items);2306 items.items.len = 0;
3014 block_scope.statements.shrinkAndFree(0);2307 sub = case_stmt.getSubStmt();
2308 continue;
2309 }
30152310
3016 const pending_node = try switch_scope.pending_block.complete(rp.c);2311 const expr = if (case_stmt.getRHS()) |rhs| blk: {
3017 switch_scope.pending_block.deinit();2312 const lhs_node = try transExprCoercing(c, scope, case_stmt.getLHS(), .used);
3018 switch_scope.pending_block = try Scope.Block.init(rp.c, scope, false);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 }
3023}2325}
30242326
3025fn transDefault(2327/// Collects all statements seen by this case into a block.
3026 rp: RestorePoint,2328/// Avoids creating a block if the first statement is a break or return.
2329fn transSwitchProngStmt(
2330 c: *Context,
3027 scope: *Scope,2331 scope: *Scope,
3028 stmt: *const clang.DefaultStmt,2332 stmt: *const clang.Stmt,
3029) TransError!*ast.Node {2333 parent_it: clang.CompoundStmt.ConstBodyIterator,
3030 const block_scope = scope.findBlockScope(rp.c) catch unreachable;2334 parent_end_it: clang.CompoundStmt.ConstBodyIterator,
3031 const switch_scope = scope.getSwitch();2335) TransError!Node {
3032 switch_scope.default_label = try block_scope.makeMangledName(rp.c, "default");2336 switch (stmt.getStmtClass()) {
3033 _ = try appendToken(rp.c, .Semicolon, ";");2337 .BreakStmtClass => return Tag.empty_block.init(),
30342338 .ReturnStmtClass => return transStmt(c, scope, stmt, .unused),
3035 const else_prong = try transCreateNodeSwitchCase(rp.c, try transCreateNodeSwitchElse(rp.c));2339 .CaseStmtClass, .DefaultStmtClass => unreachable,
3036 else_prong.expr = blk: {2340 else => {
3037 var br = try CtrlFlow.init(rp.c, .Break, switch_scope.default_label.?);2341 var block_scope = try Scope.Block.init(c, scope, false);
3038 break :blk &(try br.finish(null)).base;2342 defer block_scope.deinit();
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);
30532343
3054 const pending_node = try switch_scope.pending_block.complete(rp.c);2344 // we do not need to translate `stmt` since it is the first stmt of `parent_it`
3055 switch_scope.pending_block.deinit();2345 try transSwitchProngStmtInline(c, &block_scope, parent_it, parent_end_it);
3056 switch_scope.pending_block = try Scope.Block.init(rp.c, scope, false);2346 return try block_scope.complete(c);
3057 try switch_scope.pending_block.statements.append(pending_node);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;
3060}2412}
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 {
3063 var result: clang.ExprEvalResult = undefined;2415 var result: clang.ExprEvalResult = undefined;
3064 if (!expr.EvaluateAsConstantExpr(&result, .EvaluateForCodeGen, rp.c.clang_context))2416 if (!expr.evaluateAsConstantExpr(&result, .EvaluateForCodeGen, c.clang_context))
3065 return revertAndWarn(rp, error.UnsupportedTranslation, expr.getBeginLoc(), "invalid constant expression", .{});2417 return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "invalid constant expression", .{});
30662418
3067 var val_node: ?*ast.Node = null;
3068 switch (result.Val.getKind()) {2419 switch (result.Val.getKind()) {
3069 .Int => {2420 .Int => {
3070 // See comment in `transIntegerLiteral` for why this code is here.2421 // See comment in `transIntegerLiteral` for why this code is here.
3071 // @as(T, x)2422 // @as(T, x)
3072 const expr_base = @ptrCast(*const clang.Expr, expr);2423 const expr_base = @ptrCast(*const clang.Expr, expr);
3073 const as_node = try rp.c.createBuiltinCall("@as", 2);2424 const as_node = try Tag.as.create(c.arena, .{
3074 const ty_node = try transQualType(rp, expr_base.getType(), expr_base.getBeginLoc());2425 .lhs = try transQualType(c, scope, expr_base.getType(), expr_base.getBeginLoc()),
3075 as_node.params()[0] = ty_node;2426 .rhs = try transCreateNodeAPInt(c, result.Val.getInt()),
3076 _ = try appendToken(rp.c, .Comma, ",");2427 });
30772428 return maybeSuppressResult(c, scope, used, as_node);
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);
3084 },2429 },
3085 else => {2430 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", .{});
3087 },2432 },
3088 }2433 }
3089}2434}
30902435
3091fn transPredefinedExpr(rp: RestorePoint, scope: *Scope, expr: *const clang.PredefinedExpr, used: ResultUsed) TransError!*ast.Node {2436fn transPredefinedExpr(c: *Context, scope: *Scope, expr: *const clang.PredefinedExpr, used: ResultUsed) TransError!Node {
3092 return transStringLiteral(rp, scope, expr.getFunctionName(), used);2437 return transStringLiteral(c, scope, expr.getFunctionName(), used);
3093}2438}
30942439
3095fn transCreateCharLitNode(c: *Context, narrow: bool, val: u32) TransError!*ast.Node {2440fn transCreateCharLitNode(c: *Context, narrow: bool, val: u32) TransError!Node {
3096 const node = try c.arena.create(ast.Node.OneToken);2441 return Tag.char_literal.create(c.arena, if (narrow)
3097 node.* = .{2442 try std.fmt.allocPrint(c.arena, "'{s}'", .{std.zig.fmtEscapes(&.{@intCast(u8, val)})})
3098 .base = .{ .tag = .CharLiteral },2443 else
3099 .token = undefined,2444 try std.fmt.allocPrint(c.arena, "'\\u{{{x}}}'", .{val}));
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;
3108}2445}
31092446
3110fn transCharLiteral(2447fn transCharLiteral(
3111 rp: RestorePoint,2448 c: *Context,
3112 scope: *Scope,2449 scope: *Scope,
3113 stmt: *const clang.CharacterLiteral,2450 stmt: *const clang.CharacterLiteral,
3114 result_used: ResultUsed,2451 result_used: ResultUsed,
3115 suppress_as: SuppressCast,2452 suppress_as: SuppressCast,
3116) TransError!*ast.Node {2453) TransError!Node {
3117 const kind = stmt.getKind();2454 const kind = stmt.getKind();
3118 const val = stmt.getValue();2455 const val = stmt.getValue();
3119 const narrow = kind == .Ascii or kind == .UTF8;2456 const narrow = kind == .Ascii or kind == .UTF8;
3120 // C has a somewhat obscure feature called multi-character character constant2457 // C has a somewhat obscure feature called multi-character character constant
3121 // e.g. 'abcd'2458 // e.g. 'abcd'
3122 const int_lit_node = if (kind == .Ascii and val > 255)2459 const int_lit_node = if (kind == .Ascii and val > 255)
3123 try transCreateNodeInt(rp.c, val)2460 try transCreateNodeNumber(c, val, .int)
3124 else2461 else
3125 try transCreateCharLitNode(rp.c, narrow, val);2462 try transCreateCharLitNode(c, narrow, val);
31262463
3127 if (suppress_as == .no_as) {2464 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);
3129 }2466 }
3130 // See comment in `transIntegerLiteral` for why this code is here.2467 // See comment in `transIntegerLiteral` for why this code is here.
3131 // @as(T, x)2468 // @as(T, x)
3132 const expr_base = @ptrCast(*const clang.Expr, stmt);2469 const expr_base = @ptrCast(*const clang.Expr, stmt);
3133 const as_node = try rp.c.createBuiltinCall("@as", 2);2470 const as_node = try Tag.as.create(c.arena, .{
3134 const ty_node = try transQualType(rp, expr_base.getType(), expr_base.getBeginLoc());2471 .lhs = try transQualType(c, scope, expr_base.getType(), expr_base.getBeginLoc()),
3135 as_node.params()[0] = ty_node;2472 .rhs = int_lit_node,
3136 _ = try appendToken(rp.c, .Comma, ",");2473 });
3137 as_node.params()[1] = int_lit_node;2474 return maybeSuppressResult(c, scope, result_used, as_node);
3138
3139 as_node.rparen_token = try appendToken(rp.c, .RParen, ")");
3140 return maybeSuppressResult(rp, scope, result_used, &as_node.base);
3141}2475}
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 {
3144 const comp = stmt.getSubStmt();2478 const comp = stmt.getSubStmt();
3145 if (used == .unused) {2479 if (used == .unused) {
3146 return transCompoundStmt(rp, scope, comp);2480 return transCompoundStmt(c, scope, comp);
3147 }2481 }
3148 const lparen = try appendToken(rp.c, .LParen, "(");2482 var block_scope = try Scope.Block.init(c, scope, true);
3149 var block_scope = try Scope.Block.init(rp.c, scope, true);
3150 defer block_scope.deinit();2483 defer block_scope.deinit();
31512484
3152 var it = comp.body_begin();2485 var it = comp.body_begin();
3153 const end_it = comp.body_end();2486 const end_it = comp.body_end();
3154 while (it != end_it - 1) : (it += 1) {2487 while (it != end_it - 1) : (it += 1) {
3155 const result = try transStmt(rp, &block_scope.base, it[0], .unused, .r_value);2488 const result = try transStmt(c, &block_scope.base, it[0], .unused);
3156 try block_scope.statements.append(result);2489 switch (result.tag()) {
2490 .declaration, .empty_block => {},
2491 else => try block_scope.statements.append(result),
2492 }
3157 }2493 }
3158 const break_node = blk: {2494 const break_node = try Tag.break_val.create(c.arena, .{
3159 var tmp = try CtrlFlow.init(rp.c, .Break, "blk");2495 .label = block_scope.label,
3160 const rhs = try transStmt(rp, &block_scope.base, it[0], .used, .r_value);2496 .val = try transStmt(c, &block_scope.base, it[0], .used),
3161 break :blk try tmp.finish(rhs);2497 });
3162 };2498 try block_scope.statements.append(break_node);
3163 _ = try appendToken(rp.c, .Semicolon, ";");2499 const res = try block_scope.complete(c);
3164 try block_scope.statements.append(&break_node.base);2500 return maybeSuppressResult(c, scope, used, res);
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);
3174}2501}
31752502
3176fn transMemberExpr(rp: RestorePoint, scope: *Scope, stmt: *const clang.MemberExpr, result_used: ResultUsed) TransError!*ast.Node {2503fn transMemberExpr(c: *Context, scope: *Scope, stmt: *const clang.MemberExpr, result_used: ResultUsed) TransError!Node {
3177 var container_node = try transExpr(rp, scope, stmt.getBase(), .used, .r_value);2504 var container_node = try transExpr(c, scope, stmt.getBase(), .used);
31782505
3179 if (stmt.isArrow()) {2506 if (stmt.isArrow()) {
3180 container_node = try transCreateNodePtrDeref(rp.c, container_node);2507 container_node = try Tag.deref.create(c.arena, container_node);
3181 }2508 }
31822509
3183 const member_decl = stmt.getMemberDecl();2510 const member_decl = stmt.getMemberDecl();
...@@ -3188,19 +2515,18 @@ fn transMemberExpr(rp: RestorePoint, scope: *Scope, stmt: *const clang.MemberExp...@@ -3188,19 +2515,18 @@ fn transMemberExpr(rp: RestorePoint, scope: *Scope, stmt: *const clang.MemberExp
3188 if (decl_kind == .Field) {2515 if (decl_kind == .Field) {
3189 const field_decl = @ptrCast(*const clang.FieldDecl, member_decl);2516 const field_decl = @ptrCast(*const clang.FieldDecl, member_decl);
3190 if (field_decl.isAnonymousStructOrUnion()) {2517 if (field_decl.isAnonymousStructOrUnion()) {
3191 const name = rp.c.decl_table.get(@ptrToInt(field_decl.getCanonicalDecl())).?;2518 const name = c.decl_table.get(@ptrToInt(field_decl.getCanonicalDecl())).?;
3192 break :blk try mem.dupe(rp.c.arena, u8, name);2519 break :blk try mem.dupe(c.arena, u8, name);
3193 }2520 }
3194 }2521 }
3195 const decl = @ptrCast(*const clang.NamedDecl, member_decl);2522 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());
3197 };2524 };
31982525 const node = try Tag.field_access.create(c.arena, .{ .lhs = container_node, .field_name = name });
3199 const node = try transCreateNodeFieldAccess(rp.c, container_node, name);2526 return maybeSuppressResult(c, scope, result_used, node);
3200 return maybeSuppressResult(rp, scope, result_used, node);
3201}2527}
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 {
3204 var base_stmt = stmt.getBase();2530 var base_stmt = stmt.getBase();
32052531
3206 // Unwrap the base statement if it's an array decayed to a bare pointer type2532 // 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...@@ -3213,30 +2539,26 @@ fn transArrayAccess(rp: RestorePoint, scope: *Scope, stmt: *const clang.ArraySub
3213 }2539 }
3214 }2540 }
32152541
3216 const container_node = try transExpr(rp, scope, base_stmt, .used, .r_value);2542 const container_node = try transExpr(c, scope, base_stmt, .used);
3217 const node = try transCreateNodeArrayAccess(rp.c, container_node);
32182543
3219 // cast if the index is long long or signed2544 // cast if the index is long long or signed
3220 const subscr_expr = stmt.getIdx();2545 const subscr_expr = stmt.getIdx();
3221 const qt = getExprQualType(rp.c, subscr_expr);2546 const qt = getExprQualType(c, subscr_expr);
3222 const is_longlong = cIsLongLongInteger(qt);2547 const is_longlong = cIsLongLongInteger(qt);
3223 const is_signed = cIsSignedInteger(qt);2548 const is_signed = cIsSignedInteger(qt);
32242549
3225 if (is_longlong or is_signed) {2550 const rhs = if (is_longlong or is_signed) blk: {
3226 const cast_node = try rp.c.createBuiltinCall("@intCast", 2);
3227 // check if long long first so that signed long long doesn't just become unsigned long long2551 // 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);2552 var typeid_node = if (is_longlong) try Tag.identifier.create(c.arena, "usize") else try transQualTypeIntWidthOf(c, qt, false);
3229 cast_node.params()[0] = typeid_node;2553 break :blk try Tag.int_cast.create(c.arena, .{ .lhs = typeid_node, .rhs = try transExpr(c, scope, subscr_expr, .used) });
3230 _ = try appendToken(rp.c, .Comma, ",");2554 } else
3231 cast_node.params()[1] = try transExpr(rp, scope, subscr_expr, .used, .r_value);2555 try transExpr(c, scope, subscr_expr, .used);
3232 cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");2556
3233 node.rtoken = try appendToken(rp.c, .RBrace, "]");2557 const node = try Tag.array_access.create(c.arena, .{
3234 node.index_expr = &cast_node.base;2558 .lhs = container_node,
3235 } else {2559 .rhs = rhs,
3236 node.index_expr = try transExpr(rp, scope, subscr_expr, .used, .r_value);2560 });
3237 node.rtoken = try appendToken(rp.c, .RBrace, "]");2561 return maybeSuppressResult(c, scope, result_used, node);
3238 }
3239 return maybeSuppressResult(rp, scope, result_used, &node.base);
3240}2562}
32412563
3242/// Check if an expression is ultimately a reference to a function declaration2564/// Check if an expression is ultimately a reference to a function declaration
...@@ -3271,29 +2593,25 @@ fn cIsFunctionDeclRef(expr: *const clang.Expr) bool {...@@ -3271,29 +2593,25 @@ fn cIsFunctionDeclRef(expr: *const clang.Expr) bool {
3271 }2593 }
3272}2594}
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 {
3275 const callee = stmt.getCallee();2597 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
3278 var is_ptr = false;2600 var is_ptr = false;
3279 const fn_ty = qualTypeGetFnProto(callee.getType(), &is_ptr);2601 const fn_ty = qualTypeGetFnProto(callee.getType(), &is_ptr);
32802602
3281 const fn_expr = if (is_ptr and fn_ty != null and !cIsFunctionDeclRef(callee))2603 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)
3283 else2605 else
3284 raw_fn_expr;2606 raw_fn_expr;
32852607
3286 const num_args = stmt.getNumArgs();2608 const num_args = stmt.getNumArgs();
3287 const node = try rp.c.createCall(fn_expr, num_args);2609 const args = try c.arena.alloc(Node, num_args);
3288 const call_params = node.params();
32892610
3290 const args = stmt.getArgs();2611 const c_args = stmt.getArgs();
3291 var i: usize = 0;2612 var i: usize = 0;
3292 while (i < num_args) : (i += 1) {2613 while (i < num_args) : (i += 1) {
3293 if (i != 0) {2614 var arg = try transExpr(c, scope, c_args[i], .used);
3294 _ = try appendToken(rp.c, .Comma, ",");
3295 }
3296 var call_param = try transExpr(rp, scope, args[i], .used, .r_value);
32972615
3298 // In C the result type of a boolean expression is int. If this result is passed as2616 // In C the result type of a boolean expression is int. If this result is passed as
3299 // an argument to a function whose parameter is also int, there is no cast. Therefore2617 // 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...@@ -3304,31 +2622,26 @@ fn transCallExpr(rp: RestorePoint, scope: *Scope, stmt: *const clang.CallExpr, r
3304 const param_count = fn_proto.getNumParams();2622 const param_count = fn_proto.getNumParams();
3305 if (i < param_count) {2623 if (i < param_count) {
3306 const param_qt = fn_proto.getParamType(@intCast(c_uint, i));2624 const param_qt = fn_proto.getParamType(@intCast(c_uint, i));
3307 if (isBoolRes(call_param) and cIsNativeInt(param_qt)) {2625 if (isBoolRes(arg) and cIsNativeInt(param_qt)) {
3308 const builtin_node = try rp.c.createBuiltinCall("@boolToInt", 1);2626 arg = try Tag.bool_to_int.create(c.arena, arg);
3309 builtin_node.params()[0] = call_param;
3310 builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
3311 call_param = &builtin_node.base;
3312 }2627 }
3313 }2628 }
3314 },2629 },
3315 else => {},2630 else => {},
3316 }2631 }
3317 }2632 }
3318 call_params[i] = call_param;2633 args[i] = arg;
3319 }2634 }
3320 node.rtoken = try appendToken(rp.c, .RParen, ")");2635 const node = try Tag.call.create(c.arena, .{ .lhs = fn_expr, .args = args });
3321
3322 if (fn_ty) |ty| {2636 if (fn_ty) |ty| {
3323 const canon = ty.getReturnType().getCanonicalType();2637 const canon = ty.getReturnType().getCanonicalType();
3324 const ret_ty = canon.getTypePtr();2638 const ret_ty = canon.getTypePtr();
3325 if (ret_ty.isVoidType()) {2639 if (ret_ty.isVoidType()) {
3326 _ = try appendToken(rp.c, .Semicolon, ";");2640 return node;
3327 return &node.base;
3328 }2641 }
3329 }2642 }
33302643
3331 return maybeSuppressResult(rp, scope, result_used, &node.base);2644 return maybeSuppressResult(c, scope, result_used, node);
3332}2645}
33332646
3334const ClangFunctionType = union(enum) {2647const ClangFunctionType = union(enum) {
...@@ -3363,38 +2676,29 @@ fn qualTypeGetFnProto(qt: clang.QualType, is_ptr: *bool) ?ClangFunctionType {...@@ -3363,38 +2676,29 @@ fn qualTypeGetFnProto(qt: clang.QualType, is_ptr: *bool) ?ClangFunctionType {
3363}2676}
33642677
3365fn transUnaryExprOrTypeTraitExpr(2678fn transUnaryExprOrTypeTraitExpr(
3366 rp: RestorePoint,2679 c: *Context,
3367 scope: *Scope,2680 scope: *Scope,
3368 stmt: *const clang.UnaryExprOrTypeTraitExpr,2681 stmt: *const clang.UnaryExprOrTypeTraitExpr,
3369 result_used: ResultUsed,2682 result_used: ResultUsed,
3370) TransError!*ast.Node {2683) TransError!Node {
3371 const loc = stmt.getBeginLoc();2684 const loc = stmt.getBeginLoc();
3372 const type_node = try transQualType(2685 const type_node = try transQualType(c, scope, stmt.getTypeOfArgument(), loc);
3373 rp,
3374 stmt.getTypeOfArgument(),
3375 loc,
3376 );
33772686
3378 const kind = stmt.getKind();2687 const kind = stmt.getKind();
3379 const kind_str = switch (kind) {2688 switch (kind) {
3380 .SizeOf => "@sizeOf",2689 .SizeOf => return Tag.sizeof.create(c.arena, type_node),
3381 .AlignOf => "@alignOf",2690 .AlignOf => return Tag.alignof.create(c.arena, type_node),
3382 .PreferredAlignOf,2691 .PreferredAlignOf,
3383 .VecStep,2692 .VecStep,
3384 .OpenMPRequiredSimdAlign,2693 .OpenMPRequiredSimdAlign,
3385 => return revertAndWarn(2694 => return fail(
3386 rp,2695 c,
3387 error.UnsupportedTranslation,2696 error.UnsupportedTranslation,
3388 loc,2697 loc,
3389 "Unsupported type trait kind {}",2698 "Unsupported type trait kind {}",
3390 .{kind},2699 .{kind},
3391 ),2700 ),
3392 };2701 }
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);
3398}2702}
33992703
3400fn qualTypeHasWrappingOverflow(qt: clang.QualType) bool {2704fn qualTypeHasWrappingOverflow(qt: clang.QualType) bool {
...@@ -3407,95 +2711,79 @@ fn qualTypeHasWrappingOverflow(qt: clang.QualType) bool {...@@ -3407,95 +2711,79 @@ fn qualTypeHasWrappingOverflow(qt: clang.QualType) bool {
3407 }2711 }
3408}2712}
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 {
3411 const op_expr = stmt.getSubExpr();2715 const op_expr = stmt.getSubExpr();
3412 switch (stmt.getOpcode()) {2716 switch (stmt.getOpcode()) {
3413 .PostInc => if (qualTypeHasWrappingOverflow(stmt.getType()))2717 .PostInc => if (qualTypeHasWrappingOverflow(stmt.getType()))
3414 return transCreatePostCrement(rp, scope, stmt, .AssignAddWrap, .PlusPercentEqual, "+%=", used)2718 return transCreatePostCrement(c, scope, stmt, .add_wrap_assign, used)
3415 else2719 else
3416 return transCreatePostCrement(rp, scope, stmt, .AssignAdd, .PlusEqual, "+=", used),2720 return transCreatePostCrement(c, scope, stmt, .add_assign, used),
3417 .PostDec => if (qualTypeHasWrappingOverflow(stmt.getType()))2721 .PostDec => if (qualTypeHasWrappingOverflow(stmt.getType()))
3418 return transCreatePostCrement(rp, scope, stmt, .AssignSubWrap, .MinusPercentEqual, "-%=", used)2722 return transCreatePostCrement(c, scope, stmt, .sub_wrap_assign, used)
3419 else2723 else
3420 return transCreatePostCrement(rp, scope, stmt, .AssignSub, .MinusEqual, "-=", used),2724 return transCreatePostCrement(c, scope, stmt, .sub_assign, used),
3421 .PreInc => if (qualTypeHasWrappingOverflow(stmt.getType()))2725 .PreInc => if (qualTypeHasWrappingOverflow(stmt.getType()))
3422 return transCreatePreCrement(rp, scope, stmt, .AssignAddWrap, .PlusPercentEqual, "+%=", used)2726 return transCreatePreCrement(c, scope, stmt, .add_wrap_assign, used)
3423 else2727 else
3424 return transCreatePreCrement(rp, scope, stmt, .AssignAdd, .PlusEqual, "+=", used),2728 return transCreatePreCrement(c, scope, stmt, .add_assign, used),
3425 .PreDec => if (qualTypeHasWrappingOverflow(stmt.getType()))2729 .PreDec => if (qualTypeHasWrappingOverflow(stmt.getType()))
3426 return transCreatePreCrement(rp, scope, stmt, .AssignSubWrap, .MinusPercentEqual, "-%=", used)2730 return transCreatePreCrement(c, scope, stmt, .sub_wrap_assign, used)
3427 else2731 else
3428 return transCreatePreCrement(rp, scope, stmt, .AssignSub, .MinusEqual, "-=", used),2732 return transCreatePreCrement(c, scope, stmt, .sub_assign, used),
3429 .AddrOf => {2733 .AddrOf => {
3430 if (cIsFunctionDeclRef(op_expr)) {2734 if (cIsFunctionDeclRef(op_expr)) {
3431 return transExpr(rp, scope, op_expr, used, .r_value);2735 return transExpr(c, scope, op_expr, used);
3432 }2736 }
3433 const op_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&");2737 return Tag.address_of.create(c.arena, try transExpr(c, scope, op_expr, used));
3434 op_node.rhs = try transExpr(rp, scope, op_expr, used, .r_value);
3435 return &op_node.base;
3436 },2738 },
3437 .Deref => {2739 .Deref => {
3438 const value_node = try transExpr(rp, scope, op_expr, used, .r_value);2740 const node = try transExpr(c, scope, op_expr, used);
3439 var is_ptr = false;2741 var is_ptr = false;
3440 const fn_ty = qualTypeGetFnProto(op_expr.getType(), &is_ptr);2742 const fn_ty = qualTypeGetFnProto(op_expr.getType(), &is_ptr);
3441 if (fn_ty != null and is_ptr)2743 if (fn_ty != null and is_ptr)
3442 return value_node;2744 return node;
3443 const unwrapped = try transCreateNodeUnwrapNull(rp.c, value_node);2745 const unwrapped = try Tag.unwrap.create(c.arena, node);
3444 return transCreateNodePtrDeref(rp.c, unwrapped);2746 return Tag.deref.create(c.arena, unwrapped);
3445 },2747 },
3446 .Plus => return transExpr(rp, scope, op_expr, used, .r_value),2748 .Plus => return transExpr(c, scope, op_expr, used),
3447 .Minus => {2749 .Minus => {
3448 if (!qualTypeHasWrappingOverflow(op_expr.getType())) {2750 if (!qualTypeHasWrappingOverflow(op_expr.getType())) {
3449 const op_node = try transCreateNodeSimplePrefixOp(rp.c, .Negation, .Minus, "-");2751 return Tag.negate.create(c.arena, try transExpr(c, scope, op_expr, .used));
3450 op_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
3451 return &op_node.base;
3452 } else if (cIsUnsignedInteger(op_expr.getType())) {2752 } else if (cIsUnsignedInteger(op_expr.getType())) {
3453 // we gotta emit 0 -% x2753 // use -% x for unsigned integers
3454 const zero = try transCreateNodeInt(rp.c, 0);2754 return Tag.negate_wrap.create(c.arena, try transExpr(c, scope, op_expr, .used));
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);
3458 } else2755 } 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", .{});
3460 },2757 },
3461 .Not => {2758 .Not => {
3462 const op_node = try transCreateNodeSimplePrefixOp(rp.c, .BitNot, .Tilde, "~");2759 return Tag.bit_not.create(c.arena, try transExpr(c, scope, op_expr, .used));
3463 op_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
3464 return &op_node.base;
3465 },2760 },
3466 .LNot => {2761 .LNot => {
3467 const op_node = try transCreateNodeSimplePrefixOp(rp.c, .BoolNot, .Bang, "!");2762 return Tag.not.create(c.arena, try transBoolExpr(c, scope, op_expr, .used));
3468 op_node.rhs = try transBoolExpr(rp, scope, op_expr, .used, .r_value, true);
3469 return &op_node.base;
3470 },2763 },
3471 .Extension => {2764 .Extension => {
3472 return transExpr(rp, scope, stmt.getSubExpr(), used, .l_value);2765 return transExpr(c, scope, stmt.getSubExpr(), used);
3473 },2766 },
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()}),
3475 }2768 }
3476}2769}
34772770
3478fn transCreatePreCrement(2771fn transCreatePreCrement(
3479 rp: RestorePoint,2772 c: *Context,
3480 scope: *Scope,2773 scope: *Scope,
3481 stmt: *const clang.UnaryOperator,2774 stmt: *const clang.UnaryOperator,
3482 op: ast.Node.Tag,2775 op: Tag,
3483 op_tok_id: std.zig.Token.Id,
3484 bytes: []const u8,
3485 used: ResultUsed,2776 used: ResultUsed,
3486) TransError!*ast.Node {2777) TransError!Node {
3487 const op_expr = stmt.getSubExpr();2778 const op_expr = stmt.getSubExpr();
34882779
3489 if (used == .unused) {2780 if (used == .unused) {
3490 // common case2781 // common case
3491 // c: ++expr2782 // c: ++expr
3492 // zig: expr += 12783 // zig: expr += 1
3493 const expr = try transExpr(rp, scope, op_expr, .used, .r_value);2784 const lhs = try transExpr(c, scope, op_expr, .used);
3494 const token = try appendToken(rp.c, op_tok_id, bytes);2785 const rhs = Tag.one_literal.init();
3495 const one = try transCreateNodeInt(rp.c, 1);2786 return transCreateNodeInfixOp(c, scope, op, lhs, rhs, .used);
3496 if (scope.id != .Condition)
3497 _ = try appendToken(rp.c, .Semicolon, ";");
3498 return transCreateNodeInfixOp(rp, scope, expr, op, token, one, .used, false);
3499 }2787 }
3500 // worst case2788 // worst case
3501 // c: ++expr2789 // c: ++expr
...@@ -3504,71 +2792,44 @@ fn transCreatePreCrement(...@@ -3504,71 +2792,44 @@ fn transCreatePreCrement(
3504 // zig: _ref.* += 1;2792 // zig: _ref.* += 1;
3505 // zig: break :blk _ref.*2793 // zig: break :blk _ref.*
3506 // zig: })2794 // zig: })
3507 var block_scope = try Scope.Block.init(rp.c, scope, true);2795 var block_scope = try Scope.Block.init(c, scope, true);
3508 defer block_scope.deinit();2796 defer block_scope.deinit();
3509 const ref = try block_scope.makeMangledName(rp.c, "ref");2797 const ref = try block_scope.makeMangledName(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);
35362798
3537 const break_node = try transCreateNodeBreak(rp.c, block_scope.label, ref_node);2799 const expr = try transExpr(c, &block_scope.base, op_expr, .used);
3538 try block_scope.statements.append(&break_node.base);2800 const addr_of = try Tag.address_of.create(c.arena, expr);
3539 const block_node = try block_scope.complete(rp.c);2801 const ref_decl = try Tag.var_simple.create(c.arena, .{ .name = ref, .init = addr_of });
3540 // semicolon must immediately follow rbrace because it is the last token in a block2802 try block_scope.statements.append(ref_decl);
3541 _ = try appendToken(rp.c, .Semicolon, ";");2803
3542 const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);2804 const lhs_node = try Tag.identifier.create(c.arena, ref);
3543 grouped_expr.* = .{2805 const ref_node = try Tag.deref.create(c.arena, lhs_node);
3544 .lparen = try appendToken(rp.c, .LParen, "("),2806 const node = try transCreateNodeInfixOp(c, &block_scope.base, op, ref_node, Tag.one_literal.init(), .used);
3545 .expr = block_node,2807 try block_scope.statements.append(node);
3546 .rparen = try appendToken(rp.c, .RParen, ")"),2808
3547 };2809 const break_node = try Tag.break_val.create(c.arena, .{
3548 return &grouped_expr.base;2810 .label = block_scope.label,
2811 .val = ref_node,
2812 });
2813 try block_scope.statements.append(break_node);
2814 return block_scope.complete(c);
3549}2815}
35502816
3551fn transCreatePostCrement(2817fn transCreatePostCrement(
3552 rp: RestorePoint,2818 c: *Context,
3553 scope: *Scope,2819 scope: *Scope,
3554 stmt: *const clang.UnaryOperator,2820 stmt: *const clang.UnaryOperator,
3555 op: ast.Node.Tag,2821 op: Tag,
3556 op_tok_id: std.zig.Token.Id,
3557 bytes: []const u8,
3558 used: ResultUsed,2822 used: ResultUsed,
3559) TransError!*ast.Node {2823) TransError!Node {
3560 const op_expr = stmt.getSubExpr();2824 const op_expr = stmt.getSubExpr();
35612825
3562 if (used == .unused) {2826 if (used == .unused) {
3563 // common case2827 // common case
3564 // c: ++expr2828 // c: expr++
3565 // zig: expr += 12829 // zig: expr += 1
3566 const expr = try transExpr(rp, scope, op_expr, .used, .r_value);2830 const lhs = try transExpr(c, scope, op_expr, .used);
3567 const token = try appendToken(rp.c, op_tok_id, bytes);2831 const rhs = Tag.one_literal.init();
3568 const one = try transCreateNodeInt(rp.c, 1);2832 return transCreateNodeInfixOp(c, scope, op, lhs, rhs, .used);
3569 if (scope.id != .Condition)
3570 _ = try appendToken(rp.c, .Semicolon, ";");
3571 return transCreateNodeInfixOp(rp, scope, expr, op, token, one, .used, false);
3572 }2833 }
3573 // worst case2834 // worst case
3574 // c: expr++2835 // c: expr++
...@@ -3578,93 +2839,56 @@ fn transCreatePostCrement(...@@ -3578,93 +2839,56 @@ fn transCreatePostCrement(
3578 // zig: _ref.* += 1;2839 // zig: _ref.* += 1;
3579 // zig: break :blk _tmp2840 // zig: break :blk _tmp
3580 // zig: })2841 // zig: })
3581 var block_scope = try Scope.Block.init(rp.c, scope, true);2842 var block_scope = try Scope.Block.init(c, scope, true);
3582 defer block_scope.deinit();2843 defer block_scope.deinit();
3583 const ref = try block_scope.makeMangledName(rp.c, "ref");2844 const ref = try block_scope.makeMangledName(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);
36212845
3622 const token = try appendToken(rp.c, op_tok_id, bytes);2846 const expr = try transExpr(c, &block_scope.base, op_expr, .used);
3623 const one = try transCreateNodeInt(rp.c, 1);2847 const addr_of = try Tag.address_of.create(c.arena, expr);
3624 _ = try appendToken(rp.c, .Semicolon, ";");2848 const ref_decl = try Tag.var_simple.create(c.arena, .{ .name = ref, .init = addr_of });
3625 const assign = try transCreateNodeInfixOp(rp, scope, ref_node, op, token, one, .used, false);2849 try block_scope.statements.append(ref_decl);
3626 try block_scope.statements.append(assign);
36272850
3628 const break_node = blk: {2851 const lhs_node = try Tag.identifier.create(c.arena, ref);
3629 var tmp_ctrl_flow = try CtrlFlow.initToken(rp.c, .Break, block_scope.label);2852 const ref_node = try Tag.deref.create(c.arena, lhs_node);
3630 const rhs = try transCreateNodeIdentifier(rp.c, tmp);2853
3631 break :blk try tmp_ctrl_flow.finish(rhs);2854 const tmp = try block_scope.makeMangledName(c, "tmp");
3632 };2855 const tmp_decl = try Tag.var_simple.create(c.arena, .{ .name = tmp, .init = ref_node });
3633 try block_scope.statements.append(&break_node.base);2856 try block_scope.statements.append(tmp_decl);
3634 _ = try appendToken(rp.c, .Semicolon, ";");2857
3635 const block_node = try block_scope.complete(rp.c);2858 const node = try transCreateNodeInfixOp(c, &block_scope.base, op, ref_node, Tag.one_literal.init(), .used);
3636 const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);2859 try block_scope.statements.append(node);
3637 grouped_expr.* = .{2860
3638 .lparen = try appendToken(rp.c, .LParen, "("),2861 const break_node = try Tag.break_val.create(c.arena, .{
3639 .expr = block_node,2862 .label = block_scope.label,
3640 .rparen = try appendToken(rp.c, .RParen, ")"),2863 .val = try Tag.identifier.create(c.arena, tmp),
3641 };2864 });
3642 return &grouped_expr.base;2865 try block_scope.statements.append(break_node);
2866 return block_scope.complete(c);
3643}2867}
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 {
3646 switch (stmt.getOpcode()) {2870 switch (stmt.getOpcode()) {
3647 .MulAssign => if (qualTypeHasWrappingOverflow(stmt.getType()))2871 .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)
3649 else2873 else
3650 return transCreateCompoundAssign(rp, scope, stmt, .AssignMul, .AsteriskEqual, "*=", .Mul, .Asterisk, "*", used),2874 return transCreateCompoundAssign(c, scope, stmt, .mul_assign, used),
3651 .AddAssign => if (qualTypeHasWrappingOverflow(stmt.getType()))2875 .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)
3653 else2877 else
3654 return transCreateCompoundAssign(rp, scope, stmt, .AssignAdd, .PlusEqual, "+=", .Add, .Plus, "+", used),2878 return transCreateCompoundAssign(c, scope, stmt, .add_assign, used),
3655 .SubAssign => if (qualTypeHasWrappingOverflow(stmt.getType()))2879 .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)
3657 else2881 else
3658 return transCreateCompoundAssign(rp, scope, stmt, .AssignSub, .MinusPercentEqual, "-=", .Sub, .Minus, "-", used),2882 return transCreateCompoundAssign(c, scope, stmt, .sub_assign, used),
3659 .DivAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignDiv, .SlashEqual, "/=", .Div, .Slash, "/", used),2883 .DivAssign => return transCreateCompoundAssign(c, scope, stmt, .div_assign, used),
3660 .RemAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignMod, .PercentEqual, "%=", .Mod, .Percent, "%", used),2884 .RemAssign => return transCreateCompoundAssign(c, scope, stmt, .mod_assign, used),
3661 .ShlAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitShiftLeft, .AngleBracketAngleBracketLeftEqual, "<<=", .BitShiftLeft, .AngleBracketAngleBracketLeft, "<<", used),2885 .ShlAssign => return transCreateCompoundAssign(c, scope, stmt, .shl_assign, used),
3662 .ShrAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitShiftRight, .AngleBracketAngleBracketRightEqual, ">>=", .BitShiftRight, .AngleBracketAngleBracketRight, ">>", used),2886 .ShrAssign => return transCreateCompoundAssign(c, scope, stmt, .shr_assign, used),
3663 .AndAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitAnd, .AmpersandEqual, "&=", .BitAnd, .Ampersand, "&", used),2887 .AndAssign => return transCreateCompoundAssign(c, scope, stmt, .bit_and_assign, used),
3664 .XorAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitXor, .CaretEqual, "^=", .BitXor, .Caret, "^", used),2888 .XorAssign => return transCreateCompoundAssign(c, scope, stmt, .bit_xor_assign, used),
3665 .OrAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitOr, .PipeEqual, "|=", .BitOr, .Pipe, "|", used),2889 .OrAssign => return transCreateCompoundAssign(c, scope, stmt, .bit_or_assign, used),
3666 else => return revertAndWarn(2890 else => return fail(
3667 rp,2891 c,
3668 error.UnsupportedTranslation,2892 error.UnsupportedTranslation,
3669 stmt.getBeginLoc(),2893 stmt.getBeginLoc(),
3670 "unsupported C translation {}",2894 "unsupported C translation {}",
...@@ -3674,25 +2898,20 @@ fn transCompoundAssignOperator(rp: RestorePoint, scope: *Scope, stmt: *const cla...@@ -3674,25 +2898,20 @@ fn transCompoundAssignOperator(rp: RestorePoint, scope: *Scope, stmt: *const cla
3674}2898}
36752899
3676fn transCreateCompoundAssign(2900fn transCreateCompoundAssign(
3677 rp: RestorePoint,2901 c: *Context,
3678 scope: *Scope,2902 scope: *Scope,
3679 stmt: *const clang.CompoundAssignOperator,2903 stmt: *const clang.CompoundAssignOperator,
3680 assign_op: ast.Node.Tag,2904 op: 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,
3686 used: ResultUsed,2905 used: ResultUsed,
3687) TransError!*ast.Node {2906) TransError!Node {
3688 const is_shift = bin_op == .BitShiftLeft or bin_op == .BitShiftRight;2907 const is_shift = op == .shl_assign or op == .shr_assign;
3689 const is_div = bin_op == .Div;2908 const is_div = op == .div_assign;
3690 const is_mod = bin_op == .Mod;2909 const is_mod = op == .mod_assign;
3691 const lhs = stmt.getLHS();2910 const lhs = stmt.getLHS();
3692 const rhs = stmt.getRHS();2911 const rhs = stmt.getRHS();
3693 const loc = stmt.getBeginLoc();2912 const loc = stmt.getBeginLoc();
3694 const lhs_qt = getExprQualType(rp.c, lhs);2913 const lhs_qt = getExprQualType(c, lhs);
3695 const rhs_qt = getExprQualType(rp.c, rhs);2914 const rhs_qt = getExprQualType(c, rhs);
3696 const is_signed = cIsSignedInteger(lhs_qt);2915 const is_signed = cIsSignedInteger(lhs_qt);
3697 const requires_int_cast = blk: {2916 const requires_int_cast = blk: {
3698 const are_integers = cIsInteger(lhs_qt) and cIsInteger(rhs_qt);2917 const are_integers = cIsInteger(lhs_qt) and cIsInteger(rhs_qt);
...@@ -3704,146 +2923,100 @@ fn transCreateCompoundAssign(...@@ -3704,146 +2923,100 @@ fn transCreateCompoundAssign(
3704 // c: lhs += rhs2923 // c: lhs += rhs
3705 // zig: lhs += rhs2924 // zig: lhs += rhs
3706 if ((is_mod or is_div) and is_signed) {2925 if ((is_mod or is_div) and is_signed) {
3707 const op_token = try appendToken(rp.c, .Equal, "=");2926 const lhs_node = try transExpr(c, scope, lhs, .used);
3708 const op_node = try rp.c.arena.create(ast.Node.SimpleInfixOp);2927 const rhs_node = try transExpr(c, scope, rhs, .used);
3709 const builtin = if (is_mod) "@rem" else "@divTrunc";2928 const builtin = if (is_mod)
3710 const builtin_node = try rp.c.createBuiltinCall(builtin, 2);2929 try Tag.rem.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node })
3711 const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value);2930 else
3712 builtin_node.params()[0] = lhs_node;2931 try Tag.div_trunc.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node });
3713 _ = try appendToken(rp.c, .Comma, ",");2932
3714 builtin_node.params()[1] = try transExpr(rp, scope, rhs, .used, .r_value);2933 return transCreateNodeInfixOp(c, scope, .assign, lhs_node, builtin, .used);
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;
3724 }2934 }
37252935
3726 const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value);2936 const lhs_node = try transExpr(c, scope, lhs, .used);
3727 const eq_token = try appendToken(rp.c, assign_tok_id, assign_bytes);
3728 var rhs_node = if (is_shift or requires_int_cast)2937 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)
3730 else2939 else
3731 try transExpr(rp, scope, rhs, .used, .r_value);2940 try transExpr(c, scope, rhs, .used);
37322941
3733 if (is_shift or requires_int_cast) {2942 if (is_shift or requires_int_cast) {
3734 const cast_node = try rp.c.createBuiltinCall("@intCast", 2);2943 // @intCast(rhs)
3735 const cast_to_type = if (is_shift)2944 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)
3737 else2946 else
3738 try transQualType(rp, getExprQualType(rp.c, lhs), loc);2947 try transQualType(c, scope, getExprQualType(c, lhs), loc);
3739 cast_node.params()[0] = cast_to_type;2948
3740 _ = try appendToken(rp.c, .Comma, ",");2949 rhs_node = try Tag.int_cast.create(c.arena, .{ .lhs = cast_to_type, .rhs = rhs_node });
3741 cast_node.params()[1] = rhs_node;
3742 cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
3743 rhs_node = &cast_node.base;
3744 }2950 }
3745 if (scope.id != .Condition)2951
3746 _ = try appendToken(rp.c, .Semicolon, ";");2952 return transCreateNodeInfixOp(c, scope, op, lhs_node, rhs_node, .used);
3747 return transCreateNodeInfixOp(rp, scope, lhs_node, assign_op, eq_token, rhs_node, .used, false);
3748 }2953 }
3749 // worst case2954 // worst case
3750 // c: lhs += rhs2955 // c: lhs += rhs
3751 // zig: (blk: {2956 // zig: (blk: {
3752 // zig: const _ref = &lhs;2957 // zig: const _ref = &lhs;
3753 // zig: _ref.* = _ref.* + rhs;2958 // zig: _ref.* += rhs;
3754 // zig: break :blk _ref.*2959 // zig: break :blk _ref.*
3755 // zig: })2960 // zig: })
3756 var block_scope = try Scope.Block.init(rp.c, scope, true);2961 var block_scope = try Scope.Block.init(c, scope, true);
3757 defer block_scope.deinit();2962 defer block_scope.deinit();
3758 const ref = try block_scope.makeMangledName(rp.c, "ref");2963 const ref = try block_scope.makeMangledName(c, "ref");
37592964
3760 const mut_tok = try appendToken(rp.c, .Keyword_const, "const");2965 const expr = try transExpr(c, &block_scope.base, lhs, .used);
3761 const name_tok = try appendIdentifier(rp.c, ref);2966 const addr_of = try Tag.address_of.create(c.arena, expr);
3762 const eq_token = try appendToken(rp.c, .Equal, "=");2967 const ref_decl = try Tag.var_simple.create(c.arena, .{ .name = ref, .init = addr_of });
3763 const addr_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&");2968 try block_scope.statements.append(ref_decl);
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);
37762969
3777 const lhs_node = try transCreateNodeIdentifier(rp.c, ref);2970 const lhs_node = try Tag.identifier.create(c.arena, ref);
3778 const ref_node = try transCreateNodePtrDeref(rp.c, lhs_node);2971 const ref_node = try Tag.deref.create(c.arena, lhs_node);
3779 _ = try appendToken(rp.c, .Semicolon, ";");
37802972
3781 if ((is_mod or is_div) and is_signed) {2973 if ((is_mod or is_div) and is_signed) {
3782 const op_token = try appendToken(rp.c, .Equal, "=");2974 const rhs_node = try transExpr(c, &block_scope.base, rhs, .used);
3783 const op_node = try rp.c.arena.create(ast.Node.SimpleInfixOp);2975 const builtin = if (is_mod)
3784 const builtin = if (is_mod) "@rem" else "@divTrunc";2976 try Tag.rem.create(c.arena, .{ .lhs = ref_node, .rhs = rhs_node })
3785 const builtin_node = try rp.c.createBuiltinCall(builtin, 2);2977 else
3786 builtin_node.params()[0] = try transCreateNodePtrDeref(rp.c, lhs_node);2978 try Tag.div_trunc.create(c.arena, .{ .lhs = ref_node, .rhs = rhs_node });
3787 _ = try appendToken(rp.c, .Comma, ",");2979
3788 builtin_node.params()[1] = try transExpr(rp, scope, rhs, .used, .r_value);2980 const assign = try transCreateNodeInfixOp(c, &block_scope.base, .assign, ref_node, builtin, .used);
3789 builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");2981 try block_scope.statements.append(assign);
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);
3799 } else {2982 } else {
3800 const bin_token = try appendToken(rp.c, bin_tok_id, bin_bytes);2983 var rhs_node = try transExpr(c, &block_scope.base, rhs, .used);
3801 var rhs_node = try transExpr(rp, scope, rhs, .used, .r_value);
38022984
3803 if (is_shift or requires_int_cast) {2985 if (is_shift or requires_int_cast) {
3804 const cast_node = try rp.c.createBuiltinCall("@intCast", 2);2986 // @intCast(rhs)
3805 const cast_to_type = if (is_shift)2987 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)
3807 else2989 else
3808 try transQualType(rp, getExprQualType(rp.c, lhs), loc);2990 try transQualType(c, scope, getExprQualType(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 }
38152991
3816 const rhs_bin = try transCreateNodeInfixOp(rp, scope, ref_node, bin_op, bin_token, rhs_node, .used, false);2992 rhs_node = try Tag.int_cast.create(c.arena, .{ .lhs = cast_to_type, .rhs = rhs_node });
3817 _ = try appendToken(rp.c, .Semicolon, ";");2993 }
38182994
3819 const ass_eq_token = try appendToken(rp.c, .Equal, "=");2995 const assign = try transCreateNodeInfixOp(c, &block_scope.base, op, ref_node, rhs_node, .used);
3820 const assign = try transCreateNodeInfixOp(rp, scope, ref_node, .Assign, ass_eq_token, rhs_bin, .used, false);
3821 try block_scope.statements.append(assign);2996 try block_scope.statements.append(assign);
3822 }2997 }
38232998
3824 const break_node = try transCreateNodeBreak(rp.c, block_scope.label, ref_node);2999 const break_node = try Tag.break_val.create(c.arena, .{
3825 try block_scope.statements.append(&break_node.base);3000 .label = block_scope.label,
3826 const block_node = try block_scope.complete(rp.c);3001 .val = ref_node,
3827 const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);3002 });
3828 grouped_expr.* = .{3003 try block_scope.statements.append(break_node);
3829 .lparen = try appendToken(rp.c, .LParen, "("),3004 return block_scope.complete(c);
3830 .expr = block_node,
3831 .rparen = try appendToken(rp.c, .RParen, ")"),
3832 };
3833 return &grouped_expr.base;
3834}3005}
38353006
3836fn transCPtrCast(3007fn transCPtrCast(
3837 rp: RestorePoint,3008 c: *Context,
3009 scope: *Scope,
3838 loc: clang.SourceLocation,3010 loc: clang.SourceLocation,
3839 dst_type: clang.QualType,3011 dst_type: clang.QualType,
3840 src_type: clang.QualType,3012 src_type: clang.QualType,
3841 expr: *ast.Node,3013 expr: Node,
3842) !*ast.Node {3014) !Node {
3843 const ty = dst_type.getTypePtr();3015 const ty = dst_type.getTypePtr();
3844 const child_type = ty.getPointeeType();3016 const child_type = ty.getPointeeType();
3845 const src_ty = src_type.getTypePtr();3017 const src_ty = src_type.getTypePtr();
3846 const src_child_type = src_ty.getPointeeType();3018 const src_child_type = src_ty.getPointeeType();
3019 const dst_type_node = try transType(c, scope, ty, loc);
38473020
3848 if ((src_child_type.isConstQualified() and3021 if ((src_child_type.isConstQualified() and
3849 !child_type.isConstQualified()) or3022 !child_type.isConstQualified()) or
...@@ -3851,80 +3024,47 @@ fn transCPtrCast(...@@ -3851,80 +3024,47 @@ fn transCPtrCast(
3851 !child_type.isVolatileQualified()))3024 !child_type.isVolatileQualified()))
3852 {3025 {
3853 // Casting away const or volatile requires us to use @intToPtr3026 // Casting away const or volatile requires us to use @intToPtr
3854 const inttoptr_node = try rp.c.createBuiltinCall("@intToPtr", 2);3027 const ptr_to_int = try Tag.ptr_to_int.create(c.arena, expr);
3855 const dst_type_node = try transType(rp, ty, loc);3028 const int_to_ptr = try Tag.int_to_ptr.create(c.arena, .{ .lhs = dst_type_node, .rhs = ptr_to_int });
3856 inttoptr_node.params()[0] = dst_type_node;3029 return int_to_ptr;
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;
3866 } else {3030 } else {
3867 // Implicit downcasting from higher to lower alignment values is forbidden,3031 // Implicit downcasting from higher to lower alignment values is forbidden,
3868 // use @alignCast to side-step this problem3032 // use @alignCast to side-step this problem
3869 const ptrcast_node = try rp.c.createBuiltinCall("@ptrCast", 2);3033 const rhs = if (qualTypeCanon(child_type).isVoidType())
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()) {
3875 // void has 1-byte alignment, so @alignCast is not needed3034 // void has 1-byte alignment, so @alignCast is not needed
3876 ptrcast_node.params()[1] = expr;3035 expr
3877 } else if (typeIsOpaque(rp.c, qualTypeCanon(child_type), loc)) {3036 else if (typeIsOpaque(c, qualTypeCanon(child_type), loc))
3878 // For opaque types a ptrCast is enough3037 // For opaque types a ptrCast is enough
3879 ptrcast_node.params()[1] = expr;3038 expr
3880 } else {3039 else blk: {
3881 const aligncast_node = try rp.c.createBuiltinCall("@alignCast", 2);3040 const child_type_node = try transQualType(c, scope, child_type, loc);
3882 const alignof_node = try rp.c.createBuiltinCall("@alignOf", 1);3041 const alignof = try Tag.alignof.create(c.arena, child_type_node);
3883 const child_type_node = try transQualType(rp, child_type, loc);3042 const align_cast = try Tag.align_cast.create(c.arena, .{ .lhs = alignof, .rhs = expr });
3884 alignof_node.params()[0] = child_type_node;3043 break :blk align_cast;
3885 alignof_node.rparen_token = try appendToken(rp.c, .RParen, ")");3044 };
3886 aligncast_node.params()[0] = &alignof_node.base;3045 return Tag.ptr_cast.create(c.arena, .{ .lhs = dst_type_node, .rhs = rhs });
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;
3895 }3046 }
3896}3047}
38973048
3898fn transBreak(rp: RestorePoint, scope: *Scope) TransError!*ast.Node {3049fn transFloatingLiteral(c: *Context, scope: *Scope, stmt: *const clang.FloatingLiteral, used: ResultUsed) TransError!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 {
3915 // TODO use something more accurate3050 // TODO use something more accurate
3916 const dbl = stmt.getValueAsApproximateDouble();3051 var dbl = stmt.getValueAsApproximateDouble();
3917 const node = try rp.c.arena.create(ast.Node.OneToken);3052 const is_negative = dbl < 0;
3918 node.* = .{3053 if (is_negative) dbl = -dbl;
3919 .base = .{ .tag = .FloatLiteral },3054 const str = try std.fmt.allocPrint(c.arena, "{d}", .{dbl});
3920 .token = try appendTokenFmt(rp.c, .FloatLiteral, "{d}", .{dbl}),3055 var node = if (dbl == std.math.floor(dbl))
3921 };3056 try Tag.integer_literal.create(c.arena, str)
3922 return maybeSuppressResult(rp, scope, used, &node.base);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);
3923}3061}
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 {
3926 // GNU extension of the ternary operator where the middle expression is3064 // GNU extension of the ternary operator where the middle expression is
3927 // omitted, the conditition itself is returned if it evaluates to true3065 // 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);
3928 const casted_stmt = @ptrCast(*const clang.AbstractConditionalOperator, stmt);3068 const casted_stmt = @ptrCast(*const clang.AbstractConditionalOperator, stmt);
3929 const cond_expr = casted_stmt.getCond();3069 const cond_expr = casted_stmt.getCond();
3930 const true_expr = casted_stmt.getTrueExpr();3070 const true_expr = casted_stmt.getTrueExpr();
...@@ -3935,184 +3075,149 @@ fn transBinaryConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const...@@ -3935,184 +3075,149 @@ fn transBinaryConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const
3935 // const _cond_temp = (cond_expr);3075 // const _cond_temp = (cond_expr);
3936 // break :blk if (_cond_temp) _cond_temp else (false_expr);3076 // break :blk if (_cond_temp) _cond_temp else (false_expr);
3937 // })3077 // })
3938 const lparen = try appendToken(rp.c, .LParen, "(");3078 var block_scope = try Scope.Block.init(c, scope, true);
3939
3940 var block_scope = try Scope.Block.init(rp.c, scope, true);
3941 defer block_scope.deinit();3079 defer block_scope.deinit();
39423080
3943 const mangled_name = try block_scope.makeMangledName(rp.c, "cond_temp");3081 const mangled_name = try block_scope.makeMangledName(c, "cond_temp");
3944 const mut_tok = try appendToken(rp.c, .Keyword_const, "const");3082 const init_node = try transExpr(c, &block_scope.base, cond_expr, .used);
3945 const name_tok = try appendIdentifier(rp.c, mangled_name);3083 const ref_decl = try Tag.var_simple.create(c.arena, .{ .name = mangled_name, .init = init_node });
3946 const eq_token = try appendToken(rp.c, .Equal, "=");3084 try block_scope.statements.append(ref_decl);
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);
39603085
3961 const if_node = try transCreateNodeIf(rp.c);
3962 var cond_scope = Scope.Condition{3086 var cond_scope = Scope.Condition{
3963 .base = .{3087 .base = .{
3964 .parent = &block_scope.base,3088 .parent = &block_scope.base,
3965 .id = .Condition,3089 .id = .condition,
3966 },3090 },
3967 };3091 };
3968 defer cond_scope.deinit();3092 defer cond_scope.deinit();
3969 const tmp_var_node = try transCreateNodeIdentifier(rp.c, mangled_name);3093
39703094 const cond_ident = try Tag.identifier.create(c.arena, mangled_name);
3971 const ty = getExprQualType(rp.c, cond_expr).getTypePtr();3095 const ty = getExprQualType(c, cond_expr).getTypePtr();
3972 const cond_node = try finishBoolExpr(rp, &cond_scope.base, cond_expr.getBeginLoc(), ty, tmp_var_node, used);3096 const cond_node = try finishBoolExpr(c, &cond_scope.base, cond_expr.getBeginLoc(), ty, cond_ident, .used);
3973 if_node.condition = cond_node;3097 var then_body = cond_ident;
3974 _ = try appendToken(rp.c, .RParen, ")");3098 if (!res_is_bool and isBoolRes(init_node)) {
39753099 then_body = try Tag.bool_to_int.create(c.arena, then_body);
3976 if_node.body = try transCreateNodeIdentifier(rp.c, mangled_name);3100 }
3977 if_node.@"else" = try transCreateNodeElse(rp.c);3101
3978 if_node.@"else".?.body = try transExpr(rp, &block_scope.base, false_expr, .used, .r_value);3102 var else_body = try transExpr(c, &block_scope.base, false_expr, .used);
3979 _ = try appendToken(rp.c, .Semicolon, ";");3103 if (!res_is_bool and isBoolRes(else_body)) {
39803104 else_body = try Tag.bool_to_int.create(c.arena, else_body);
3981 const break_node = try break_node_tmp.finish(&if_node.base);3105 }
3982 _ = try appendToken(rp.c, .Semicolon, ";");3106 const if_node = try Tag.@"if".create(c.arena, .{
3983 try block_scope.statements.append(&break_node.base);3107 .cond = cond_node,
3984 const block_node = try block_scope.complete(rp.c);3108 .then = then_body,
39853109 .@"else" = else_body,
3986 const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);3110 });
3987 grouped_expr.* = .{3111 const break_node = try Tag.break_val.create(c.arena, .{
3988 .lparen = lparen,3112 .label = block_scope.label,
3989 .expr = block_node,3113 .val = if_node,
3990 .rparen = try appendToken(rp.c, .RParen, ")"),3114 });
3991 };3115 try block_scope.statements.append(break_node);
3992 return maybeSuppressResult(rp, scope, used, &grouped_expr.base);3116 const res = try block_scope.complete(c);
3117 return maybeSuppressResult(c, scope, used, res);
3993}3118}
39943119
3995fn transConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const clang.ConditionalOperator, used: ResultUsed) TransError!*ast.Node {3120fn transConditionalOperator(c: *Context, scope: *Scope, stmt: *const clang.ConditionalOperator, used: ResultUsed) TransError!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);
3999 var cond_scope = Scope.Condition{3121 var cond_scope = Scope.Condition{
4000 .base = .{3122 .base = .{
4001 .parent = scope,3123 .parent = scope,
4002 .id = .Condition,3124 .id = .condition,
4003 },3125 },
4004 };3126 };
4005 defer cond_scope.deinit();3127 defer cond_scope.deinit();
40063128
3129 const qt = @ptrCast(*const clang.Expr, stmt).getType();
3130 const res_is_bool = qualTypeIsBoolean(qt);
4007 const casted_stmt = @ptrCast(*const clang.AbstractConditionalOperator, stmt);3131 const casted_stmt = @ptrCast(*const clang.AbstractConditionalOperator, stmt);
4008 const cond_expr = casted_stmt.getCond();3132 const cond_expr = casted_stmt.getCond();
4009 const true_expr = casted_stmt.getTrueExpr();3133 const true_expr = casted_stmt.getTrueExpr();
4010 const false_expr = casted_stmt.getFalseExpr();3134 const false_expr = casted_stmt.getFalseExpr();
40113135
4012 if_node.condition = try transBoolExpr(rp, &cond_scope.base, cond_expr, .used, .r_value, false);3136 const cond = try transBoolExpr(c, &cond_scope.base, cond_expr, .used);
4013 _ = try appendToken(rp.c, .RParen, ")");
4014
4015 if_node.body = try transExpr(rp, scope, true_expr, .used, .r_value);
40163137
4017 if_node.@"else" = try transCreateNodeElse(rp.c);3138 var then_body = try transExpr(c, scope, true_expr, used);
4018 if_node.@"else".?.body = try transExpr(rp, scope, false_expr, .used, .r_value);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) {3143 var else_body = try transExpr(c, scope, false_expr, used);
4021 const rparen = try appendToken(rp.c, .RParen, ")");3144 if (!res_is_bool and isBoolRes(else_body)) {
4022 const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);3145 else_body = try Tag.bool_to_int.create(c.arena, else_body);
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);
4031 }3146 }
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;
4032}3155}
40333156
4034fn maybeSuppressResult(3157fn maybeSuppressResult(
4035 rp: RestorePoint,3158 c: *Context,
4036 scope: *Scope,3159 scope: *Scope,
4037 used: ResultUsed,3160 used: ResultUsed,
4038 result: *ast.Node,3161 result: Node,
4039) TransError!*ast.Node {3162) TransError!Node {
4040 if (used == .used) return result;3163 if (used == .used) return result;
4041 if (scope.id != .Condition) {3164 return Tag.discard.create(c.arena, result);
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;
4061}3165}
40623166
4063fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: *ast.Node) !void {3167fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: Node) !void {
4064 try c.root_decls.append(c.gpa, decl_node);
4065 _ = try c.global_scope.sym_table.put(name, decl_node);3168 _ = try c.global_scope.sym_table.put(name, decl_node);
3169 try c.global_scope.nodes.append(decl_node);
4066}3170}
40673171
4068/// Translate a qual type for a variable with an initializer. The initializer3172/// Translate a qual type for a variable with an initializer. The initializer
4069/// only matters for incomplete arrays, since the size of the array is determined3173/// only matters for incomplete arrays, since the size of the array is determined
4070/// by the size of the initializer3174/// by the size of the initializer
4071fn transQualTypeInitialized(3175fn transQualTypeInitialized(
4072 rp: RestorePoint,3176 c: *Context,
3177 scope: *Scope,
4073 qt: clang.QualType,3178 qt: clang.QualType,
4074 decl_init: *const clang.Expr,3179 decl_init: *const clang.Expr,
4075 source_loc: clang.SourceLocation,3180 source_loc: clang.SourceLocation,
4076) TypeError!*ast.Node {3181) TypeError!Node {
4077 const ty = qt.getTypePtr();3182 const ty = qt.getTypePtr();
4078 if (ty.getTypeClass() == .IncompleteArray) {3183 if (ty.getTypeClass() == .IncompleteArray) {
4079 const incomplete_array_ty = @ptrCast(*const clang.IncompleteArrayType, ty);3184 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
4082 switch (decl_init.getStmtClass()) {3187 switch (decl_init.getStmtClass()) {
4083 .StringLiteralClass => {3188 .StringLiteralClass => {
4084 const string_lit = @ptrCast(*const clang.StringLiteral, decl_init);3189 const string_lit = @ptrCast(*const clang.StringLiteral, decl_init);
4085 const string_lit_size = string_lit.getLength() + 1; // +1 for null terminator3190 const string_lit_size = string_lit.getLength() + 1; // +1 for null terminator
4086 const array_size = @intCast(usize, string_lit_size);3191 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 });
4088 },3193 },
4089 .InitListExprClass => {3194 .InitListExprClass => {
4090 const init_expr = @ptrCast(*const clang.InitListExpr, decl_init);3195 const init_expr = @ptrCast(*const clang.InitListExpr, decl_init);
4091 const size = init_expr.getNumInits();3196 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 });
4093 },3198 },
4094 else => {},3199 else => {},
4095 }3200 }
4096 }3201 }
4097 return transQualType(rp, qt, source_loc);3202 return transQualType(c, scope, qt, source_loc);
4098}3203}
40993204
4100fn transQualType(rp: RestorePoint, qt: clang.QualType, source_loc: clang.SourceLocation) TypeError!*ast.Node {3205fn transQualType(c: *Context, scope: *Scope, qt: clang.QualType, source_loc: clang.SourceLocation) TypeError!Node {
4101 return transType(rp, qt.getTypePtr(), source_loc);3206 return transType(c, scope, qt.getTypePtr(), source_loc);
4102}3207}
41033208
4104/// Produces a Zig AST node by translating a Clang QualType, respecting the width, but modifying the signed-ness.3209/// Produces a Zig AST node by translating a Clang QualType, respecting the width, but modifying the signed-ness.
4105/// Asserts the type is an integer.3210/// 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 {
4107 return transTypeIntWidthOf(c, qualTypeCanon(ty), is_signed);3212 return transTypeIntWidthOf(c, qualTypeCanon(ty), is_signed);
4108}3213}
41093214
4110/// Produces a Zig AST node by translating a Clang Type, respecting the width, but modifying the signed-ness.3215/// Produces a Zig AST node by translating a Clang Type, respecting the width, but modifying the signed-ness.
4111/// Asserts the type is an integer.3216/// 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 {
4113 assert(ty.getTypeClass() == .Builtin);3218 assert(ty.getTypeClass() == .Builtin);
4114 const builtin_ty = @ptrCast(*const clang.BuiltinType, ty);3219 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()) {
4116 .Char_U, .Char_S, .UChar, .SChar, .Char8 => if (is_signed) "i8" else "u8",3221 .Char_U, .Char_S, .UChar, .SChar, .Char8 => if (is_signed) "i8" else "u8",
4117 .UShort, .Short => if (is_signed) "c_short" else "c_ushort",3222 .UShort, .Short => if (is_signed) "c_short" else "c_ushort",
4118 .UInt, .Int => if (is_signed) "c_int" else "c_uint",3223 .UInt, .Int => if (is_signed) "c_int" else "c_uint",
...@@ -4141,7 +3246,7 @@ fn qualTypeIsBoolean(qt: clang.QualType) bool {...@@ -4141,7 +3246,7 @@ fn qualTypeIsBoolean(qt: clang.QualType) bool {
4141 return qualTypeCanon(qt).isBooleanType();3246 return qualTypeCanon(qt).isBooleanType();
4142}3247}
41433248
4144fn qualTypeIntBitWidth(rp: RestorePoint, qt: clang.QualType, source_loc: clang.SourceLocation) !u32 {3249fn qualTypeIntBitWidth(c: *Context, qt: clang.QualType) !u32 {
4145 const ty = qt.getTypePtr();3250 const ty = qt.getTypePtr();
41463251
4147 switch (ty.getTypeClass()) {3252 switch (ty.getTypeClass()) {
...@@ -4165,7 +3270,7 @@ fn qualTypeIntBitWidth(rp: RestorePoint, qt: clang.QualType, source_loc: clang.S...@@ -4165,7 +3270,7 @@ fn qualTypeIntBitWidth(rp: RestorePoint, qt: clang.QualType, source_loc: clang.S
4165 .Typedef => {3270 .Typedef => {
4166 const typedef_ty = @ptrCast(*const clang.TypedefType, ty);3271 const typedef_ty = @ptrCast(*const clang.TypedefType, ty);
4167 const typedef_decl = typedef_ty.getDecl();3272 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
4170 if (mem.eql(u8, type_name, "uint8_t") or mem.eql(u8, type_name, "int8_t")) {3275 if (mem.eql(u8, type_name, "uint8_t") or mem.eql(u8, type_name, "int8_t")) {
4171 return 8;3276 return 8;
...@@ -4181,55 +3286,19 @@ fn qualTypeIntBitWidth(rp: RestorePoint, qt: clang.QualType, source_loc: clang.S...@@ -4181,55 +3286,19 @@ fn qualTypeIntBitWidth(rp: RestorePoint, qt: clang.QualType, source_loc: clang.S
4181 },3286 },
4182 else => return 0,3287 else => return 0,
4183 }3288 }
4184
4185 unreachable;
4186}3289}
41873290
4188fn qualTypeToLog2IntRef(rp: RestorePoint, qt: clang.QualType, source_loc: clang.SourceLocation) !*ast.Node {3291fn qualTypeToLog2IntRef(c: *Context, scope: *Scope, qt: clang.QualType, source_loc: clang.SourceLocation) !Node {
4189 const int_bit_width = try qualTypeIntBitWidth(rp, qt, source_loc);3292 const int_bit_width = try qualTypeIntBitWidth(c, qt);
41903293
4191 if (int_bit_width != 0) {3294 if (int_bit_width != 0) {
4192 // we can perform the log2 now.3295 // we can perform the log2 now.
4193 const cast_bit_width = math.log2_int(u64, int_bit_width);3296 const cast_bit_width = math.log2_int(u64, int_bit_width);
4194 const node = try rp.c.arena.create(ast.Node.OneToken);3297 return Tag.log2_int_type.create(c.arena, cast_bit_width);
4195 node.* = .{3298 }
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, ")");
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);
4233}3302}
42343303
4235fn qualTypeChildIsFnProto(qt: clang.QualType) bool {3304fn qualTypeChildIsFnProto(qt: clang.QualType) bool {
...@@ -4393,28 +3462,22 @@ fn cIsLongLongInteger(qt: clang.QualType) bool {...@@ -4393,28 +3462,22 @@ fn cIsLongLongInteger(qt: clang.QualType) bool {
4393 };3462 };
4394}3463}
4395fn transCreateNodeAssign(3464fn transCreateNodeAssign(
4396 rp: RestorePoint,3465 c: *Context,
4397 scope: *Scope,3466 scope: *Scope,
4398 result_used: ResultUsed,3467 result_used: ResultUsed,
4399 lhs: *const clang.Expr,3468 lhs: *const clang.Expr,
4400 rhs: *const clang.Expr,3469 rhs: *const clang.Expr,
4401) !*ast.Node {3470) !Node {
4402 // common case3471 // common case
4403 // c: lhs = rhs3472 // c: lhs = rhs
4404 // zig: lhs = rhs3473 // zig: lhs = rhs
4405 if (result_used == .unused) {3474 if (result_used == .unused) {
4406 const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value);3475 const lhs_node = try transExpr(c, scope, lhs, .used);
4407 const eq_token = try appendToken(rp.c, .Equal, "=");3476 var rhs_node = try transExprCoercing(c, scope, rhs, .used);
4408 var rhs_node = try transExprCoercing(rp, scope, rhs, .used, .r_value);
4409 if (!exprIsBooleanType(lhs) and isBoolRes(rhs_node)) {3477 if (!exprIsBooleanType(lhs) and isBoolRes(rhs_node)) {
4410 const builtin_node = try rp.c.createBuiltinCall("@boolToInt", 1);3478 rhs_node = try Tag.bool_to_int.create(c.arena, rhs_node);
4411 builtin_node.params()[0] = rhs_node;
4412 builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
4413 rhs_node = &builtin_node.base;
4414 }3479 }
4415 if (scope.id != .Condition)3480 return transCreateNodeInfixOp(c, scope, .assign, lhs_node, rhs_node, .used);
4416 _ = try appendToken(rp.c, .Semicolon, ";");
4417 return transCreateNodeInfixOp(rp, scope, lhs_node, .Assign, eq_token, rhs_node, .used, false);
4418 }3481 }
44193482
4420 // worst case3483 // worst case
...@@ -4424,176 +3487,62 @@ fn transCreateNodeAssign(...@@ -4424,176 +3487,62 @@ fn transCreateNodeAssign(
4424 // zig: lhs = _tmp;3487 // zig: lhs = _tmp;
4425 // zig: break :blk _tmp3488 // zig: break :blk _tmp
4426 // zig: })3489 // zig: })
4427 var block_scope = try Scope.Block.init(rp.c, scope, true);3490 var block_scope = try Scope.Block.init(c, scope, true);
4428 defer block_scope.deinit();3491 defer block_scope.deinit();
44293492
4430 const tmp = try block_scope.makeMangledName(rp.c, "tmp");3493 const tmp = try block_scope.makeMangledName(c, "tmp");
4431 const mut_tok = try appendToken(rp.c, .Keyword_const, "const");3494 const rhs_node = try transExpr(c, &block_scope.base, rhs, .used);
4432 const name_tok = try appendIdentifier(rp.c, tmp);3495 const tmp_decl = try Tag.var_simple.create(c.arena, .{ .name = tmp, .init = rhs_node });
4433 const eq_token = try appendToken(rp.c, .Equal, "=");3496 try block_scope.statements.append(tmp_decl);
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, ";");
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);
4459 try block_scope.statements.append(assign);3501 try block_scope.statements.append(assign);
44603502
4461 const break_node = blk: {3503 const break_node = try Tag.break_val.create(c.arena, .{
4462 var tmp_ctrl_flow = try CtrlFlow.init(rp.c, .Break, tokenSlice(rp.c, block_scope.label.?));3504 .label = block_scope.label,
4463 const rhs_expr = try transCreateNodeIdentifier(rp.c, tmp);3505 .val = tmp_ident,
4464 break :blk try tmp_ctrl_flow.finish(rhs_expr);3506 });
4465 };3507 try block_scope.statements.append(break_node);
4466 _ = try appendToken(rp.c, .Semicolon, ";");3508 return block_scope.complete(c);
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;
4498}3509}
44993510
4500fn transCreateNodeInfixOp(3511fn transCreateNodeInfixOp(
4501 rp: RestorePoint,3512 c: *Context,
4502 scope: *Scope,3513 scope: *Scope,
4503 lhs_node: *ast.Node,3514 op: Tag,
4504 op: ast.Node.Tag,3515 lhs: Node,
4505 op_token: ast.TokenIndex,3516 rhs: Node,
4506 rhs_node: *ast.Node,
4507 used: ResultUsed,3517 used: ResultUsed,
4508 grouped: bool,3518) !Node {
4509) !*ast.Node {3519 const payload = try c.arena.create(ast.Payload.BinOp);
4510 var lparen = if (grouped)3520 payload.* = .{
4511 try appendToken(rp.c, .LParen, "(")
4512 else
4513 null;
4514 const node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
4515 node.* = .{
4516 .base = .{ .tag = op },3521 .base = .{ .tag = op },
4517 .op_token = op_token,3522 .data = .{
4518 .lhs = lhs_node,3523 .lhs = lhs,
4519 .rhs = rhs_node,3524 .rhs = rhs,
4520 };3525 },
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,
4528 };3526 };
4529 return maybeSuppressResult(rp, scope, used, &grouped_expr.base);3527 return maybeSuppressResult(c, scope, used, Node.initPayload(&payload.base));
4530}3528}
45313529
4532fn transCreateNodeBoolInfixOp(3530fn transCreateNodeBoolInfixOp(
4533 rp: RestorePoint,3531 c: *Context,
4534 scope: *Scope,3532 scope: *Scope,
4535 stmt: *const clang.BinaryOperator,3533 stmt: *const clang.BinaryOperator,
4536 op: ast.Node.Tag,3534 op: Tag,
4537 used: ResultUsed,3535 used: ResultUsed,
4538 grouped: bool,3536) !Node {
4539) !*ast.Node {3537 std.debug.assert(op == .@"and" or op == .@"or");
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);
45483538
4549 return transCreateNodeInfixOp(3539 const lhs = try transBoolExpr(c, scope, stmt.getLHS(), .used);
4550 rp,3540 const rhs = try transBoolExpr(c, scope, stmt.getRHS(), .used);
4551 scope,
4552 lhs_hode,
4553 op,
4554 op_token,
4555 rhs,
4556 used,
4557 grouped,
4558 );
4559}
45603541
4561fn transCreateNodePtrType(3542 return transCreateNodeInfixOp(c, scope, op, lhs, rhs, used);
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;
4594}3543}
45953544
4596fn transCreateNodeAPInt(c: *Context, int: *const clang.APSInt) !*ast.Node {3545fn transCreateNodeAPInt(c: *Context, int: *const clang.APSInt) !Node {
4597 const num_limbs = math.cast(usize, int.getNumWords()) catch |err| switch (err) {3546 const num_limbs = math.cast(usize, int.getNumWords()) catch |err| switch (err) {
4598 error.Overflow => return error.OutOfMemory,3547 error.Overflow => return error.OutOfMemory,
4599 };3548 };
...@@ -4629,418 +3578,96 @@ fn transCreateNodeAPInt(c: *Context, int: *const clang.APSInt) !*ast.Node {...@@ -4629,418 +3578,96 @@ fn transCreateNodeAPInt(c: *Context, int: *const clang.APSInt) !*ast.Node {
4629 else => @compileError("unimplemented"),3578 else => @compileError("unimplemented"),
4630 }3579 }
46313580
4632 const big: math.big.int.Const = .{ .limbs = limbs, .positive = !is_negative };3581 const big: math.big.int.Const = .{ .limbs = limbs, .positive = true };
4633 const str = big.toStringAlloc(c.arena, 10, false) catch |err| switch (err) {3582 const str = big.toStringAlloc(c.arena, 10, false) catch |err| switch (err) {
4634 error.OutOfMemory => return error.OutOfMemory,3583 error.OutOfMemory => return error.OutOfMemory,
4635 };3584 };
4636 defer c.arena.free(str);3585 const res = try Tag.integer_literal.create(c.arena, str);
4637 const token = try appendToken(c, .IntegerLiteral, str);3586 if (is_negative) return Tag.negate.create(c.arena, res);
4638 const node = try c.arena.create(ast.Node.OneToken);3587 return res;
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;
4664}3588}
46653589
4666fn transCreateNodeBoolLiteral(c: *Context, value: bool) !*ast.Node {3590fn transCreateNodeNumber(c: *Context, num: anytype, num_kind: enum { int, float }) !Node {
4667 const token = if (value)3591 const fmt_s = if (comptime std.meta.trait.isNumber(@TypeOf(num))) "{d}" else "{s}";
4668 try appendToken(c, .Keyword_true, "true")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)
4669 else3595 else
4670 try appendToken(c, .Keyword_false, "false");3596 return Tag.integer_literal.create(c.arena, str);
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;
4688}3597}
46893598
4690fn transCreateNodeFloat(c: *Context, str: []const u8) !*ast.Node {3599fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: Node, proto_alias: *ast.Payload.Func) !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 {
4716 const scope = &c.global_scope.base;3600 const scope = &c.global_scope.base;
47173601
4718 const pub_tok = try appendToken(c, .Keyword_pub, "pub");3602 var fn_params = std.ArrayList(ast.Payload.Param).init(c.gpa);
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);
4724 defer fn_params.deinit();3603 defer fn_params.deinit();
47253604
4726 for (proto_alias.params()) |param, i| {3605 for (proto_alias.data.params) |param, i| {
4727 if (i != 0) {3606 const param_name = param.name orelse
4728 _ = try appendToken(c, .Comma, ",");3607 try std.fmt.allocPrint(c.arena, "arg_{d}", .{c.getMangle()});
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, ":");
47343608
4735 (try fn_params.addOne()).* = .{3609 try fn_params.append(.{
4736 .doc_comments = null,3610 .name = param_name,
4737 .comptime_token = null,3611 .type = param.type,
4738 .noalias_token = param.noalias_token,3612 .is_noalias = param.is_noalias,
4739 .name_token = param_name_tok,3613 });
4740 .param_type = param.param_type,
4741 };
4742 }3614 }
47433615
4744 _ = try appendToken(c, .RParen, ")");3616 const init = if (ref.castTag(.var_decl)) |v|
47453617 v.data.init.?
4746 _ = try appendToken(c, .Keyword_callconv, "callconv");3618 else if (ref.castTag(.var_simple) orelse ref.castTag(.pub_var_simple)) |v|
4747 _ = try appendToken(c, .LParen, "(");3619 v.data.init
4748 const callconv_expr = try transCreateNodeEnumLiteral(c, "Inline");3620 else
4749 _ = try appendToken(c, .RParen, ")");3621 unreachable;
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();
47583622
3623 const unwrap_expr = try Tag.unwrap.create(c.arena, init);
3624 const args = try c.arena.alloc(Node, fn_params.items.len);
4759 for (fn_params.items) |param, i| {3625 for (fn_params.items) |param, i| {
4760 if (i != 0) {3626 args[i] = try Tag.identifier.create(c.arena, param.name.?);
4761 _ = try appendToken(c, .Comma, ",");
4762 }
4763 call_params[i] = try transCreateNodeIdentifier(c, tokenSlice(c, param.name_token.?));
4764 }3627 }
4765 call_expr.rtoken = try appendToken(c, .RParen, ")");3628 const call_expr = try Tag.call.create(c.arena, .{
47663629 .lhs = unwrap_expr,
4767 const return_expr = try ast.Node.ControlFlowExpression.create(c.arena, .{3630 .args = args,
4768 .ltoken = return_kw,
4769 .tag = .Return,
4770 }, .{
4771 .rhs = &call_expr.base,
4772 });3631 });
4773 _ = try appendToken(c, .Semicolon, ";");3632 const return_expr = try Tag.@"return".create(c.arena, call_expr);
47743633 const block = try Tag.block_single.create(c.arena, return_expr);
4775 const block = try ast.Node.Block.alloc(c.arena, 1);3634
4776 block.* = .{3635 return Tag.pub_inline_fn.create(c.arena, .{
4777 .lbrace = block_lbrace,3636 .name = name,
4778 .statements_len = 1,3637 .params = try c.arena.dupe(ast.Payload.Param, fn_params.items),
4779 .rbrace = try appendToken(c, .RBrace, "}"),3638 .return_type = proto_alias.data.return_type,
4780 };3639 .body = block,
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,
4792 });3640 });
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;
4959}3641}
49603642
4961fn transCreateNodeShiftOp(3643fn transCreateNodeShiftOp(
4962 rp: RestorePoint,3644 c: *Context,
4963 scope: *Scope,3645 scope: *Scope,
4964 stmt: *const clang.BinaryOperator,3646 stmt: *const clang.BinaryOperator,
4965 op: ast.Node.Tag,3647 op: Tag,
4966 op_tok_id: std.zig.Token.Id,3648 used: ResultUsed,
4967 bytes: []const u8,3649) !Node {
4968) !*ast.Node {3650 std.debug.assert(op == .shl or op == .shr);
4969 std.debug.assert(op == .BitShiftLeft or op == .BitShiftRight);
49703651
4971 const lhs_expr = stmt.getLHS();3652 const lhs_expr = stmt.getLHS();
4972 const rhs_expr = stmt.getRHS();3653 const rhs_expr = stmt.getRHS();
4973 const rhs_location = rhs_expr.getBeginLoc();3654 const rhs_location = rhs_expr.getBeginLoc();
4974 // lhs >> @as(u5, rh)3655 // lhs >> @as(u5, rh)
49753656
4976 const lhs = try transExpr(rp, scope, lhs_expr, .used, .l_value);3657 const lhs = try transExpr(c, scope, lhs_expr, .used);
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 };
49943658
4995 return &node.base;3659 const rhs_type = try qualTypeToLog2IntRef(c, scope, stmt.getType(), rhs_location);
4996}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 {3663 return transCreateNodeInfixOp(c, scope, op, lhs, rhs_casted, used);
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;
5006}3664}
50073665
5008fn transCreateNodeArrayAccess(c: *Context, lhs: *ast.Node) !*ast.Node.ArrayAccess {3666fn transType(c: *Context, scope: *Scope, ty: *const clang.Type, source_loc: clang.SourceLocation) TypeError!Node {
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 {
5040 switch (ty.getTypeClass()) {3667 switch (ty.getTypeClass()) {
5041 .Builtin => {3668 .Builtin => {
5042 const builtin_ty = @ptrCast(*const clang.BuiltinType, ty);3669 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()) {
5044 .Void => "c_void",3671 .Void => "c_void",
5045 .Bool => "bool",3672 .Bool => "bool",
5046 .Char_U, .UChar, .Char_S, .Char8 => "u8",3673 .Char_U, .UChar, .Char_S, .Char8 => "u8",
...@@ -5060,112 +3687,115 @@ fn transType(rp: RestorePoint, ty: *const clang.Type, source_loc: clang.SourceLo...@@ -5060,112 +3687,115 @@ fn transType(rp: RestorePoint, ty: *const clang.Type, source_loc: clang.SourceLo
5060 .Float128 => "f128",3687 .Float128 => "f128",
5061 .Float16 => "f16",3688 .Float16 => "f16",
5062 .LongDouble => "c_longdouble",3689 .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", .{}),
5064 });3691 });
5065 },3692 },
5066 .FunctionProto => {3693 .FunctionProto => {
5067 const fn_proto_ty = @ptrCast(*const clang.FunctionProtoType, ty);3694 const fn_proto_ty = @ptrCast(*const clang.FunctionProtoType, ty);
5068 const fn_proto = try transFnProto(rp, null, fn_proto_ty, source_loc, null, false);3695 const fn_proto = try transFnProto(c, null, fn_proto_ty, source_loc, null, false);
5069 return &fn_proto.base;3696 return Node.initPayload(&fn_proto.base);
5070 },3697 },
5071 .FunctionNoProto => {3698 .FunctionNoProto => {
5072 const fn_no_proto_ty = @ptrCast(*const clang.FunctionType, ty);3699 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);3700 const fn_proto = try transFnNoProto(c, fn_no_proto_ty, source_loc, null, false);
5074 return &fn_proto.base;3701 return Node.initPayload(&fn_proto.base);
5075 },3702 },
5076 .Paren => {3703 .Paren => {
5077 const paren_ty = @ptrCast(*const clang.ParenType, ty);3704 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);
5079 },3706 },
5080 .Pointer => {3707 .Pointer => {
5081 const child_qt = ty.getPointeeType();3708 const child_qt = ty.getPointeeType();
5082 if (qualTypeChildIsFnProto(child_qt)) {3709 if (qualTypeChildIsFnProto(child_qt)) {
5083 const optional_node = try transCreateNodeSimplePrefixOp(rp.c, .OptionalType, .QuestionMark, "?");3710 return Tag.optional_type.create(c.arena, try transQualType(c, scope, child_qt, source_loc));
5084 optional_node.rhs = try transQualType(rp, child_qt, source_loc);
5085 return &optional_node.base;
5086 }3711 }
5087 if (typeIsOpaque(rp.c, child_qt.getTypePtr(), source_loc) or qualTypeWasDemotedToOpaque(rp.c, child_qt)) {3712 const is_const = child_qt.isConstQualified();
5088 const optional_node = try transCreateNodeSimplePrefixOp(rp.c, .OptionalType, .QuestionMark, "?");3713 const is_volatile = child_qt.isVolatileQualified();
5089 const pointer_node = try transCreateNodePtrType(3714 const elem_type = try transQualType(c, scope, child_qt, source_loc);
5090 rp.c,3715 if (typeIsOpaque(c, child_qt.getTypePtr(), source_loc) or qualTypeWasDemotedToOpaque(c, child_qt)) {
5091 child_qt.isConstQualified(),3716 const ptr = try Tag.single_pointer.create(c.arena, .{ .is_const = is_const, .is_volatile = is_volatile, .elem_type = elem_type });
5092 child_qt.isVolatileQualified(),3717 return Tag.optional_type.create(c.arena, ptr);
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;
5098 }3718 }
5099 const pointer_node = try transCreateNodePtrType(3719
5100 rp.c,3720 return Tag.c_pointer.create(c.arena, .{ .is_const = is_const, .is_volatile = is_volatile, .elem_type = elem_type });
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;
5107 },3721 },
5108 .ConstantArray => {3722 .ConstantArray => {
5109 const const_arr_ty = @ptrCast(*const clang.ConstantArrayType, ty);3723 const const_arr_ty = @ptrCast(*const clang.ConstantArrayType, ty);
51103724
5111 const size_ap_int = const_arr_ty.getSize();3725 const size_ap_int = const_arr_ty.getSize();
5112 const size = size_ap_int.getLimitedValue(math.maxInt(usize));3726 const size = size_ap_int.getLimitedValue(math.maxInt(usize));
5113 const elem_ty = const_arr_ty.getElementType().getTypePtr();3727 const elem_type = try transType(c, scope, const_arr_ty.getElementType().getTypePtr(), source_loc);
5114 return try transCreateNodeArrayType(rp, source_loc, elem_ty, size);3728
3729 return Tag.array_type.create(c.arena, .{ .len = size, .elem_type = elem_type });
5115 },3730 },
5116 .IncompleteArray => {3731 .IncompleteArray => {
5117 const incomplete_array_ty = @ptrCast(*const clang.IncompleteArrayType, ty);3732 const incomplete_array_ty = @ptrCast(*const clang.IncompleteArrayType, ty);
51183733
5119 const child_qt = incomplete_array_ty.getElementType();3734 const child_qt = incomplete_array_ty.getElementType();
5120 var node = try transCreateNodePtrType(3735 const is_const = child_qt.isConstQualified();
5121 rp.c,3736 const is_volatile = child_qt.isVolatileQualified();
5122 child_qt.isConstQualified(),3737 const elem_type = try transQualType(c, scope, child_qt, source_loc);
5123 child_qt.isVolatileQualified(),3738
5124 .Identifier,3739 return Tag.c_pointer.create(c.arena, .{ .is_const = is_const, .is_volatile = is_volatile, .elem_type = elem_type });
5125 );
5126 node.rhs = try transQualType(rp, child_qt, source_loc);
5127 return &node.base;
5128 },3740 },
5129 .Typedef => {3741 .Typedef => {
5130 const typedef_ty = @ptrCast(*const clang.TypedefType, ty);3742 const typedef_ty = @ptrCast(*const clang.TypedefType, ty);
51313743
5132 const typedef_decl = typedef_ty.getDecl();3744 const typedef_decl = typedef_ty.getDecl();
5133 return (try transTypeDef(rp.c, typedef_decl, false)) orelse3745 var trans_scope = scope;
5134 revertAndWarn(rp, error.UnsupportedType, source_loc, "unable to translate typedef declaration", .{});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);
5135 },3753 },
5136 .Record => {3754 .Record => {
5137 const record_ty = @ptrCast(*const clang.RecordType, ty);3755 const record_ty = @ptrCast(*const clang.RecordType, ty);
51383756
5139 const record_decl = record_ty.getDecl();3757 const record_decl = record_ty.getDecl();
5140 return (try transRecordDecl(rp.c, record_decl)) orelse3758 var trans_scope = scope;
5141 revertAndWarn(rp, error.UnsupportedType, source_loc, "unable to resolve record declaration", .{});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);
5142 },3766 },
5143 .Enum => {3767 .Enum => {
5144 const enum_ty = @ptrCast(*const clang.EnumType, ty);3768 const enum_ty = @ptrCast(*const clang.EnumType, ty);
51453769
5146 const enum_decl = enum_ty.getDecl();3770 const enum_decl = enum_ty.getDecl();
5147 return (try transEnumDecl(rp.c, enum_decl)) orelse3771 var trans_scope = scope;
5148 revertAndWarn(rp, error.UnsupportedType, source_loc, "unable to translate enum declaration", .{});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);
5149 },3779 },
5150 .Elaborated => {3780 .Elaborated => {
5151 const elaborated_ty = @ptrCast(*const clang.ElaboratedType, ty);3781 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);
5153 },3783 },
5154 .Decayed => {3784 .Decayed => {
5155 const decayed_ty = @ptrCast(*const clang.DecayedType, ty);3785 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);
5157 },3787 },
5158 .Attributed => {3788 .Attributed => {
5159 const attributed_ty = @ptrCast(*const clang.AttributedType, ty);3789 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);
5161 },3791 },
5162 .MacroQualified => {3792 .MacroQualified => {
5163 const macroqualified_ty = @ptrCast(*const clang.MacroQualifiedType, ty);3793 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);
5165 },3795 },
5166 else => {3796 else => {
5167 const type_name = rp.c.str(ty.getTypeClassName());3797 const type_name = c.str(ty.getTypeClassName());
5168 return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported type: '{s}'", .{type_name});3798 return fail(c, error.UnsupportedType, source_loc, "unsupported type: '{s}'", .{type_name});
5169 },3799 },
5170 }3800 }
5171}3801}
...@@ -5231,7 +3861,7 @@ const FnDeclContext = struct {...@@ -5231,7 +3861,7 @@ const FnDeclContext = struct {
5231};3861};
52323862
5233fn transCC(3863fn transCC(
5234 rp: RestorePoint,3864 c: *Context,
5235 fn_ty: *const clang.FunctionType,3865 fn_ty: *const clang.FunctionType,
5236 source_loc: clang.SourceLocation,3866 source_loc: clang.SourceLocation,
5237) !CallingConvention {3867) !CallingConvention {
...@@ -5244,8 +3874,8 @@ fn transCC(...@@ -5244,8 +3874,8 @@ fn transCC(
5244 .X86ThisCall => return CallingConvention.Thiscall,3874 .X86ThisCall => return CallingConvention.Thiscall,
5245 .AAPCS => return CallingConvention.AAPCS,3875 .AAPCS => return CallingConvention.AAPCS,
5246 .AAPCS_VFP => return CallingConvention.AAPCSVFP,3876 .AAPCS_VFP => return CallingConvention.AAPCSVFP,
5247 else => return revertAndWarn(3877 else => return fail(
5248 rp,3878 c,
5249 error.UnsupportedType,3879 error.UnsupportedType,
5250 source_loc,3880 source_loc,
5251 "unsupported calling convention: {s}",3881 "unsupported calling convention: {s}",
...@@ -5255,33 +3885,33 @@ fn transCC(...@@ -5255,33 +3885,33 @@ fn transCC(
5255}3885}
52563886
5257fn transFnProto(3887fn transFnProto(
5258 rp: RestorePoint,3888 c: *Context,
5259 fn_decl: ?*const clang.FunctionDecl,3889 fn_decl: ?*const clang.FunctionDecl,
5260 fn_proto_ty: *const clang.FunctionProtoType,3890 fn_proto_ty: *const clang.FunctionProtoType,
5261 source_loc: clang.SourceLocation,3891 source_loc: clang.SourceLocation,
5262 fn_decl_context: ?FnDeclContext,3892 fn_decl_context: ?FnDeclContext,
5263 is_pub: bool,3893 is_pub: bool,
5264) !*ast.Node.FnProto {3894) !*ast.Payload.Func {
5265 const fn_ty = @ptrCast(*const clang.FunctionType, fn_proto_ty);3895 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);
5267 const is_var_args = fn_proto_ty.isVariadic();3897 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);
5269}3899}
52703900
5271fn transFnNoProto(3901fn transFnNoProto(
5272 rp: RestorePoint,3902 c: *Context,
5273 fn_ty: *const clang.FunctionType,3903 fn_ty: *const clang.FunctionType,
5274 source_loc: clang.SourceLocation,3904 source_loc: clang.SourceLocation,
5275 fn_decl_context: ?FnDeclContext,3905 fn_decl_context: ?FnDeclContext,
5276 is_pub: bool,3906 is_pub: bool,
5277) !*ast.Node.FnProto {3907) !*ast.Payload.Func {
5278 const cc = try transCC(rp, fn_ty, source_loc);3908 const cc = try transCC(c, fn_ty, source_loc);
5279 const is_var_args = if (fn_decl_context) |ctx| (!ctx.is_export and ctx.storage_class != .Static) else true;3909 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);
5281}3911}
52823912
5283fn finishTransFnProto(3913fn finishTransFnProto(
5284 rp: RestorePoint,3914 c: *Context,
5285 fn_decl: ?*const clang.FunctionDecl,3915 fn_decl: ?*const clang.FunctionDecl,
5286 fn_proto_ty: ?*const clang.FunctionProtoType,3916 fn_proto_ty: ?*const clang.FunctionProtoType,
5287 fn_ty: *const clang.FunctionType,3917 fn_ty: *const clang.FunctionType,
...@@ -5290,128 +3920,78 @@ fn finishTransFnProto(...@@ -5290,128 +3920,78 @@ fn finishTransFnProto(
5290 is_var_args: bool,3920 is_var_args: bool,
5291 cc: CallingConvention,3921 cc: CallingConvention,
5292 is_pub: bool,3922 is_pub: bool,
5293) !*ast.Node.FnProto {3923) !*ast.Payload.Func {
5294 const is_export = if (fn_decl_context) |ctx| ctx.is_export else false;3924 const is_export = if (fn_decl_context) |ctx| ctx.is_export else false;
5295 const is_extern = if (fn_decl_context) |ctx| !ctx.has_body else false;3925 const is_extern = if (fn_decl_context) |ctx| !ctx.has_body else false;
3926 const scope = &c.global_scope.base;
52963927
5297 // TODO check for always_inline attribute3928 // TODO check for always_inline attribute
5298 // TODO check for align attribute3929 // TODO check for align attribute
52993930
5300 // pub extern fn name(...) T3931 var fn_params = std.ArrayList(ast.Payload.Param).init(c.gpa);
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);
5313 defer fn_params.deinit();3932 defer fn_params.deinit();
5314 const param_count: usize = if (fn_proto_ty != null) fn_proto_ty.?.getNumParams() else 0;3933 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 node3934 try fn_params.ensureCapacity(param_count);
53163935
5317 var i: usize = 0;3936 var i: usize = 0;
5318 while (i < param_count) : (i += 1) {3937 while (i < param_count) : (i += 1) {
5319 const param_qt = fn_proto_ty.?.getParamType(@intCast(c_uint, i));3938 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;3941 const param_name: ?[]const u8 =
53223942 if (fn_decl) |decl|
5323 const param_name_tok: ?ast.TokenIndex = blk: {3943 blk: {
5324 if (fn_decl) |decl| {3944 const param = decl.getParamDecl(@intCast(c_uint, i));
5325 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());
5326 const param_name: []const u8 = try rp.c.str(@ptrCast(*const clang.NamedDecl, param).getName_bytes_begin());3946 if (param_name.len < 1)
5327 if (param_name.len < 1)3947 break :blk null;
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 };
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
5339 fn_params.addOneAssumeCapacity().* = .{3953 fn_params.addOneAssumeCapacity().* = .{
5340 .doc_comments = null,3954 .is_noalias = is_noalias,
5341 .comptime_token = null,3955 .name = param_name,
5342 .noalias_token = noalias_tok,3956 .type = type_node,
5343 .name_token = param_name_tok,
5344 .param_type = .{ .type_expr = type_node },
5345 };3957 };
5346
5347 if (i + 1 < param_count) {
5348 _ = try appendToken(rp.c, .Comma, ",");
5349 }
5350 }3958 }
53513959
5352 const var_args_token: ?ast.TokenIndex = if (is_var_args) blk: {3960 const linksection_string = 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: {
5362 if (fn_decl) |decl| {3961 if (fn_decl) |decl| {
5363 var str_len: usize = undefined;3962 var str_len: usize = undefined;
5364 if (decl.getSectionAttribute(&str_len)) |str_ptr| {3963 if (decl.getSectionAttribute(&str_len)) |str_ptr| {
5365 _ = try appendToken(rp.c, .Keyword_linksection, "linksection");3964 break :blk str_ptr[0..str_len];
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;
5374 }3965 }
5375 }3966 }
5376 break :blk null;3967 break :blk null;
5377 };3968 };
53783969
5379 const align_expr = blk: {3970 const alignment = blk: {
5380 if (fn_decl) |decl| {3971 if (fn_decl) |decl| {
5381 const alignment = decl.getAlignedAttribute(rp.c.clang_context);3972 const alignment = decl.getAlignedAttribute(c.clang_context);
5382 if (alignment != 0) {3973 if (alignment != 0) {
5383 _ = try appendToken(rp.c, .Keyword_align, "align");
5384 _ = try appendToken(rp.c, .LParen, "(");
5385 // Clang reports the alignment in bits3974 // Clang reports the alignment in bits
5386 const expr = try transCreateNodeInt(rp.c, alignment / 8);3975 break :blk alignment / 8;
5387 _ = try appendToken(rp.c, .RParen, ")");
5388
5389 break :blk expr;
5390 }3976 }
5391 }3977 }
5392 break :blk null;3978 break :blk null;
5393 };3979 };
53943980
5395 const callconv_expr = if ((is_export or is_extern) and cc == .C) null else blk: {3981 const explicit_callconv = if ((is_export or is_extern) and cc == .C) null else cc;
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 };
54023982
5403 const return_type_node = blk: {3983 const return_type_node = blk: {
5404 if (fn_ty.getNoReturnAttr()) {3984 if (fn_ty.getNoReturnAttr()) {
5405 break :blk try transCreateNodeIdentifier(rp.c, "noreturn");3985 break :blk Tag.noreturn_type.init();
5406 } else {3986 } else {
5407 const return_qt = fn_ty.getReturnType();3987 const return_qt = fn_ty.getReturnType();
5408 if (isCVoid(return_qt)) {3988 if (isCVoid(return_qt)) {
5409 // convert primitive c_void to actual void (only for return type)3989 // 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();
5411 } else {3991 } 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) {
5413 error.UnsupportedType => {3993 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", .{});
5415 return err;3995 return err;
5416 },3996 },
5417 error.OutOfMemory => |e| return e,3997 error.OutOfMemory => |e| return e,
...@@ -5419,116 +3999,57 @@ fn finishTransFnProto(...@@ -5419,116 +3999,57 @@ fn finishTransFnProto(
5419 }3999 }
5420 }4000 }
5421 };4001 };
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.4023fn warn(c: *Context, scope: *Scope, loc: clang.SourceLocation, comptime format: []const u8, args: anytype) !void {
5424 var body_node: ?*ast.Node = null;4024 const args_prefix = .{c.locStr(loc)};
5425 if (fn_decl_context) |ctx| {4025 const value = try std.fmt.allocPrint(c.arena, "// {s}: warning: " ++ format, args_prefix ++ args);
5426 if (ctx.has_body) {4026 try scope.appendNode(try Tag.warning.create(c.arena, value));
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;
5450}4027}
54514028
5452fn revertAndWarn(4029fn fail(
5453 rp: RestorePoint,4030 c: *Context,
5454 err: anytype,4031 err: anytype,
5455 source_loc: clang.SourceLocation,4032 source_loc: clang.SourceLocation,
5456 comptime format: []const u8,4033 comptime format: []const u8,
5457 args: anytype,4034 args: anytype,
5458) (@TypeOf(err) || error{OutOfMemory}) {4035) (@TypeOf(err) || error{OutOfMemory}) {
5459 rp.activate();4036 try warn(c, &c.global_scope.base, source_loc, format, args);
5460 try emitWarning(rp.c, source_loc, format, args);
5461 return err;4037 return err;
5462}4038}
54634039
5464fn emitWarning(c: *Context, loc: clang.SourceLocation, 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 {
5465 const args_prefix = .{c.locStr(loc)};4041 // location
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 {
5470 // pub const name = @compileError(msg);4042 // pub const name = @compileError(msg);
5471 const pub_tok = try appendToken(c, .Keyword_pub, "pub");4043 const fail_msg = try std.fmt.allocPrint(c.arena, format, args);
5472 const const_tok = try appendToken(c, .Keyword_const, "const");4044 try addTopLevelDecl(c, name, try Tag.fail_decl.create(c.arena, .{ .actual = name, .mangled = fail_msg }));
5473 const name_tok = try appendIdentifier(c, name);4045 const location_comment = try std.fmt.allocPrint(c.arena, "// {s}", .{c.locStr(loc)});
5474 const eq_tok = try appendToken(c, .Equal, "=");4046 try c.global_scope.nodes.append(try Tag.warning.create(c.arena, location_comment));
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});
5511}4047}
55124048
5513fn appendTokenFmt(c: *Context, token_id: Token.Id, comptime format: []const u8, args: anytype) !ast.TokenIndex {4049pub fn freeErrors(errors: []ClangErrMsg) void {
5514 assert(token_id != .Invalid);4050 errors.ptr.delete(errors.len);
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;
5529}4051}
55304052
5531// TODO hook up with codegen
5532fn isZigPrimitiveType(name: []const u8) bool {4053fn isZigPrimitiveType(name: []const u8) bool {
5533 if (name.len > 1 and (name[0] == 'u' or name[0] == 'i')) {4054 if (name.len > 1 and (name[0] == 'u' or name[0] == 'i')) {
5534 for (name[1..]) |c| {4055 for (name[1..]) |c| {
...@@ -5539,56 +4060,7 @@ fn isZigPrimitiveType(name: []const u8) bool {...@@ -5539,56 +4060,7 @@ fn isZigPrimitiveType(name: []const u8) bool {
5539 }4060 }
5540 return true;4061 return true;
5541 }4062 }
5542 // void is invalid in c so it doesn't need to be checked.4063 return @import("astgen.zig").simple_types.has(name);
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);
5592}4064}
55934065
5594const MacroCtx = struct {4066const MacroCtx = struct {
...@@ -5709,27 +4181,13 @@ fn transPreprocessorEntities(c: *Context, unit: *clang.ASTUnit) Error!void {...@@ -5709,27 +4181,13 @@ fn transPreprocessorEntities(c: *Context, unit: *clang.ASTUnit) Error!void {
5709fn transMacroDefine(c: *Context, m: *MacroCtx) ParseError!void {4181fn transMacroDefine(c: *Context, m: *MacroCtx) ParseError!void {
5710 const scope = &c.global_scope.base;4182 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
5717 const init_node = try parseCExpr(c, m, scope);4184 const init_node = try parseCExpr(c, m, scope);
5718 const last = m.next().?;4185 const last = m.next().?;
5719 if (last != .Eof and last != .Nl)4186 if (last != .Eof and last != .Nl)
5720 return m.fail(c, "unable to translate C expr: unexpected token .{s}", .{@tagName(last)});4187 return m.fail(c, "unable to translate C expr: unexpected token .{s}", .{@tagName(last)});
57214188
5722 const semicolon_token = try appendToken(c, .Semicolon, ";");4189 const var_decl = try Tag.pub_var_simple.create(c.arena, .{ .name = m.name, .init = init_node });
5723 const node = try ast.Node.VarDecl.create(c.arena, .{4190 _ = try c.global_scope.macro_table.put(m.name, var_decl);
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);
5733}4191}
57344192
5735fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {4193fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {
...@@ -5737,16 +4195,11 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {...@@ -5737,16 +4195,11 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {
5737 defer block_scope.deinit();4195 defer block_scope.deinit();
5738 const scope = &block_scope.base;4196 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
5745 if (m.next().? != .LParen) {4198 if (m.next().? != .LParen) {
5746 return m.fail(c, "unable to translate C expr: expected '('", .{});4199 return m.fail(c, "unable to translate C expr: expected '('", .{});
5747 }4200 }
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);
5750 defer fn_params.deinit();4203 defer fn_params.deinit();
57514204
5752 while (true) {4205 while (true) {
...@@ -5754,120 +4207,82 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {...@@ -5754,120 +4207,82 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {
5754 _ = m.next();4207 _ = m.next();
57554208
5756 const mangled_name = try block_scope.makeMangledName(c, m.slice());4209 const mangled_name = try block_scope.makeMangledName(c, m.slice());
5757 const param_name_tok = try appendIdentifier(c, mangled_name);4210 try fn_params.append(.{
5758 _ = try appendToken(c, .Colon, ":");4211 .is_noalias = false,
57594212 .name = mangled_name,
5760 const any_type = try c.arena.create(ast.Node.OneToken);4213 .type = Tag.@"anytype".init(),
5761 any_type.* = .{4214 });
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 };
57734215
5774 if (m.peek().? != .Comma) break;4216 if (m.peek().? != .Comma) break;
5775 _ = m.next();4217 _ = m.next();
5776 _ = try appendToken(c, .Comma, ",");
5777 }4218 }
57784219
5779 if (m.next().? != .RParen) {4220 if (m.next().? != .RParen) {
5780 return m.fail(c, "unable to translate C expr: expected ')'", .{});4221 return m.fail(c, "unable to translate C expr: expected ')'", .{});
5781 }4222 }
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");
5793 const expr = try parseCExpr(c, m, scope);4224 const expr = try parseCExpr(c, m, scope);
5794 const last = m.next().?;4225 const last = m.next().?;
5795 if (last != .Eof and last != .Nl)4226 if (last != .Eof and last != .Nl)
5796 return m.fail(c, "unable to translate C expr: unexpected token .{s}", .{@tagName(last)});4227 return m.fail(c, "unable to translate C expr: unexpected token .{s}", .{@tagName(last)});
5797 _ = try appendToken(c, .Semicolon, ";");4228
5798 const type_of_arg = if (!expr.tag.isBlock()) expr else blk: {4229 const typeof_arg = if (expr.castTag(.block)) |some| blk: {
5799 const stmts = expr.blockStatements();4230 const stmts = some.data.stmts;
5800 const blk_last = stmts[stmts.len - 1];4231 const blk_last = stmts[stmts.len - 1];
5801 const br = blk_last.cast(ast.Node.ControlFlowExpression).?;4232 const br = blk_last.castTag(.break_val).?;
5802 break :blk br.getRHS().?;4233 break :blk br.data.val;
5803 };4234 } else expr;
5804 type_of.params()[0] = type_of_arg;4235 const return_type = if (typeof_arg.castTag(.std_meta_cast)) |some|
5805 type_of.rparen_token = try appendToken(c, .RParen, ")");4236 some.data.lhs
5806 const return_expr = try ast.Node.ControlFlowExpression.create(c.arena, .{4237 else
5807 .ltoken = return_kw,4238 try Tag.typeof.create(c.arena, typeof_arg);
5808 .tag = .Return,
5809 }, .{
5810 .rhs = expr,
5811 });
58124239
5813 try block_scope.statements.append(&return_expr.base);4240 const return_expr = try Tag.@"return".create(c.arena, expr);
5814 const block_node = try block_scope.complete(c);4241 try block_scope.statements.append(return_expr);
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);
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);
5828}4250}
58294251
5830const ParseError = Error || error{ParseError};4252const 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 {
5833 // TODO parseCAssignExpr here4255 // TODO parseCAssignExpr here
5834 const node = try parseCCondExpr(c, m, scope);4256 const node = try parseCCondExpr(c, m, scope);
5835 if (m.next().? != .Comma) {4257 if (m.next().? != .Comma) {
5836 m.i -= 1;4258 m.i -= 1;
5837 return node;4259 return node;
5838 }4260 }
5839 _ = try appendToken(c, .Semicolon, ";");
5840 var block_scope = try Scope.Block.init(c, scope, true);4261 var block_scope = try Scope.Block.init(c, scope, true);
5841 defer block_scope.deinit();4262 defer block_scope.deinit();
58424263
5843 var last = node;4264 var last = node;
5844 while (true) {4265 while (true) {
5845 // suppress result4266 // suppress result
5846 const lhs = try transCreateNodeIdentifier(c, "_");4267 const ignore = try Tag.discard.create(c.arena, last);
5847 const op_token = try appendToken(c, .Equal, "=");4268 try block_scope.statements.append(ignore);
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);
58564269
5857 last = try parseCCondExpr(c, m, scope);4270 last = try parseCCondExpr(c, m, scope);
5858 _ = try appendToken(c, .Semicolon, ";");
5859 if (m.next().? != .Comma) {4271 if (m.next().? != .Comma) {
5860 m.i -= 1;4272 m.i -= 1;
5861 break;4273 break;
5862 }4274 }
5863 }4275 }
58644276
5865 const break_node = try transCreateNodeBreak(c, block_scope.label, last);4277 const break_node = try Tag.break_val.create(c.arena, .{
5866 try block_scope.statements.append(&break_node.base);4278 .label = block_scope.label,
4279 .val = last,
4280 });
4281 try block_scope.statements.append(break_node);
5867 return try block_scope.complete(c);4282 return try block_scope.complete(c);
5868}4283}
58694284
5870fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!*ast.Node {4285fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!Node {
5871 var lit_bytes = m.slice();4286 var lit_bytes = m.slice();
58724287
5873 switch (m.list[m.i].id) {4288 switch (m.list[m.i].id) {
...@@ -5887,11 +4302,10 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!*ast.Node {...@@ -5887,11 +4302,10 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!*ast.Node {
5887 }4302 }
58884303
5889 if (suffix == .none) {4304 if (suffix == .none) {
5890 return transCreateNodeInt(c, lit_bytes);4305 return transCreateNodeNumber(c, lit_bytes, .int);
5891 }4306 }
58924307
5893 const cast_node = try c.createBuiltinCall("@as", 2);4308 const type_node = try Tag.type.create(c.arena, switch (suffix) {
5894 cast_node.params()[0] = try transCreateNodeIdentifier(c, switch (suffix) {
5895 .u => "c_uint",4309 .u => "c_uint",
5896 .l => "c_long",4310 .l => "c_long",
5897 .lu => "c_ulong",4311 .lu => "c_ulong",
...@@ -5905,27 +4319,22 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!*ast.Node {...@@ -5905,27 +4319,22 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!*ast.Node {
5905 .llu => 3,4319 .llu => 3,
5906 else => unreachable,4320 else => unreachable,
5907 }];4321 }];
5908 _ = try appendToken(c, .Comma, ",");4322 const rhs = try transCreateNodeNumber(c, lit_bytes, .int);
5909 cast_node.params()[1] = try transCreateNodeInt(c, lit_bytes);4323 return Tag.as.create(c.arena, .{ .lhs = type_node, .rhs = rhs });
5910 cast_node.rparen_token = try appendToken(c, .RParen, ")");
5911 return &cast_node.base;
5912 },4324 },
5913 .FloatLiteral => |suffix| {4325 .FloatLiteral => |suffix| {
5914 if (lit_bytes[0] == '.')4326 if (lit_bytes[0] == '.')
5915 lit_bytes = try std.fmt.allocPrint(c.arena, "0{s}", .{lit_bytes});4327 lit_bytes = try std.fmt.allocPrint(c.arena, "0{s}", .{lit_bytes});
5916 if (suffix == .none) {4328 if (suffix == .none) {
5917 return transCreateNodeFloat(c, lit_bytes);4329 return transCreateNodeNumber(c, lit_bytes, .float);
5918 }4330 }
5919 const cast_node = try c.createBuiltinCall("@as", 2);4331 const type_node = try Tag.type.create(c.arena, switch (suffix) {
5920 cast_node.params()[0] = try transCreateNodeIdentifier(c, switch (suffix) {
5921 .f => "f32",4332 .f => "f32",
5922 .l => "c_longdouble",4333 .l => "c_longdouble",
5923 else => unreachable,4334 else => unreachable,
5924 });4335 });
5925 _ = try appendToken(c, .Comma, ",");4336 const rhs = try transCreateNodeNumber(c, lit_bytes[0 .. lit_bytes.len - 1], .float);
5926 cast_node.params()[1] = try transCreateNodeFloat(c, lit_bytes[0 .. lit_bytes.len - 1]);4337 return Tag.as.create(c.arena, .{ .lhs = type_node, .rhs = rhs });
5927 cast_node.rparen_token = try appendToken(c, .RParen, ")");
5928 return &cast_node.base;
5929 },4338 },
5930 else => unreachable,4339 else => unreachable,
5931 }4340 }
...@@ -6091,79 +4500,62 @@ fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 {...@@ -6091,79 +4500,62 @@ fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 {
6091 return bytes[0..i];4500 return bytes[0..i];
6092}4501}
60934502
6094fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {4503fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
6095 const tok = m.next().?;4504 const tok = m.next().?;
6096 const slice = m.slice();4505 const slice = m.slice();
6097 switch (tok) {4506 switch (tok) {
6098 .CharLiteral => {4507 .CharLiteral => {
6099 if (slice[0] != '\'' or slice[1] == '\\' or slice.len == 3) {4508 if (slice[0] != '\'' or slice[1] == '\\' or slice.len == 3) {
6100 const token = try appendToken(c, .CharLiteral, try zigifyEscapeSequences(c, m));4509 return Tag.char_literal.create(c.arena, 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;
6107 } else {4510 } else {
6108 const token = try appendTokenFmt(c, .IntegerLiteral, "0x{x}", .{slice[1 .. slice.len - 1]});4511 const str = try std.fmt.allocPrint(c.arena, "0x{x}", .{slice[1 .. slice.len - 1]});
6109 const node = try c.arena.create(ast.Node.OneToken);4512 return Tag.integer_literal.create(c.arena, str);
6110 node.* = .{
6111 .base = .{ .tag = .IntegerLiteral },
6112 .token = token,
6113 };
6114 return &node.base;
6115 }4513 }
6116 },4514 },
6117 .StringLiteral => {4515 .StringLiteral => {
6118 const token = try appendToken(c, .StringLiteral, try zigifyEscapeSequences(c, m));4516 return Tag.string_literal.create(c.arena, 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;
6125 },4517 },
6126 .IntegerLiteral, .FloatLiteral => {4518 .IntegerLiteral, .FloatLiteral => {
6127 return parseCNumLit(c, m);4519 return parseCNumLit(c, m);
6128 },4520 },
6129 // eventually this will be replaced by std.c.parse which will handle these correctly4521 // eventually this will be replaced by std.c.parse which will handle these correctly
6130 .Keyword_void => return transCreateNodeIdentifierUnchecked(c, "c_void"),4522 .Keyword_void => return Tag.type.create(c.arena, "c_void"),
6131 .Keyword_bool => return transCreateNodeIdentifierUnchecked(c, "bool"),4523 .Keyword_bool => return Tag.type.create(c.arena, "bool"),
6132 .Keyword_double => return transCreateNodeIdentifierUnchecked(c, "f64"),4524 .Keyword_double => return Tag.type.create(c.arena, "f64"),
6133 .Keyword_long => return transCreateNodeIdentifierUnchecked(c, "c_long"),4525 .Keyword_long => return Tag.type.create(c.arena, "c_long"),
6134 .Keyword_int => return transCreateNodeIdentifierUnchecked(c, "c_int"),4526 .Keyword_int => return Tag.type.create(c.arena, "c_int"),
6135 .Keyword_float => return transCreateNodeIdentifierUnchecked(c, "f32"),4527 .Keyword_float => return Tag.type.create(c.arena, "f32"),
6136 .Keyword_short => return transCreateNodeIdentifierUnchecked(c, "c_short"),4528 .Keyword_short => return Tag.type.create(c.arena, "c_short"),
6137 .Keyword_char => return transCreateNodeIdentifierUnchecked(c, "u8"),4529 .Keyword_char => return Tag.type.create(c.arena, "u8"),
6138 .Keyword_unsigned => if (m.next()) |t| switch (t) {4530 .Keyword_unsigned => if (m.next()) |t| switch (t) {
6139 .Keyword_char => return transCreateNodeIdentifierUnchecked(c, "u8"),4531 .Keyword_char => return Tag.type.create(c.arena, "u8"),
6140 .Keyword_short => return transCreateNodeIdentifierUnchecked(c, "c_ushort"),4532 .Keyword_short => return Tag.type.create(c.arena, "c_ushort"),
6141 .Keyword_int => return transCreateNodeIdentifierUnchecked(c, "c_uint"),4533 .Keyword_int => return Tag.type.create(c.arena, "c_uint"),
6142 .Keyword_long => if (m.peek() != null and m.peek().? == .Keyword_long) {4534 .Keyword_long => if (m.peek() != null and m.peek().? == .Keyword_long) {
6143 _ = m.next();4535 _ = m.next();
6144 return transCreateNodeIdentifierUnchecked(c, "c_ulonglong");4536 return Tag.type.create(c.arena, "c_ulonglong");
6145 } else return transCreateNodeIdentifierUnchecked(c, "c_ulong"),4537 } else return Tag.type.create(c.arena, "c_ulong"),
6146 else => {4538 else => {
6147 m.i -= 1;4539 m.i -= 1;
6148 return transCreateNodeIdentifierUnchecked(c, "c_uint");4540 return Tag.type.create(c.arena, "c_uint");
6149 },4541 },
6150 } else {4542 } else {
6151 return transCreateNodeIdentifierUnchecked(c, "c_uint");4543 return Tag.type.create(c.arena, "c_uint");
6152 },4544 },
6153 .Keyword_signed => if (m.next()) |t| switch (t) {4545 .Keyword_signed => if (m.next()) |t| switch (t) {
6154 .Keyword_char => return transCreateNodeIdentifierUnchecked(c, "i8"),4546 .Keyword_char => return Tag.type.create(c.arena, "i8"),
6155 .Keyword_short => return transCreateNodeIdentifierUnchecked(c, "c_short"),4547 .Keyword_short => return Tag.type.create(c.arena, "c_short"),
6156 .Keyword_int => return transCreateNodeIdentifierUnchecked(c, "c_int"),4548 .Keyword_int => return Tag.type.create(c.arena, "c_int"),
6157 .Keyword_long => if (m.peek() != null and m.peek().? == .Keyword_long) {4549 .Keyword_long => if (m.peek() != null and m.peek().? == .Keyword_long) {
6158 _ = m.next();4550 _ = m.next();
6159 return transCreateNodeIdentifierUnchecked(c, "c_longlong");4551 return Tag.type.create(c.arena, "c_longlong");
6160 } else return transCreateNodeIdentifierUnchecked(c, "c_long"),4552 } else return Tag.type.create(c.arena, "c_long"),
6161 else => {4553 else => {
6162 m.i -= 1;4554 m.i -= 1;
6163 return transCreateNodeIdentifierUnchecked(c, "c_int");4555 return Tag.type.create(c.arena, "c_int");
6164 },4556 },
6165 } else {4557 } else {
6166 return transCreateNodeIdentifierUnchecked(c, "c_int");4558 return Tag.type.create(c.arena, "c_int");
6167 },4559 },
6168 .Keyword_enum, .Keyword_struct, .Keyword_union => {4560 .Keyword_enum, .Keyword_struct, .Keyword_union => {
6169 // struct Foo will be declared as struct_Foo by transRecordDecl4561 // struct Foo will be declared as struct_Foo by transRecordDecl
...@@ -6173,17 +4565,12 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*...@@ -6173,17 +4565,12 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*
6173 return error.ParseError;4565 return error.ParseError;
6174 }4566 }
61754567
6176 const ident_token = try appendTokenFmt(c, .Identifier, "{s}_{s}", .{ slice, m.slice() });4568 const name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ slice, m.slice() });
6177 const identifier = try c.arena.create(ast.Node.OneToken);4569 return Tag.identifier.create(c.arena, name);
6178 identifier.* = .{
6179 .base = .{ .tag = .Identifier },
6180 .token = ident_token,
6181 };
6182 return &identifier.base;
6183 },4570 },
6184 .Identifier => {4571 .Identifier => {
6185 const mangled_name = scope.getAlias(slice);4572 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);
6187 },4574 },
6188 .LParen => {4575 .LParen => {
6189 const inner_node = try parseCExpr(c, m, scope);4576 const inner_node = try parseCExpr(c, m, scope);
...@@ -6213,10 +4600,6 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*...@@ -6213,10 +4600,6 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*
6213 },4600 },
6214 else => return inner_node,4601 else => return inner_node,
6215 }4602 }
6216
6217 // hack to get zig fmt to render a comma in builtin calls
6218 _ = try appendToken(c, .Comma, ",");
6219
6220 const node_to_cast = try parseCExpr(c, m, scope);4603 const node_to_cast = try parseCExpr(c, m, scope);
62214604
6222 if (saw_l_paren and m.next().? != .RParen) {4605 if (saw_l_paren and m.next().? != .RParen) {
...@@ -6224,28 +4607,7 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*...@@ -6224,28 +4607,7 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*
6224 return error.ParseError;4607 return error.ParseError;
6225 }4608 }
62264609
6227 const lparen = try appendToken(c, .LParen, "(");4610 return Tag.std_meta_cast.create(c.arena, .{ .lhs = inner_node, .rhs = node_to_cast });
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;
6249 },4611 },
6250 else => {4612 else => {
6251 try m.fail(c, "unable to translate C expr: unexpected token .{s}", .{@tagName(tok)});4613 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!*...@@ -6254,447 +4616,256 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*
6254 }4616 }
6255}4617}
62564618
6257fn parseCPrimaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {4619fn parseCPrimaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
6258 var node = try parseCPrimaryExprInner(c, m, scope);4620 var node = try parseCPrimaryExprInner(c, m, scope);
6259 // In C the preprocessor would handle concatting strings while expanding macros.4621 // In C the preprocessor would handle concatting strings while expanding macros.
6260 // This should do approximately the same by concatting any strings and identifiers4622 // This should do approximately the same by concatting any strings and identifiers
6261 // after a primary expression.4623 // after a primary expression.
6262 while (true) {4624 while (true) {
6263 var op_token: ast.TokenIndex = undefined;
6264 var op_id: ast.Node.Tag = undefined;
6265 switch (m.peek().?) {4625 switch (m.peek().?) {
6266 .StringLiteral, .Identifier => {},4626 .StringLiteral, .Identifier => {},
6267 else => break,4627 else => break,
6268 }4628 }
6269 const op_node = try c.arena.create(ast.Node.SimpleInfixOp);4629 node = try Tag.array_cat.create(c.arena, .{ .lhs = node, .rhs = try parseCPrimaryExprInner(c, m, scope) });
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;
6277 }4630 }
6278 return node;4631 return node;
6279}4632}
62804633
6281fn nodeIsInfixOp(tag: ast.Node.Tag) bool {4634fn macroBoolToInt(c: *Context, node: Node) !Node {
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 {
6333 if (!isBoolRes(node)) {4635 if (!isBoolRes(node)) {
6334 if (!nodeIsInfixOp(node.tag)) return node;4636 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;
6343 }4637 }
63444638
6345 const builtin_node = try c.createBuiltinCall("@boolToInt", 1);4639 return Tag.bool_to_int.create(c.arena, node);
6346 builtin_node.params()[0] = node;
6347 builtin_node.rparen_token = try appendToken(c, .RParen, ")");
6348 return &builtin_node.base;
6349}4640}
63504641
6351fn macroIntToBool(c: *Context, node: *ast.Node) !*ast.Node {4642fn macroIntToBool(c: *Context, node: Node) !Node {
6352 if (isBoolRes(node)) {4643 if (isBoolRes(node)) {
6353 if (!nodeIsInfixOp(node.tag)) return node;4644 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;
6362 }4645 }
63634646
6364 const op_token = try appendToken(c, .BangEqual, "!=");4647 return Tag.not_equal.create(c.arena, .{ .lhs = node, .rhs = Tag.zero_literal.init() });
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;
6380}4648}
63814649
6382fn macroGroup(c: *Context, node: *ast.Node) !*ast.Node {4650fn parseCCondExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!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 {
6395 const node = try parseCOrExpr(c, m, scope);4651 const node = try parseCOrExpr(c, m, scope);
6396 if (m.peek().? != .QuestionMark) {4652 if (m.peek().? != .QuestionMark) {
6397 return node;4653 return node;
6398 }4654 }
6399 _ = m.next();4655 _ = m.next();
64004656
6401 // must come immediately after expr4657 const then_body = try parseCOrExpr(c, m, scope);
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);
6406 if (m.next().? != .Colon) {4658 if (m.next().? != .Colon) {
6407 try m.fail(c, "unable to translate C expr: expected ':'", .{});4659 try m.fail(c, "unable to translate C expr: expected ':'", .{});
6408 return error.ParseError;4660 return error.ParseError;
6409 }4661 }
6410 if_node.@"else" = try transCreateNodeElse(c);4662 const else_body = try parseCCondExpr(c, m, scope);
6411 if_node.@"else".?.body = try parseCCondExpr(c, m, scope);4663 return Tag.@"if".create(c.arena, .{ .cond = node, .then = then_body, .@"else" = else_body });
6412 return &if_node.base;
6413}4664}
64144665
6415fn parseCOrExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {4666fn parseCOrExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
6416 var node = try parseCAndExpr(c, m, scope);4667 var node = try parseCAndExpr(c, m, scope);
6417 while (m.next().? == .PipePipe) {4668 while (m.next().? == .PipePipe) {
6418 const lhs_node = try macroIntToBool(c, node);4669 const lhs = try macroIntToBool(c, node);
6419 const op_token = try appendToken(c, .Keyword_or, "or");4670 const rhs = try macroIntToBool(c, try parseCAndExpr(c, m, scope));
6420 const rhs_node = try parseCAndExpr(c, m, scope);4671 node = try Tag.@"or".create(c.arena, .{ .lhs = lhs, .rhs = rhs });
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;
6429 }4672 }
6430 m.i -= 1;4673 m.i -= 1;
6431 return node;4674 return node;
6432}4675}
64334676
6434fn parseCAndExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {4677fn parseCAndExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
6435 var node = try parseCBitOrExpr(c, m, scope);4678 var node = try parseCBitOrExpr(c, m, scope);
6436 while (m.next().? == .AmpersandAmpersand) {4679 while (m.next().? == .AmpersandAmpersand) {
6437 const lhs_node = try macroIntToBool(c, node);4680 const lhs = try macroIntToBool(c, node);
6438 const op_token = try appendToken(c, .Keyword_and, "and");4681 const rhs = try macroIntToBool(c, try parseCBitOrExpr(c, m, scope));
6439 const rhs_node = try parseCBitOrExpr(c, m, scope);4682 node = try Tag.@"and".create(c.arena, .{ .lhs = lhs, .rhs = rhs });
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;
6448 }4683 }
6449 m.i -= 1;4684 m.i -= 1;
6450 return node;4685 return node;
6451}4686}
64524687
6453fn parseCBitOrExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {4688fn parseCBitOrExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
6454 var node = try parseCBitXorExpr(c, m, scope);4689 var node = try parseCBitXorExpr(c, m, scope);
6455 while (m.next().? == .Pipe) {4690 while (m.next().? == .Pipe) {
6456 const lhs_node = try macroBoolToInt(c, node);4691 const lhs = try macroBoolToInt(c, node);
6457 const op_token = try appendToken(c, .Pipe, "|");4692 const rhs = try macroBoolToInt(c, try parseCBitXorExpr(c, m, scope));
6458 const rhs_node = try parseCBitXorExpr(c, m, scope);4693 node = try Tag.bit_or.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
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;
6467 }4694 }
6468 m.i -= 1;4695 m.i -= 1;
6469 return node;4696 return node;
6470}4697}
64714698
6472fn parseCBitXorExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {4699fn parseCBitXorExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
6473 var node = try parseCBitAndExpr(c, m, scope);4700 var node = try parseCBitAndExpr(c, m, scope);
6474 while (m.next().? == .Caret) {4701 while (m.next().? == .Caret) {
6475 const lhs_node = try macroBoolToInt(c, node);4702 const lhs = try macroBoolToInt(c, node);
6476 const op_token = try appendToken(c, .Caret, "^");4703 const rhs = try macroBoolToInt(c, try parseCBitAndExpr(c, m, scope));
6477 const rhs_node = try parseCBitAndExpr(c, m, scope);4704 node = try Tag.bit_xor.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
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;
6486 }4705 }
6487 m.i -= 1;4706 m.i -= 1;
6488 return node;4707 return node;
6489}4708}
64904709
6491fn parseCBitAndExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {4710fn parseCBitAndExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
6492 var node = try parseCEqExpr(c, m, scope);4711 var node = try parseCEqExpr(c, m, scope);
6493 while (m.next().? == .Ampersand) {4712 while (m.next().? == .Ampersand) {
6494 const lhs_node = try macroBoolToInt(c, node);4713 const lhs = try macroBoolToInt(c, node);
6495 const op_token = try appendToken(c, .Ampersand, "&");4714 const rhs = try macroBoolToInt(c, try parseCEqExpr(c, m, scope));
6496 const rhs_node = try parseCEqExpr(c, m, scope);4715 node = try Tag.bit_and.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
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;
6505 }4716 }
6506 m.i -= 1;4717 m.i -= 1;
6507 return node;4718 return node;
6508}4719}
65094720
6510fn parseCEqExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {4721fn parseCEqExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
6511 var node = try parseCRelExpr(c, m, scope);4722 var node = try parseCRelExpr(c, m, scope);
6512 while (true) {4723 while (true) {
6513 var op_token: ast.TokenIndex = undefined;
6514 var op_id: ast.Node.Tag = undefined;
6515 switch (m.peek().?) {4724 switch (m.peek().?) {
6516 .BangEqual => {4725 .BangEqual => {
6517 op_token = try appendToken(c, .BangEqual, "!=");4726 _ = m.next();
6518 op_id = .BangEqual;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 });
6519 },4730 },
6520 .EqualEqual => {4731 .EqualEqual => {
6521 op_token = try appendToken(c, .EqualEqual, "==");4732 _ = m.next();
6522 op_id = .EqualEqual;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 });
6523 },4736 },
6524 else => return node,4737 else => return node,
6525 }4738 }
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;
6537 }4739 }
6538}4740}
65394741
6540fn parseCRelExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {4742fn parseCRelExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
6541 var node = try parseCShiftExpr(c, m, scope);4743 var node = try parseCShiftExpr(c, m, scope);
6542 while (true) {4744 while (true) {
6543 var op_token: ast.TokenIndex = undefined;
6544 var op_id: ast.Node.Tag = undefined;
6545 switch (m.peek().?) {4745 switch (m.peek().?) {
6546 .AngleBracketRight => {4746 .AngleBracketRight => {
6547 op_token = try appendToken(c, .AngleBracketRight, ">");4747 _ = m.next();
6548 op_id = .GreaterThan;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 });
6549 },4751 },
6550 .AngleBracketRightEqual => {4752 .AngleBracketRightEqual => {
6551 op_token = try appendToken(c, .AngleBracketRightEqual, ">=");4753 _ = m.next();
6552 op_id = .GreaterOrEqual;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 });
6553 },4757 },
6554 .AngleBracketLeft => {4758 .AngleBracketLeft => {
6555 op_token = try appendToken(c, .AngleBracketLeft, "<");4759 _ = m.next();
6556 op_id = .LessThan;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 });
6557 },4763 },
6558 .AngleBracketLeftEqual => {4764 .AngleBracketLeftEqual => {
6559 op_token = try appendToken(c, .AngleBracketLeftEqual, "<=");4765 _ = m.next();
6560 op_id = .LessOrEqual;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 });
6561 },4769 },
6562 else => return node,4770 else => return node,
6563 }4771 }
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;
6575 }4772 }
6576}4773}
65774774
6578fn parseCShiftExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {4775fn parseCShiftExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
6579 var node = try parseCAddSubExpr(c, m, scope);4776 var node = try parseCAddSubExpr(c, m, scope);
6580 while (true) {4777 while (true) {
6581 var op_token: ast.TokenIndex = undefined;
6582 var op_id: ast.Node.Tag = undefined;
6583 switch (m.peek().?) {4778 switch (m.peek().?) {
6584 .AngleBracketAngleBracketLeft => {4779 .AngleBracketAngleBracketLeft => {
6585 op_token = try appendToken(c, .AngleBracketAngleBracketLeft, "<<");4780 _ = m.next();
6586 op_id = .BitShiftLeft;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 });
6587 },4784 },
6588 .AngleBracketAngleBracketRight => {4785 .AngleBracketAngleBracketRight => {
6589 op_token = try appendToken(c, .AngleBracketAngleBracketRight, ">>");4786 _ = m.next();
6590 op_id = .BitShiftRight;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 });
6591 },4790 },
6592 else => return node,4791 else => return node,
6593 }4792 }
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;
6605 }4793 }
6606}4794}
66074795
6608fn parseCAddSubExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {4796fn parseCAddSubExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
6609 var node = try parseCMulExpr(c, m, scope);4797 var node = try parseCMulExpr(c, m, scope);
6610 while (true) {4798 while (true) {
6611 var op_token: ast.TokenIndex = undefined;
6612 var op_id: ast.Node.Tag = undefined;
6613 switch (m.peek().?) {4799 switch (m.peek().?) {
6614 .Plus => {4800 .Plus => {
6615 op_token = try appendToken(c, .Plus, "+");4801 _ = m.next();
6616 op_id = .Add;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 });
6617 },4805 },
6618 .Minus => {4806 .Minus => {
6619 op_token = try appendToken(c, .Minus, "-");4807 _ = m.next();
6620 op_id = .Sub;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 });
6621 },4811 },
6622 else => return node,4812 else => return node,
6623 }4813 }
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;
6635 }4814 }
6636}4815}
66374816
6638fn parseCMulExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {4817fn parseCMulExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
6639 var node = try parseCUnaryExpr(c, m, scope);4818 var node = try parseCUnaryExpr(c, m, scope);
6640 while (true) {4819 while (true) {
6641 var op_token: ast.TokenIndex = undefined;
6642 var op_id: ast.Node.Tag = undefined;
6643 switch (m.next().?) {4820 switch (m.next().?) {
6644 .Asterisk => {4821 .Asterisk => {
6645 if (m.peek().? == .RParen) {4822 const next = m.peek().?;
4823 if (next == .RParen or next == .Nl or next == .Eof) {
6646 // type *)4824 // type *)
66474825
6648 // hack to get zig fmt to render a comma in builtin calls
6649 _ = try appendToken(c, .Comma, ",");
6650
6651 // last token of `node`4826 // last token of `node`
6652 const prev_id = m.list[m.i - 1].id;4827 const prev_id = m.list[m.i - 1].id;
66534828
6654 if (prev_id == .Keyword_void) {4829 if (prev_id == .Keyword_void) {
6655 const ptr = try transCreateNodePtrType(c, false, false, .Asterisk);4830 const ptr = try Tag.single_pointer.create(c.arena, .{
6656 ptr.rhs = node;4831 .is_const = false,
6657 const optional_node = try transCreateNodeSimplePrefixOp(c, .OptionalType, .QuestionMark, "?");4832 .is_volatile = false,
6658 optional_node.rhs = &ptr.base;4833 .elem_type = node,
6659 return &optional_node.base;4834 });
4835 return Tag.optional_type.create(c.arena, ptr);
6660 } else {4836 } else {
6661 const ptr = try transCreateNodePtrType(c, false, false, Token.Id.Identifier);4837 return Tag.c_pointer.create(c.arena, .{
6662 ptr.rhs = node;4838 .is_const = false,
6663 return &ptr.base;4839 .is_volatile = false,
4840 .elem_type = node,
4841 });
6664 }4842 }
6665 } else {4843 } else {
6666 // expr * expr4844 // expr * expr
6667 op_token = try appendToken(c, .Asterisk, "*");4845 const lhs = try macroBoolToInt(c, node);
6668 op_id = .BitShiftLeft;4846 const rhs = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope));
4847 node = try Tag.mul.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
6669 }4848 }
6670 },4849 },
6671 .Slash => {4850 .Slash => {
6672 op_id = .Div;4851 const lhs = try macroBoolToInt(c, node);
6673 op_token = try appendToken(c, .Slash, "/");4852 const rhs = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope));
4853 node = try Tag.div.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
6674 },4854 },
6675 .Percent => {4855 .Percent => {
6676 op_id = .Mod;4856 const lhs = try macroBoolToInt(c, node);
6677 op_token = try appendToken(c, .Percent, "%");4857 const rhs = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope));
4858 node = try Tag.mod.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
6678 },4859 },
6679 else => {4860 else => {
6680 m.i -= 1;4861 m.i -= 1;
6681 return node;4862 return node;
6682 },4863 },
6683 }4864 }
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;
6694 }4865 }
6695}4866}
66964867
6697fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {4868fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
6698 var node = try parseCPrimaryExpr(c, m, scope);4869 var node = try parseCPrimaryExpr(c, m, scope);
6699 while (true) {4870 while (true) {
6700 switch (m.next().?) {4871 switch (m.next().?) {
...@@ -6704,38 +4875,33 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.N...@@ -6704,38 +4875,33 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.N
6704 return error.ParseError;4875 return error.ParseError;
6705 }4876 }
67064877
6707 node = try transCreateNodeFieldAccess(c, node, m.slice());4878 node = try Tag.field_access.create(c.arena, .{ .lhs = node, .field_name = m.slice() });
6708 continue;
6709 },4879 },
6710 .Arrow => {4880 .Arrow => {
6711 if (m.next().? != .Identifier) {4881 if (m.next().? != .Identifier) {
6712 try m.fail(c, "unable to translate C expr: expected identifier", .{});4882 try m.fail(c, "unable to translate C expr: expected identifier", .{});
6713 return error.ParseError;4883 return error.ParseError;
6714 }4884 }
6715 const deref = try transCreateNodePtrDeref(c, node);4885
6716 node = try transCreateNodeFieldAccess(c, deref, m.slice());4886 const deref = try Tag.deref.create(c.arena, node);
6717 continue;4887 node = try Tag.field_access.create(c.arena, .{ .lhs = deref, .field_name = m.slice() });
6718 },4888 },
6719 .LBracket => {4889 .LBracket => {
6720 const arr_node = try transCreateNodeArrayAccess(c, node);4890 const index = try macroBoolToInt(c, try parseCExpr(c, m, scope));
6721 arr_node.index_expr = try parseCExpr(c, m, scope);4891 node = try Tag.array_access.create(c.arena, .{ .lhs = node, .rhs = index });
6722 arr_node.rtoken = try appendToken(c, .RBracket, "]");
6723 node = &arr_node.base;
6724 if (m.next().? != .RBracket) {4892 if (m.next().? != .RBracket) {
6725 try m.fail(c, "unable to translate C expr: expected ']'", .{});4893 try m.fail(c, "unable to translate C expr: expected ']'", .{});
6726 return error.ParseError;4894 return error.ParseError;
6727 }4895 }
6728 continue;
6729 },4896 },
6730 .LParen => {4897 .LParen => {
6731 _ = try appendToken(c, .LParen, "(");4898 var args = std.ArrayList(Node).init(c.gpa);
6732 var call_params = std.ArrayList(*ast.Node).init(c.gpa);4899 defer args.deinit();
6733 defer call_params.deinit();
6734 while (true) {4900 while (true) {
6735 const arg = try parseCCondExpr(c, m, scope);4901 const arg = try parseCCondExpr(c, m, scope);
6736 try call_params.append(arg);4902 try args.append(arg);
6737 switch (m.next().?) {4903 switch (m.next().?) {
6738 .Comma => _ = try appendToken(c, .Comma, ","),4904 .Comma => {},
6739 .RParen => break,4905 .RParen => break,
6740 else => {4906 else => {
6741 try m.fail(c, "unable to translate C expr: expected ',' or ')'", .{});4907 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...@@ -6743,32 +4909,17 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.N
6743 },4909 },
6744 }4910 }
6745 }4911 }
6746 const call_node = try ast.Node.Call.alloc(c.arena, call_params.items.len);4912 node = try Tag.call.create(c.arena, .{ .lhs = node, .args = try c.arena.dupe(Node, args.items) });
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;
6756 },4913 },
6757 .LBrace => {4914 .LBrace => {
6758 // must come immediately after `node`4915 var init_vals = std.ArrayList(Node).init(c.gpa);
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);
6765 defer init_vals.deinit();4916 defer init_vals.deinit();
67664917
6767 while (true) {4918 while (true) {
6768 const val = try parseCCondExpr(c, m, scope);4919 const val = try parseCCondExpr(c, m, scope);
6769 try init_vals.append(val);4920 try init_vals.append(val);
6770 switch (m.next().?) {4921 switch (m.next().?) {
6771 .Comma => _ = try appendToken(c, .Comma, ","),4922 .Comma => {},
6772 .RBrace => break,4923 .RBrace => break,
6773 else => {4924 else => {
6774 try m.fail(c, "unable to translate C expr: expected ',' or '}}'", .{});4925 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...@@ -6776,29 +4927,8 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.N
6776 },4927 },
6777 }4928 }
6778 }4929 }
6779 const tuple_node = try ast.Node.StructInitializerDot.alloc(c.arena, init_vals.items.len);4930 const tuple_node = try Tag.tuple.create(c.arena, try c.arena.dupe(Node, init_vals.items));
6780 tuple_node.* = .{4931 node = try Tag.std_mem_zeroinit.create(c.arena, .{ .lhs = node, .rhs = 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;
6802 },4932 },
6803 .PlusPlus, .MinusMinus => {4933 .PlusPlus, .MinusMinus => {
6804 try m.fail(c, "TODO postfix inc/dec expr", .{});4934 try m.fail(c, "TODO postfix inc/dec expr", .{});
...@@ -6812,35 +4942,31 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.N...@@ -6812,35 +4942,31 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.N
6812 }4942 }
6813}4943}
68144944
6815fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {4945fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
6816 switch (m.next().?) {4946 switch (m.next().?) {
6817 .Bang => {4947 .Bang => {
6818 const node = try transCreateNodeSimplePrefixOp(c, .BoolNot, .Bang, "!");4948 const operand = try macroIntToBool(c, try parseCUnaryExpr(c, m, scope));
6819 node.rhs = try macroIntToBool(c, try parseCUnaryExpr(c, m, scope));4949 return Tag.not.create(c.arena, operand);
6820 return &node.base;
6821 },4950 },
6822 .Minus => {4951 .Minus => {
6823 const node = try transCreateNodeSimplePrefixOp(c, .Negation, .Minus, "-");4952 const operand = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope));
6824 node.rhs = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope));4953 return Tag.negate.create(c.arena, operand);
6825 return &node.base;
6826 },4954 },
6827 .Plus => return try parseCUnaryExpr(c, m, scope),4955 .Plus => return try parseCUnaryExpr(c, m, scope),
6828 .Tilde => {4956 .Tilde => {
6829 const node = try transCreateNodeSimplePrefixOp(c, .BitNot, .Tilde, "~");4957 const operand = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope));
6830 node.rhs = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope));4958 return Tag.bit_not.create(c.arena, operand);
6831 return &node.base;
6832 },4959 },
6833 .Asterisk => {4960 .Asterisk => {
6834 const node = try macroGroup(c, try parseCUnaryExpr(c, m, scope));4961 const operand = try parseCUnaryExpr(c, m, scope);
6835 return try transCreateNodePtrDeref(c, node);4962 return Tag.deref.create(c.arena, operand);
6836 },4963 },
6837 .Ampersand => {4964 .Ampersand => {
6838 const node = try transCreateNodeSimplePrefixOp(c, .AddressOf, .Ampersand, "&");4965 const operand = try parseCUnaryExpr(c, m, scope);
6839 node.rhs = try macroGroup(c, try parseCUnaryExpr(c, m, scope));4966 return Tag.address_of.create(c.arena, operand);
6840 return &node.base;
6841 },4967 },
6842 .Keyword_sizeof => {4968 .Keyword_sizeof => {
6843 const inner = if (m.peek().? == .LParen) blk: {4969 const operand = if (m.peek().? == .LParen) blk: {
6844 _ = m.next();4970 _ = m.next();
6845 // C grammar says this should be 'type-name' but we have to4971 // C grammar says this should be 'type-name' but we have to
6846 // use parseCMulExpr to correctly handle pointer types.4972 // use parseCMulExpr to correctly handle pointer types.
...@@ -6852,18 +4978,7 @@ fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Nod...@@ -6852,18 +4978,7 @@ fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Nod
6852 break :blk inner;4978 break :blk inner;
6853 } else try parseCUnaryExpr(c, m, scope);4979 } else try parseCUnaryExpr(c, m, scope);
68544980
6855 //(@import("std").meta.sizeof(dest, x))4981 return Tag.std_meta_sizeof.create(c.arena, operand);
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;
6867 },4982 },
6868 .Keyword_alignof => {4983 .Keyword_alignof => {
6869 // TODO this won't work if using <stdalign.h>'s4984 // 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...@@ -6874,16 +4989,13 @@ fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Nod
6874 }4989 }
6875 // C grammar says this should be 'type-name' but we have to4990 // C grammar says this should be 'type-name' but we have to
6876 // use parseCMulExpr to correctly handle pointer types.4991 // use parseCMulExpr to correctly handle pointer types.
6877 const inner = try parseCMulExpr(c, m, scope);4992 const operand = try parseCMulExpr(c, m, scope);
6878 if (m.next().? != .RParen) {4993 if (m.next().? != .RParen) {
6879 try m.fail(c, "unable to translate C expr: expected ')'", .{});4994 try m.fail(c, "unable to translate C expr: expected ')'", .{});
6880 return error.ParseError;4995 return error.ParseError;
6881 }4996 }
68824997
6883 const builtin_call = try c.createBuiltinCall("@alignOf", 1);4998 return Tag.alignof.create(c.arena, operand);
6884 builtin_call.params()[0] = inner;
6885 builtin_call.rparen_token = try appendToken(c, .RParen, ")");
6886 return &builtin_call.base;
6887 },4999 },
6888 .PlusPlus, .MinusMinus => {5000 .PlusPlus, .MinusMinus => {
6889 try m.fail(c, "TODO unary inc/dec expr", .{});5001 try m.fail(c, "TODO unary inc/dec expr", .{});
...@@ -6896,51 +5008,40 @@ fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Nod...@@ -6896,51 +5008,40 @@ fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Nod
6896 }5008 }
6897}5009}
68985010
6899fn tokenSlice(c: *Context, token: ast.TokenIndex) []u8 {5011fn getContainer(c: *Context, node: Node) ?Node {
6900 const tok = c.token_locs.items[token];5012 switch (node.tag()) {
6901 const slice = c.source_buffer.items[tok.start..tok.end];5013 .@"union",
6902 return if (mem.startsWith(u8, slice, "@\""))5014 .@"struct",
6903 slice[2 .. slice.len - 1]5015 .@"enum",
6904 else5016 .address_of,
6905 slice;5017 .bit_not,
6906}5018 .not,
69075019 .optional_type,
6908fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node {5020 .negate,
6909 switch (node.tag) {5021 .negate_wrap,
6910 .ContainerDecl,5022 .array_type,
6911 .AddressOf,5023 .c_pointer,
6912 .Await,5024 .single_pointer,
6913 .BitNot,
6914 .BoolNot,
6915 .OptionalType,
6916 .Negation,
6917 .NegationWrap,
6918 .Resume,
6919 .Try,
6920 .ArrayType,
6921 .ArrayTypeSentinel,
6922 .PtrType,
6923 .SliceType,
6924 => return node,5025 => return node,
69255026
6926 .Identifier => {5027 .identifier => {
6927 const ident = node.castTag(.Identifier).?;5028 const ident = node.castTag(.identifier).?;
6928 if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |value| {5029 if (c.global_scope.sym_table.get(ident.data)) |value| {
6929 if (value.cast(ast.Node.VarDecl)) |var_decl|5030 if (value.castTag(.var_decl)) |var_decl|
6930 return getContainer(c, var_decl.getInitNode().?);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);
6931 }5034 }
6932 },5035 },
69335036
6934 .Period => {5037 .field_access => {
6935 const infix = node.castTag(.Period).?;5038 const field_access = node.castTag(.field_access).?;
69365039
6937 if (getContainerTypeOf(c, infix.lhs)) |ty_node| {5040 if (getContainerTypeOf(c, field_access.data.lhs)) |ty_node| {
6938 if (ty_node.cast(ast.Node.ContainerDecl)) |container| {5041 if (ty_node.castTag(.@"struct") orelse ty_node.castTag(.@"union")) |container| {
6939 for (container.fieldsAndDecls()) |field_ref| {5042 for (container.data.fields) |field| {
6940 const field = field_ref.cast(ast.Node.ContainerField).?;5043 if (mem.eql(u8, field.name, field_access.data.field_name)) {
6941 const ident = infix.rhs.castTag(.Identifier).?;5044 return getContainer(c, field.type);
6942 if (mem.eql(u8, tokenSlice(c, field.name_token), tokenSlice(c, ident.token))) {
6943 return getContainer(c, field.type_expr.?);
6944 }5045 }
6945 }5046 }
6946 }5047 }
...@@ -6952,22 +5053,19 @@ fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node {...@@ -6952,22 +5053,19 @@ fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node {
6952 return null;5053 return null;
6953}5054}
69545055
6955fn getContainerTypeOf(c: *Context, ref: *ast.Node) ?*ast.Node {5056fn getContainerTypeOf(c: *Context, ref: Node) ?Node {
6956 if (ref.castTag(.Identifier)) |ident| {5057 if (ref.castTag(.identifier)) |ident| {
6957 if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |value| {5058 if (c.global_scope.sym_table.get(ident.data)) |value| {
6958 if (value.cast(ast.Node.VarDecl)) |var_decl| {5059 if (value.castTag(.var_decl)) |var_decl| {
6959 if (var_decl.getTypeNode()) |ty|5060 return getContainer(c, var_decl.data.type);
6960 return getContainer(c, ty);
6961 }5061 }
6962 }5062 }
6963 } else if (ref.castTag(.Period)) |infix| {5063 } else if (ref.castTag(.field_access)) |field_access| {
6964 if (getContainerTypeOf(c, infix.lhs)) |ty_node| {5064 if (getContainerTypeOf(c, field_access.data.lhs)) |ty_node| {
6965 if (ty_node.cast(ast.Node.ContainerDecl)) |container| {5065 if (ty_node.castTag(.@"struct") orelse ty_node.castTag(.@"union")) |container| {
6966 for (container.fieldsAndDecls()) |field_ref| {5066 for (container.data.fields) |field| {
6967 const field = field_ref.cast(ast.Node.ContainerField).?;5067 if (mem.eql(u8, field.name, field_access.data.field_name)) {
6968 const ident = infix.rhs.castTag(.Identifier).?;5068 return getContainer(c, field.type);
6969 if (mem.eql(u8, tokenSlice(c, field.name_token), tokenSlice(c, ident.token))) {
6970 return getContainer(c, field.type_expr.?);
6971 }5069 }
6972 }5070 }
6973 } else5071 } else
...@@ -6977,11 +5075,16 @@ fn getContainerTypeOf(c: *Context, ref: *ast.Node) ?*ast.Node {...@@ -6977,11 +5075,16 @@ fn getContainerTypeOf(c: *Context, ref: *ast.Node) ?*ast.Node {
6977 return null;5075 return null;
6978}5076}
69795077
6980fn getFnProto(c: *Context, ref: *ast.Node) ?*ast.Node.FnProto {5078fn getFnProto(c: *Context, ref: Node) ?*ast.Payload.Func {
6981 const init = if (ref.cast(ast.Node.VarDecl)) |v| v.getInitNode().? else return null;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;
6982 if (getContainerTypeOf(c, init)) |ty_node| {5085 if (getContainerTypeOf(c, init)) |ty_node| {
6983 if (ty_node.castTag(.OptionalType)) |prefix| {5086 if (ty_node.castTag(.optional_type)) |prefix| {
6984 if (prefix.rhs.cast(ast.Node.FnProto)) |fn_proto| {5087 if (prefix.data.castTag(.func)) |fn_proto| {
6985 return fn_proto;5088 return fn_proto;
6986 }5089 }
6987 }5090 }
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 {...@@ -28,6 +28,8 @@ pub const Type = extern union {
28 .i32,28 .i32,
29 .u64,29 .u64,
30 .i64,30 .i64,
31 .u128,
32 .i128,
31 .usize,33 .usize,
32 .isize,34 .isize,
33 .c_short,35 .c_short,
...@@ -357,6 +359,8 @@ pub const Type = extern union {...@@ -357,6 +359,8 @@ pub const Type = extern union {
357 .i32,359 .i32,
358 .u64,360 .u64,
359 .i64,361 .i64,
362 .u128,
363 .i128,
360 .usize,364 .usize,
361 .isize,365 .isize,
362 .c_short,366 .c_short,
...@@ -506,6 +510,8 @@ pub const Type = extern union {...@@ -506,6 +510,8 @@ pub const Type = extern union {
506 .i32,510 .i32,
507 .u64,511 .u64,
508 .i64,512 .i64,
513 .u128,
514 .i128,
509 .usize,515 .usize,
510 .isize,516 .isize,
511 .c_short,517 .c_short,
...@@ -772,6 +778,8 @@ pub const Type = extern union {...@@ -772,6 +778,8 @@ pub const Type = extern union {
772 .i32,778 .i32,
773 .u64,779 .u64,
774 .i64,780 .i64,
781 .u128,
782 .i128,
775 .usize,783 .usize,
776 .isize,784 .isize,
777 .c_short,785 .c_short,
...@@ -868,6 +876,7 @@ pub const Type = extern union {...@@ -868,6 +876,7 @@ pub const Type = extern union {
868 .i16, .u16 => return 2,876 .i16, .u16 => return 2,
869 .i32, .u32 => return 4,877 .i32, .u32 => return 4,
870 .i64, .u64 => return 8,878 .i64, .u64 => return 8,
879 .u128, .i128 => return 16,
871880
872 .isize,881 .isize,
873 .usize,882 .usize,
...@@ -1010,6 +1019,7 @@ pub const Type = extern union {...@@ -1010,6 +1019,7 @@ pub const Type = extern union {
1010 .i16, .u16 => return 2,1019 .i16, .u16 => return 2,
1011 .i32, .u32 => return 4,1020 .i32, .u32 => return 4,
1012 .i64, .u64 => return 8,1021 .i64, .u64 => return 8,
1022 .u128, .i128 => return 16,
10131023
1014 .@"anyframe", .anyframe_T, .isize, .usize => return @divExact(target.cpu.arch.ptrBitWidth(), 8),1024 .@"anyframe", .anyframe_T, .isize, .usize => return @divExact(target.cpu.arch.ptrBitWidth(), 8),
10151025
...@@ -1109,6 +1119,8 @@ pub const Type = extern union {...@@ -1109,6 +1119,8 @@ pub const Type = extern union {
1109 .i32,1119 .i32,
1110 .u64,1120 .u64,
1111 .i64,1121 .i64,
1122 .u128,
1123 .i128,
1112 .usize,1124 .usize,
1113 .isize,1125 .isize,
1114 .c_short,1126 .c_short,
...@@ -1191,6 +1203,8 @@ pub const Type = extern union {...@@ -1191,6 +1203,8 @@ pub const Type = extern union {
1191 .i32,1203 .i32,
1192 .u64,1204 .u64,
1193 .i64,1205 .i64,
1206 .u128,
1207 .i128,
1194 .usize,1208 .usize,
1195 .isize,1209 .isize,
1196 .c_short,1210 .c_short,
...@@ -1278,6 +1292,8 @@ pub const Type = extern union {...@@ -1278,6 +1292,8 @@ pub const Type = extern union {
1278 .i32,1292 .i32,
1279 .u64,1293 .u64,
1280 .i64,1294 .i64,
1295 .u128,
1296 .i128,
1281 .usize,1297 .usize,
1282 .isize,1298 .isize,
1283 .c_short,1299 .c_short,
...@@ -1359,6 +1375,8 @@ pub const Type = extern union {...@@ -1359,6 +1375,8 @@ pub const Type = extern union {
1359 .i32,1375 .i32,
1360 .u64,1376 .u64,
1361 .i64,1377 .i64,
1378 .u128,
1379 .i128,
1362 .usize,1380 .usize,
1363 .isize,1381 .isize,
1364 .c_short,1382 .c_short,
...@@ -1440,6 +1458,8 @@ pub const Type = extern union {...@@ -1440,6 +1458,8 @@ pub const Type = extern union {
1440 .i32,1458 .i32,
1441 .u64,1459 .u64,
1442 .i64,1460 .i64,
1461 .u128,
1462 .i128,
1443 .usize,1463 .usize,
1444 .isize,1464 .isize,
1445 .c_short,1465 .c_short,
...@@ -1522,6 +1542,8 @@ pub const Type = extern union {...@@ -1522,6 +1542,8 @@ pub const Type = extern union {
1522 .i32,1542 .i32,
1523 .u64,1543 .u64,
1524 .i64,1544 .i64,
1545 .u128,
1546 .i128,
1525 .usize,1547 .usize,
1526 .isize,1548 .isize,
1527 .c_short,1549 .c_short,
...@@ -1660,6 +1682,8 @@ pub const Type = extern union {...@@ -1660,6 +1682,8 @@ pub const Type = extern union {
1660 .i32 => unreachable,1682 .i32 => unreachable,
1661 .u64 => unreachable,1683 .u64 => unreachable,
1662 .i64 => unreachable,1684 .i64 => unreachable,
1685 .u128 => unreachable,
1686 .i128 => unreachable,
1663 .usize => unreachable,1687 .usize => unreachable,
1664 .isize => unreachable,1688 .isize => unreachable,
1665 .c_short => unreachable,1689 .c_short => unreachable,
...@@ -1776,6 +1800,8 @@ pub const Type = extern union {...@@ -1776,6 +1800,8 @@ pub const Type = extern union {
1776 .i32,1800 .i32,
1777 .u64,1801 .u64,
1778 .i64,1802 .i64,
1803 .u128,
1804 .i128,
1779 .usize,1805 .usize,
1780 .isize,1806 .isize,
1781 .c_short,1807 .c_short,
...@@ -1856,6 +1882,8 @@ pub const Type = extern union {...@@ -1856,6 +1882,8 @@ pub const Type = extern union {
1856 .i32,1882 .i32,
1857 .u64,1883 .u64,
1858 .i64,1884 .i64,
1885 .u128,
1886 .i128,
1859 .usize,1887 .usize,
1860 .isize,1888 .isize,
1861 .c_short,1889 .c_short,
...@@ -2009,6 +2037,8 @@ pub const Type = extern union {...@@ -2009,6 +2037,8 @@ pub const Type = extern union {
2009 .i16,2037 .i16,
2010 .i32,2038 .i32,
2011 .i64,2039 .i64,
2040 .u128,
2041 .i128,
2012 => true,2042 => true,
2013 };2043 };
2014 }2044 }
...@@ -2061,6 +2091,8 @@ pub const Type = extern union {...@@ -2061,6 +2091,8 @@ pub const Type = extern union {
2061 .i16,2091 .i16,
2062 .i32,2092 .i32,
2063 .i64,2093 .i64,
2094 .u128,
2095 .i128,
2064 .optional,2096 .optional,
2065 .optional_single_mut_pointer,2097 .optional_single_mut_pointer,
2066 .optional_single_const_pointer,2098 .optional_single_const_pointer,
...@@ -2167,6 +2199,8 @@ pub const Type = extern union {...@@ -2167,6 +2199,8 @@ pub const Type = extern union {
2167 .i32 => .{ .signedness = .signed, .bits = 32 },2199 .i32 => .{ .signedness = .signed, .bits = 32 },
2168 .u64 => .{ .signedness = .unsigned, .bits = 64 },2200 .u64 => .{ .signedness = .unsigned, .bits = 64 },
2169 .i64 => .{ .signedness = .signed, .bits = 64 },2201 .i64 => .{ .signedness = .signed, .bits = 64 },
2202 .u128 => .{ .signedness = .unsigned, .bits = 128 },
2203 .i128 => .{ .signedness = .signed, .bits = 128 },
2170 .usize => .{ .signedness = .unsigned, .bits = target.cpu.arch.ptrBitWidth() },2204 .usize => .{ .signedness = .unsigned, .bits = target.cpu.arch.ptrBitWidth() },
2171 .isize => .{ .signedness = .signed, .bits = target.cpu.arch.ptrBitWidth() },2205 .isize => .{ .signedness = .signed, .bits = target.cpu.arch.ptrBitWidth() },
2172 .c_short => .{ .signedness = .signed, .bits = CType.short.sizeInBits(target) },2206 .c_short => .{ .signedness = .signed, .bits = CType.short.sizeInBits(target) },
...@@ -2227,6 +2261,8 @@ pub const Type = extern union {...@@ -2227,6 +2261,8 @@ pub const Type = extern union {
2227 .i32,2261 .i32,
2228 .u64,2262 .u64,
2229 .i64,2263 .i64,
2264 .u128,
2265 .i128,
2230 .optional,2266 .optional,
2231 .optional_single_mut_pointer,2267 .optional_single_mut_pointer,
2232 .optional_single_const_pointer,2268 .optional_single_const_pointer,
...@@ -2333,6 +2369,8 @@ pub const Type = extern union {...@@ -2333,6 +2369,8 @@ pub const Type = extern union {
2333 .i32,2369 .i32,
2334 .u64,2370 .u64,
2335 .i64,2371 .i64,
2372 .u128,
2373 .i128,
2336 .usize,2374 .usize,
2337 .isize,2375 .isize,
2338 .c_short,2376 .c_short,
...@@ -2417,6 +2455,8 @@ pub const Type = extern union {...@@ -2417,6 +2455,8 @@ pub const Type = extern union {
2417 .i32,2455 .i32,
2418 .u64,2456 .u64,
2419 .i64,2457 .i64,
2458 .u128,
2459 .i128,
2420 .usize,2460 .usize,
2421 .isize,2461 .isize,
2422 .c_short,2462 .c_short,
...@@ -2500,6 +2540,8 @@ pub const Type = extern union {...@@ -2500,6 +2540,8 @@ pub const Type = extern union {
2500 .i32,2540 .i32,
2501 .u64,2541 .u64,
2502 .i64,2542 .i64,
2543 .u128,
2544 .i128,
2503 .usize,2545 .usize,
2504 .isize,2546 .isize,
2505 .c_short,2547 .c_short,
...@@ -2583,6 +2625,8 @@ pub const Type = extern union {...@@ -2583,6 +2625,8 @@ pub const Type = extern union {
2583 .i32,2625 .i32,
2584 .u64,2626 .u64,
2585 .i64,2627 .i64,
2628 .u128,
2629 .i128,
2586 .usize,2630 .usize,
2587 .isize,2631 .isize,
2588 .c_short,2632 .c_short,
...@@ -2663,6 +2707,8 @@ pub const Type = extern union {...@@ -2663,6 +2707,8 @@ pub const Type = extern union {
2663 .i32,2707 .i32,
2664 .u64,2708 .u64,
2665 .i64,2709 .i64,
2710 .u128,
2711 .i128,
2666 .usize,2712 .usize,
2667 .isize,2713 .isize,
2668 .c_short,2714 .c_short,
...@@ -2743,6 +2789,8 @@ pub const Type = extern union {...@@ -2743,6 +2789,8 @@ pub const Type = extern union {
2743 .i32,2789 .i32,
2744 .u64,2790 .u64,
2745 .i64,2791 .i64,
2792 .u128,
2793 .i128,
2746 .usize,2794 .usize,
2747 .isize,2795 .isize,
2748 .c_short,2796 .c_short,
...@@ -2793,6 +2841,8 @@ pub const Type = extern union {...@@ -2793,6 +2841,8 @@ pub const Type = extern union {
2793 .i32,2841 .i32,
2794 .u64,2842 .u64,
2795 .i64,2843 .i64,
2844 .u128,
2845 .i128,
2796 .usize,2846 .usize,
2797 .isize,2847 .isize,
2798 .c_short,2848 .c_short,
...@@ -2874,6 +2924,8 @@ pub const Type = extern union {...@@ -2874,6 +2924,8 @@ pub const Type = extern union {
2874 .i32,2924 .i32,
2875 .u64,2925 .u64,
2876 .i64,2926 .i64,
2927 .u128,
2928 .i128,
2877 .usize,2929 .usize,
2878 .isize,2930 .isize,
2879 .c_short,2931 .c_short,
...@@ -2971,6 +3023,8 @@ pub const Type = extern union {...@@ -2971,6 +3023,8 @@ pub const Type = extern union {
2971 .i32,3023 .i32,
2972 .u64,3024 .u64,
2973 .i64,3025 .i64,
3026 .u128,
3027 .i128,
2974 .usize,3028 .usize,
2975 .isize,3029 .isize,
2976 .c_short,3030 .c_short,
...@@ -3060,6 +3114,8 @@ pub const Type = extern union {...@@ -3060,6 +3114,8 @@ pub const Type = extern union {
3060 .i32,3114 .i32,
3061 .u64,3115 .u64,
3062 .i64,3116 .i64,
3117 .u128,
3118 .i128,
3063 .usize,3119 .usize,
3064 .isize,3120 .isize,
3065 .c_short,3121 .c_short,
...@@ -3193,6 +3249,8 @@ pub const Type = extern union {...@@ -3193,6 +3249,8 @@ pub const Type = extern union {
3193 i32,3249 i32,
3194 u64,3250 u64,
3195 i64,3251 i64,
3252 u128,
3253 i128,
3196 usize,3254 usize,
3197 isize,3255 isize,
3198 c_short,3256 c_short,
...@@ -3277,6 +3335,8 @@ pub const Type = extern union {...@@ -3277,6 +3335,8 @@ pub const Type = extern union {
3277 .i32,3335 .i32,
3278 .u64,3336 .u64,
3279 .i64,3337 .i64,
3338 .u128,
3339 .i128,
3280 .usize,3340 .usize,
3281 .isize,3341 .isize,
3282 .c_short,3342 .c_short,
...@@ -3352,6 +3412,11 @@ pub const Type = extern union {...@@ -3352,6 +3412,11 @@ pub const Type = extern union {
3352 };3412 };
3353 }3413 }
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
3355 pub fn create(comptime t: Tag, ally: *Allocator, data: Data(t)) error{OutOfMemory}!Type {3420 pub fn create(comptime t: Tag, ally: *Allocator, data: Data(t)) error{OutOfMemory}!Type {
3356 const ptr = try ally.create(t.Type());3421 const ptr = try ally.create(t.Type());
3357 ptr.* = .{3422 ptr.* = .{
src/zig_clang.cpp+5
...@@ -2244,6 +2244,11 @@ unsigned ZigClangAPSInt_getNumWords(const ZigClangAPSInt *self) {...@@ -2244,6 +2244,11 @@ unsigned ZigClangAPSInt_getNumWords(const ZigClangAPSInt *self) {
2244 return casted->getNumWords();2244 return casted->getNumWords();
2245}2245}
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
2247uint64_t ZigClangAPInt_getLimitedValue(const ZigClangAPInt *self, uint64_t limit) {2252uint64_t ZigClangAPInt_getLimitedValue(const ZigClangAPInt *self, uint64_t limit) {
2248 auto casted = reinterpret_cast<const llvm::APInt *>(self);2253 auto casted = reinterpret_cast<const llvm::APInt *>(self);
2249 return casted->getLimitedValue(limit);2254 return casted->getLimitedValue(limit);
src/zig_clang.h+1
...@@ -1097,6 +1097,7 @@ ZIG_EXTERN_C const struct ZigClangAPSInt *ZigClangAPSInt_negate(const struct Zig...@@ -1097,6 +1097,7 @@ ZIG_EXTERN_C const struct ZigClangAPSInt *ZigClangAPSInt_negate(const struct Zig
1097ZIG_EXTERN_C void ZigClangAPSInt_free(const struct ZigClangAPSInt *self);1097ZIG_EXTERN_C void ZigClangAPSInt_free(const struct ZigClangAPSInt *self);
1098ZIG_EXTERN_C const uint64_t *ZigClangAPSInt_getRawData(const struct ZigClangAPSInt *self);1098ZIG_EXTERN_C const uint64_t *ZigClangAPSInt_getRawData(const struct ZigClangAPSInt *self);
1099ZIG_EXTERN_C unsigned ZigClangAPSInt_getNumWords(const struct ZigClangAPSInt *self);1099ZIG_EXTERN_C unsigned ZigClangAPSInt_getNumWords(const struct ZigClangAPSInt *self);
1100ZIG_EXTERN_C bool ZigClangAPSInt_lessThanEqual(const struct ZigClangAPSInt *self, uint64_t rhs);
11001101
1101ZIG_EXTERN_C uint64_t ZigClangAPInt_getLimitedValue(const struct ZigClangAPInt *self, uint64_t limit);1102ZIG_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 {...@@ -53,6 +53,9 @@ pub const Inst = struct {
53 indexable_ptr_len,53 indexable_ptr_len,
54 /// Function parameter value. These must be first in a function's main block,54 /// Function parameter value. These must be first in a function's main block,
55 /// in respective order with the parameters.55 /// 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.
56 arg,59 arg,
57 /// Type coercion.60 /// Type coercion.
58 as,61 as,
...@@ -169,8 +172,10 @@ pub const Inst = struct {...@@ -169,8 +172,10 @@ pub const Inst = struct {
169 floatcast,172 floatcast,
170 /// Declare a function body.173 /// Declare a function body.
171 @"fn",174 @"fn",
172 /// Returns a function type.175 /// Returns a function type, assuming unspecified calling convention.
173 fntype,176 fn_type,
177 /// Returns a function type, with a calling convention instruction operand.
178 fn_type_cc,
174 /// @import(operand)179 /// @import(operand)
175 import,180 import,
176 /// Integer literal.181 /// Integer literal.
...@@ -340,6 +345,8 @@ pub const Inst = struct {...@@ -340,6 +345,8 @@ pub const Inst = struct {
340 void_value,345 void_value,
341 /// A switch expression.346 /// A switch expression.
342 switchbr,347 switchbr,
348 /// Same as `switchbr` but the target is a pointer to the value being switched on.
349 switchbr_ref,
343 /// A range in a switch case, `lhs...rhs`.350 /// A range in a switch case, `lhs...rhs`.
344 /// Only checks that `lhs >= rhs` if they are ints, everything else is351 /// Only checks that `lhs >= rhs` if they are ints, everything else is
345 /// validated by the .switch instruction.352 /// validated by the .switch instruction.
...@@ -450,6 +457,8 @@ pub const Inst = struct {...@@ -450,6 +457,8 @@ pub const Inst = struct {
450 .block_comptime_flat,457 .block_comptime_flat,
451 => Block,458 => Block,
452459
460 .switchbr, .switchbr_ref => SwitchBr,
461
453 .arg => Arg,462 .arg => Arg,
454 .array_type_sentinel => ArrayTypeSentinel,463 .array_type_sentinel => ArrayTypeSentinel,
455 .@"break" => Break,464 .@"break" => Break,
...@@ -471,7 +480,8 @@ pub const Inst = struct {...@@ -471,7 +480,8 @@ pub const Inst = struct {
471 .@"export" => Export,480 .@"export" => Export,
472 .param_type => ParamType,481 .param_type => ParamType,
473 .primitive => Primitive,482 .primitive => Primitive,
474 .fntype => FnType,483 .fn_type => FnType,
484 .fn_type_cc => FnTypeCc,
475 .elem_ptr, .elem_val => Elem,485 .elem_ptr, .elem_val => Elem,
476 .condbr => CondBr,486 .condbr => CondBr,
477 .ptr_type => PtrType,487 .ptr_type => PtrType,
...@@ -485,7 +495,6 @@ pub const Inst = struct {...@@ -485,7 +495,6 @@ pub const Inst = struct {
485 .enum_type => EnumType,495 .enum_type => EnumType,
486 .union_type => UnionType,496 .union_type => UnionType,
487 .struct_type => StructType,497 .struct_type => StructType,
488 .switchbr => SwitchBr,
489 };498 };
490 }499 }
491500
...@@ -546,7 +555,8 @@ pub const Inst = struct {...@@ -546,7 +555,8 @@ pub const Inst = struct {
546 .field_ptr_named,555 .field_ptr_named,
547 .field_val_named,556 .field_val_named,
548 .@"fn",557 .@"fn",
549 .fntype,558 .fn_type,
559 .fn_type_cc,
550 .int,560 .int,
551 .intcast,561 .intcast,
552 .int_type,562 .int_type,
...@@ -614,7 +624,6 @@ pub const Inst = struct {...@@ -614,7 +624,6 @@ pub const Inst = struct {
614 .struct_type,624 .struct_type,
615 .void_value,625 .void_value,
616 .switch_range,626 .switch_range,
617 .switchbr,
618 => false,627 => false,
619628
620 .@"break",629 .@"break",
...@@ -629,6 +638,8 @@ pub const Inst = struct {...@@ -629,6 +638,8 @@ pub const Inst = struct {
629 .container_field_named,638 .container_field_named,
630 .container_field_typed,639 .container_field_typed,
631 .container_field,640 .container_field,
641 .switchbr,
642 .switchbr_ref,
632 => true,643 => true,
633 };644 };
634 }645 }
...@@ -689,6 +700,8 @@ pub const Inst = struct {...@@ -689,6 +700,8 @@ pub const Inst = struct {
689 base: Inst,700 base: Inst,
690701
691 positionals: struct {702 positionals: struct {
703 /// This exists to be passed to the arg TZIR instruction, which
704 /// needs it for debug info.
692 name: []const u8,705 name: []const u8,
693 },706 },
694 kw_args: struct {},707 kw_args: struct {},
...@@ -725,6 +738,8 @@ pub const Inst = struct {...@@ -725,6 +738,8 @@ pub const Inst = struct {
725 kw_args: struct {},738 kw_args: struct {},
726 };739 };
727740
741 // TODO break this into multiple call instructions to avoid paying the cost
742 // of the calling convention field most of the time.
728 pub const Call = struct {743 pub const Call = struct {
729 pub const base_tag = Tag.call;744 pub const base_tag = Tag.call;
730 base: Inst,745 base: Inst,
...@@ -732,10 +747,9 @@ pub const Inst = struct {...@@ -732,10 +747,9 @@ pub const Inst = struct {
732 positionals: struct {747 positionals: struct {
733 func: *Inst,748 func: *Inst,
734 args: []*Inst,749 args: []*Inst,
735 },
736 kw_args: struct {
737 modifier: std.builtin.CallOptions.Modifier = .auto,750 modifier: std.builtin.CallOptions.Modifier = .auto,
738 },751 },
752 kw_args: struct {},
739 };753 };
740754
741 pub const DeclRef = struct {755 pub const DeclRef = struct {
...@@ -849,8 +863,8 @@ pub const Inst = struct {...@@ -849,8 +863,8 @@ pub const Inst = struct {
849 kw_args: struct {863 kw_args: struct {
850 @"volatile": bool = false,864 @"volatile": bool = false,
851 output: ?*Inst = null,865 output: ?*Inst = null,
852 inputs: []*Inst = &[0]*Inst{},866 inputs: []const []const u8 = &.{},
853 clobbers: []*Inst = &[0]*Inst{},867 clobbers: []const []const u8 = &.{},
854 args: []*Inst = &[0]*Inst{},868 args: []*Inst = &[0]*Inst{},
855 },869 },
856 };870 };
...@@ -867,7 +881,18 @@ pub const Inst = struct {...@@ -867,7 +881,18 @@ pub const Inst = struct {
867 };881 };
868882
869 pub const FnType = struct {883 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;
871 base: Inst,896 base: Inst,
872897
873 positionals: struct {898 positionals: struct {
...@@ -1167,20 +1192,12 @@ pub const Inst = struct {...@@ -1167,20 +1192,12 @@ pub const Inst = struct {
1167 },1192 },
1168 kw_args: struct {1193 kw_args: struct {
1169 init_inst: ?*Inst = null,1194 init_inst: ?*Inst = null,
1170 init_kind: InitKind = .none,1195 has_enum_token: bool,
1171 layout: std.builtin.TypeInfo.ContainerLayout = .Auto,1196 layout: std.builtin.TypeInfo.ContainerLayout = .Auto,
1172 },1197 },
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 };
1180 };1198 };
11811199
1182 pub const SwitchBr = struct {1200 pub const SwitchBr = struct {
1183 pub const base_tag = Tag.switchbr;
1184 base: Inst,1201 base: Inst,
11851202
1186 positionals: struct {1203 positionals: struct {
...@@ -1189,14 +1206,12 @@ pub const Inst = struct {...@@ -1189,14 +1206,12 @@ pub const Inst = struct {
1189 items: []*Inst,1206 items: []*Inst,
1190 cases: []Case,1207 cases: []Case,
1191 else_body: Body,1208 else_body: Body,
1192 },
1193 kw_args: struct {
1194 /// Pointer to first range if such exists.1209 /// Pointer to first range if such exists.
1195 range: ?*Inst = null,1210 range: ?*Inst = null,
1196 special_prong: SpecialProng = .none,1211 special_prong: SpecialProng = .none,
1197 },1212 },
1213 kw_args: struct {},
11981214
1199 // Not anonymous due to stage1 limitations
1200 pub const SpecialProng = enum {1215 pub const SpecialProng = enum {
1201 none,1216 none,
1202 @"else",1217 @"else",
...@@ -1391,6 +1406,7 @@ const Writer = struct {...@@ -1391,6 +1406,7 @@ const Writer = struct {
1391 }1406 }
1392 switch (@TypeOf(param)) {1407 switch (@TypeOf(param)) {
1393 *Inst => return self.writeInstParamToStream(stream, param),1408 *Inst => return self.writeInstParamToStream(stream, param),
1409 ?*Inst => return self.writeInstParamToStream(stream, param.?),
1394 []*Inst => {1410 []*Inst => {
1395 try stream.writeByte('[');1411 try stream.writeByte('[');
1396 for (param) |inst, i| {1412 for (param) |inst, i| {
...@@ -1458,7 +1474,7 @@ const Writer = struct {...@@ -1458,7 +1474,7 @@ const Writer = struct {
1458 const name = self.loop_table.get(param).?;1474 const name = self.loop_table.get(param).?;
1459 return stream.print("\"{}\"", .{std.zig.fmtEscapes(name)});1475 return stream.print("\"{}\"", .{std.zig.fmtEscapes(name)});
1460 },1476 },
1461 [][]const u8 => {1477 [][]const u8, []const []const u8 => {
1462 try stream.writeByte('[');1478 try stream.writeByte('[');
1463 for (param) |str, i| {1479 for (param) |str, i| {
1464 if (i != 0) {1480 if (i != 0) {
...@@ -1586,6 +1602,7 @@ const DumpTzir = struct {...@@ -1586,6 +1602,7 @@ const DumpTzir = struct {
1586 .unreach,1602 .unreach,
1587 .breakpoint,1603 .breakpoint,
1588 .dbg_stmt,1604 .dbg_stmt,
1605 .arg,
1589 => {},1606 => {},
15901607
1591 .ref,1608 .ref,
...@@ -1630,8 +1647,6 @@ const DumpTzir = struct {...@@ -1630,8 +1647,6 @@ const DumpTzir = struct {
1630 try dtz.findConst(bin_op.rhs);1647 try dtz.findConst(bin_op.rhs);
1631 },1648 },
16321649
1633 .arg => {},
1634
1635 .br => {1650 .br => {
1636 const br = inst.castTag(.br).?;1651 const br = inst.castTag(.br).?;
1637 try dtz.findConst(&br.block.base);1652 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!...@@ -91,7 +91,8 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
91 .@"fn" => return zirFn(mod, scope, old_inst.castTag(.@"fn").?),91 .@"fn" => return zirFn(mod, scope, old_inst.castTag(.@"fn").?),
92 .@"export" => return zirExport(mod, scope, old_inst.castTag(.@"export").?),92 .@"export" => return zirExport(mod, scope, old_inst.castTag(.@"export").?),
93 .primitive => return zirPrimitive(mod, scope, old_inst.castTag(.primitive).?),93 .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).?),
95 .intcast => return zirIntcast(mod, scope, old_inst.castTag(.intcast).?),96 .intcast => return zirIntcast(mod, scope, old_inst.castTag(.intcast).?),
96 .bitcast => return zirBitcast(mod, scope, old_inst.castTag(.bitcast).?),97 .bitcast => return zirBitcast(mod, scope, old_inst.castTag(.bitcast).?),
97 .floatcast => return zirFloatcast(mod, scope, old_inst.castTag(.floatcast).?),98 .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!...@@ -154,7 +155,8 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
154 .bool_and => return zirBoolOp(mod, scope, old_inst.castTag(.bool_and).?),155 .bool_and => return zirBoolOp(mod, scope, old_inst.castTag(.bool_and).?),
155 .bool_or => return zirBoolOp(mod, scope, old_inst.castTag(.bool_or).?),156 .bool_or => return zirBoolOp(mod, scope, old_inst.castTag(.bool_or).?),
156 .void_value => return mod.constVoid(scope, old_inst.src),157 .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),
158 .switch_range => return zirSwitchRange(mod, scope, old_inst.castTag(.switch_range).?),160 .switch_range => return zirSwitchRange(mod, scope, old_inst.castTag(.switch_range).?),
159161
160 .container_field_named,162 .container_field_named,
...@@ -957,11 +959,11 @@ fn zirCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst {...@@ -957,11 +959,11 @@ fn zirCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst {
957 );959 );
958 }960 }
959961
960 if (inst.kw_args.modifier == .compile_time) {962 if (inst.positionals.modifier == .compile_time) {
961 return mod.fail(scope, inst.base.src, "TODO implement comptime function calls", .{});963 return mod.fail(scope, inst.base.src, "TODO implement comptime function calls", .{});
962 }964 }
963 if (inst.kw_args.modifier != .auto) {965 if (inst.positionals.modifier != .auto) {
964 return mod.fail(scope, inst.base.src, "TODO implement call with modifier {}", .{inst.kw_args.modifier});966 return mod.fail(scope, inst.base.src, "TODO implement call with modifier {}", .{inst.positionals.modifier});
965 }967 }
966968
967 // TODO handle function calls of generic functions969 // TODO handle function calls of generic functions
...@@ -979,8 +981,8 @@ fn zirCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst {...@@ -979,8 +981,8 @@ fn zirCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst {
979 const ret_type = func.ty.fnReturnType();981 const ret_type = func.ty.fnReturnType();
980982
981 const b = try mod.requireFunctionBlock(scope, inst.base.src);983 const b = try mod.requireFunctionBlock(scope, inst.base.src);
982 const is_comptime_call = b.is_comptime or inst.kw_args.modifier == .compile_time;984 const is_comptime_call = b.is_comptime or inst.positionals.modifier == .compile_time;
983 const is_inline_call = is_comptime_call or inst.kw_args.modifier == .always_inline or985 const is_inline_call = is_comptime_call or inst.positionals.modifier == .always_inline or
984 func.ty.fnCallingConvention() == .Inline;986 func.ty.fnCallingConvention() == .Inline;
985 if (is_inline_call) {987 if (is_inline_call) {
986 const func_val = try mod.resolveConstValue(scope, func);988 const func_val = try mod.resolveConstValue(scope, func);
...@@ -1294,34 +1296,69 @@ fn zirEnsureErrPayloadVoid(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp)...@@ -1294,34 +1296,69 @@ fn zirEnsureErrPayloadVoid(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp)
1294fn zirFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst {1296fn zirFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst {
1295 const tracy = trace(@src());1297 const tracy = trace(@src());
1296 defer tracy.end();1298 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
1298 const cc_tv = try resolveInstConst(mod, scope, fntype.positionals.cc);1314 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
1299 const cc_str = cc_tv.val.castTag(.enum_literal).?.data;1317 const cc_str = cc_tv.val.castTag(.enum_literal).?.data;
1300 const cc = std.meta.stringToEnum(std.builtin.CallingConvention, cc_str) orelse1318 const cc = std.meta.stringToEnum(std.builtin.CallingConvention, cc_str) orelse
1301 return mod.fail(scope, fntype.positionals.cc.src, "Unknown calling convention {s}", .{cc_str});1319 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
1303 // Hot path for some common function types.1340 // Hot path for some common function types.
1304 if (fntype.positionals.param_types.len == 0) {1341 if (zir_param_types.len == 0) {
1305 if (return_type.zigTypeTag() == .NoReturn and cc == .Unspecified) {1342 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));
1307 }1344 }
13081345
1309 if (return_type.zigTypeTag() == .Void and cc == .Unspecified) {1346 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));
1311 }1348 }
13121349
1313 if (return_type.zigTypeTag() == .NoReturn and cc == .Naked) {1350 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));
1315 }1352 }
13161353
1317 if (return_type.zigTypeTag() == .Void and cc == .C) {1354 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));
1319 }1356 }
1320 }1357 }
13211358
1322 const arena = scope.arena();1359 const arena = scope.arena();
1323 const param_types = try arena.alloc(Type, fntype.positionals.param_types.len);1360 const param_types = try arena.alloc(Type, zir_param_types.len);
1324 for (fntype.positionals.param_types) |param_type, i| {1361 for (zir_param_types) |param_type, i| {
1325 const resolved = try resolveType(mod, scope, param_type);1362 const resolved = try resolveType(mod, scope, param_type);
1326 // TODO skip for comptime params1363 // TODO skip for comptime params
1327 if (!resolved.isValidVarType(false)) {1364 if (!resolved.isValidVarType(false)) {
...@@ -1335,7 +1372,7 @@ fn zirFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*...@@ -1335,7 +1372,7 @@ fn zirFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*
1335 .return_type = return_type,1372 .return_type = return_type,
1336 .cc = cc,1373 .cc = cc,
1337 });1374 });
1338 return mod.constType(scope, fntype.base.src, fn_ty);1375 return mod.constType(scope, zir_inst.src, fn_ty);
1339}1376}
13401377
1341fn zirPrimitive(mod: *Module, scope: *Scope, primitive: *zir.Inst.Primitive) InnerError!*Inst {1378fn 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...@@ -1554,10 +1591,15 @@ fn zirSwitchRange(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError
1554 return mod.constVoid(scope, inst.base.src);1591 return mod.constVoid(scope, inst.base.src);
1555}1592}
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 {
1558 const tracy = trace(@src());1595 const tracy = trace(@src());
1559 defer tracy.end();1596 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;
1561 try validateSwitch(mod, scope, target, inst);1603 try validateSwitch(mod, scope, target, inst);
15621604
1563 if (try mod.resolveDefinedValue(scope, target)) |target_val| {1605 if (try mod.resolveDefinedValue(scope, target)) |target_val| {
...@@ -1626,13 +1668,13 @@ fn zirSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) InnerError...@@ -1626,13 +1668,13 @@ fn zirSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) InnerError
16261668
1627fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.SwitchBr) InnerError!void {1669fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.SwitchBr) InnerError!void {
1628 // validate usage of '_' prongs1670 // 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) {
1630 return mod.fail(scope, inst.base.src, "'_' prong only allowed when switching on non-exhaustive enums", .{});1672 return mod.fail(scope, inst.base.src, "'_' prong only allowed when switching on non-exhaustive enums", .{});
1631 // TODO notes "'_' prong here" inst.positionals.cases[last].src1673 // TODO notes "'_' prong here" inst.positionals.cases[last].src
1632 }1674 }
16331675
1634 // check that target type supports ranges1676 // check that target type supports ranges
1635 if (inst.kw_args.range) |range_inst| {1677 if (inst.positionals.range) |range_inst| {
1636 switch (target.ty.zigTypeTag()) {1678 switch (target.ty.zigTypeTag()) {
1637 .Int, .ComptimeInt => {},1679 .Int, .ComptimeInt => {},
1638 else => {1680 else => {
...@@ -1683,14 +1725,14 @@ fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.Sw...@@ -1683,14 +1725,14 @@ fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.Sw
1683 const start = try target.ty.minInt(&arena, mod.getTarget());1725 const start = try target.ty.minInt(&arena, mod.getTarget());
1684 const end = try target.ty.maxInt(&arena, mod.getTarget());1726 const end = try target.ty.maxInt(&arena, mod.getTarget());
1685 if (try range_set.spans(start, end)) {1727 if (try range_set.spans(start, end)) {
1686 if (inst.kw_args.special_prong == .@"else") {1728 if (inst.positionals.special_prong == .@"else") {
1687 return mod.fail(scope, inst.base.src, "unreachable else prong, all cases already handled", .{});1729 return mod.fail(scope, inst.base.src, "unreachable else prong, all cases already handled", .{});
1688 }1730 }
1689 return;1731 return;
1690 }1732 }
1691 }1733 }
16921734
1693 if (inst.kw_args.special_prong != .@"else") {1735 if (inst.positionals.special_prong != .@"else") {
1694 return mod.fail(scope, inst.base.src, "switch must handle all possibilities", .{});1736 return mod.fail(scope, inst.base.src, "switch must handle all possibilities", .{});
1695 }1737 }
1696 },1738 },
...@@ -1710,15 +1752,15 @@ fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.Sw...@@ -1710,15 +1752,15 @@ fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.Sw
1710 return mod.fail(scope, item.src, "duplicate switch value", .{});1752 return mod.fail(scope, item.src, "duplicate switch value", .{});
1711 }1753 }
1712 }1754 }
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") {
1714 return mod.fail(scope, inst.base.src, "switch must handle all possibilities", .{});1756 return mod.fail(scope, inst.base.src, "switch must handle all possibilities", .{});
1715 }1757 }
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") {
1717 return mod.fail(scope, inst.base.src, "unreachable else prong, all cases already handled", .{});1759 return mod.fail(scope, inst.base.src, "unreachable else prong, all cases already handled", .{});
1718 }1760 }
1719 },1761 },
1720 .EnumLiteral, .Void, .Fn, .Pointer, .Type => {1762 .EnumLiteral, .Void, .Fn, .Pointer, .Type => {
1721 if (inst.kw_args.special_prong != .@"else") {1763 if (inst.positionals.special_prong != .@"else") {
1722 return mod.fail(scope, inst.base.src, "else prong required when switching on type '{}'", .{target.ty});1764 return mod.fail(scope, inst.base.src, "else prong required when switching on type '{}'", .{target.ty});
1723 }1765 }
17241766
...@@ -1981,19 +2023,21 @@ fn zirDeref(mod: *Module, scope: *Scope, deref: *zir.Inst.UnOp) InnerError!*Inst...@@ -1981,19 +2023,21 @@ fn zirDeref(mod: *Module, scope: *Scope, deref: *zir.Inst.UnOp) InnerError!*Inst
1981fn zirAsm(mod: *Module, scope: *Scope, assembly: *zir.Inst.Asm) InnerError!*Inst {2023fn zirAsm(mod: *Module, scope: *Scope, assembly: *zir.Inst.Asm) InnerError!*Inst {
1982 const tracy = trace(@src());2024 const tracy = trace(@src());
1983 defer tracy.end();2025 defer tracy.end();
2026
1984 const return_type = try resolveType(mod, scope, assembly.positionals.return_type);2027 const return_type = try resolveType(mod, scope, assembly.positionals.return_type);
1985 const asm_source = try resolveConstString(mod, scope, assembly.positionals.asm_source);2028 const asm_source = try resolveConstString(mod, scope, assembly.positionals.asm_source);
1986 const output = if (assembly.kw_args.output) |o| try resolveConstString(mod, scope, o) else null;2029 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);2031 const arena = scope.arena();
1989 const clobbers = try scope.arena().alloc([]const u8, assembly.kw_args.clobbers.len);2032 const inputs = try arena.alloc([]const u8, assembly.kw_args.inputs.len);
1990 const args = try scope.arena().alloc(*Inst, assembly.kw_args.args.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
1992 for (inputs) |*elem, i| {2036 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]);
1994 }2038 }
1995 for (clobbers) |*elem, i| {2039 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]);
1997 }2041 }
1998 for (args) |*elem, i| {2042 for (args) |*elem, i| {
1999 const arg = try resolveInst(mod, scope, assembly.kw_args.args[i]);2043 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");...@@ -3,6 +3,33 @@ const tests = @import("tests.zig");
3const nl = std.cstr.line_sep;3const nl = std.cstr.line_sep;
44
5pub fn addCases(cases: *tests.RunTranslatedCContext) void {5pub 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
6 cases.add("parenthesized string literal",33 cases.add("parenthesized string literal",
7 \\void foo(const char *s) {}34 \\void foo(const char *s) {}
8 \\int main(void) {35 \\int main(void) {
...@@ -922,4 +949,13 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {...@@ -922,4 +949,13 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
922 \\ return 0;949 \\ return 0;
923 \\}950 \\}
924 , "");951 , "");
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 , "");
925}961}
test/stage2/test.zig+8-4
...@@ -1088,7 +1088,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1088,7 +1088,7 @@ pub fn addCases(ctx: *TestContext) !void {
1088 \\ _ = foo;1088 \\ _ = foo;
1089 \\}1089 \\}
1090 \\extern var foo;1090 \\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"});
1092 }1092 }
10931093
1094 {1094 {
...@@ -1194,12 +1194,12 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1194,12 +1194,12 @@ pub fn addCases(ctx: *TestContext) !void {
1194 \\comptime {1194 \\comptime {
1195 \\ foo: while (true) {}1195 \\ foo: while (true) {}
1196 \\}1196 \\}
1197 , &[_][]const u8{":2:5: error: unused while label"});1197 , &[_][]const u8{":2:5: error: unused while loop label"});
1198 case.addError(1198 case.addError(
1199 \\comptime {1199 \\comptime {
1200 \\ foo: for ("foo") |_| {}1200 \\ foo: for ("foo") |_| {}
1201 \\}1201 \\}
1202 , &[_][]const u8{":2:5: error: unused for label"});1202 , &[_][]const u8{":2:5: error: unused for loop label"});
1203 case.addError(1203 case.addError(
1204 \\comptime {1204 \\comptime {
1205 \\ blk: {blk: {}}1205 \\ blk: {blk: {}}
...@@ -1294,6 +1294,10 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1294,6 +1294,10 @@ pub fn addCases(ctx: *TestContext) !void {
1294 ,1294 ,
1295 "",1295 "",
1296 );1296 );
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.
1297 case.addError(1301 case.addError(
1298 \\export fn _start() noreturn {1302 \\export fn _start() noreturn {
1299 \\ const y = fibonacci(999);1303 \\ const y = fibonacci(999);
...@@ -1314,7 +1318,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1314,7 +1318,7 @@ pub fn addCases(ctx: *TestContext) !void {
1314 \\ );1318 \\ );
1315 \\ unreachable;1319 \\ unreachable;
1316 \\}1320 \\}
1317 , &[_][]const u8{":8:10: error: evaluation exceeded 1000 backwards branches"});1321 , &[_][]const u8{":8:19: error: evaluation exceeded 1000 backwards branches"});
1318 }1322 }
1319 {1323 {
1320 var case = ctx.exe("orelse at comptime", linux_x64);1324 var case = ctx.exe("orelse at comptime", linux_x64);
test/translate_c.zig+478-268
...@@ -3,12 +3,208 @@ const std = @import("std");...@@ -3,12 +3,208 @@ const std = @import("std");
3const CrossTarget = std.zig.CrossTarget;3const CrossTarget = std.zig.CrossTarget;
44
5pub fn addCases(cases: *tests.TranslateCContext) void {5pub 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",
7 \\int foo(int bar, ...) {203 \\int foo(int bar, ...) {
8 \\ return 1;204 \\ return 1;
9 \\}205 \\}
10 , &[_][]const u8{206 , &[_][]const u8{
11 \\warning: TODO unable to translate variadic function, demoted to declaration207 \\warning: TODO unable to translate variadic function, demoted to extern
12 \\pub extern fn foo(bar: c_int, ...) c_int;208 \\pub extern fn foo(bar: c_int, ...) c_int;
13 });209 });
14210
...@@ -21,11 +217,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -21,11 +217,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
21 \\ Foo *bar;217 \\ Foo *bar;
22 \\} Bar;218 \\} Bar;
23 , &[_][]const u8{219 , &[_][]const u8{
24 \\const struct_unnamed_1 = //220 \\source.h:1:9: warning: struct demoted to opaque type - unable to translate type of field foo
25 ,221 \\const struct_unnamed_1 = opaque {};
26 \\warning: unsupported type: 'Atomic'
27 \\ opaque {}; //
28 ,
29 \\pub const Foo = struct_unnamed_1;222 \\pub const Foo = struct_unnamed_1;
30 \\const struct_unnamed_2 = extern struct {223 \\const struct_unnamed_2 = extern struct {
31 \\ bar: ?*Foo,224 \\ bar: ?*Foo,
...@@ -43,8 +236,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -43,8 +236,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
43 ,236 ,
44 \\pub const VALUE = ((((1 + (2 * 3)) + (4 * 5)) + 6) << 7) | @boolToInt(8 == 9);237 \\pub const VALUE = ((((1 + (2 * 3)) + (4 * 5)) + 6) << 7) | @boolToInt(8 == 9);
45 ,238 ,
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)) {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)) {
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);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);
48 \\}241 \\}
49 });242 });
50243
...@@ -57,7 +250,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -57,7 +250,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
57 \\pub export var bar: f32 = @import("std").mem.zeroes(f32);250 \\pub export var bar: f32 = @import("std").mem.zeroes(f32);
58 \\threadlocal var bar_1: c_int = 2;251 \\threadlocal var bar_1: c_int = 2;
59 \\pub export fn foo() c_int {252 \\pub export fn foo() c_int {
60 \\ _ = bar_1;
61 \\ return 0;253 \\ return 0;
62 \\}254 \\}
63 });255 });
...@@ -107,7 +299,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -107,7 +299,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
107 \\ int i1;299 \\ int i1;
108 \\} boom_t;300 \\} boom_t;
109 \\#define FOO ((boom_t){1})301 \\#define FOO ((boom_t){1})
110 , &[_][]const u8{ // TODO properly translate this302 , &[_][]const u8{
111 \\pub const struct_Color = extern struct {303 \\pub const struct_Color = extern struct {
112 \\ r: u8,304 \\ r: u8,
113 \\ g: u8,305 \\ g: u8,
...@@ -127,7 +319,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -127,7 +319,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
127 \\};319 \\};
128 \\pub const boom_t = struct_boom_t;320 \\pub const boom_t = struct_boom_t;
129 ,321 ,
130 \\pub const FOO = @import("std").mem.zeroInit(boom_t, .{ 1 });322 \\pub const FOO = @import("std").mem.zeroInit(boom_t, .{1});
131 });323 });
132324
133 cases.add("complex switch",325 cases.add("complex switch",
...@@ -142,14 +334,17 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -142,14 +334,17 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
142 \\ }334 \\ }
143 \\}335 \\}
144 , &[_][]const u8{ // TODO properly translate this336 , &[_][]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;
146 });341 });
147342
148 cases.add("correct semicolon after infixop",343 cases.add("correct semicolon after infixop",
149 \\#define __ferror_unlocked_body(_fp) (((_fp)->_flags & _IO_ERR_SEEN) != 0)344 \\#define __ferror_unlocked_body(_fp) (((_fp)->_flags & _IO_ERR_SEEN) != 0)
150 , &[_][]const u8{345 , &[_][]const u8{
151 \\pub fn __ferror_unlocked_body(_fp: anytype) callconv(.Inline) @TypeOf(((_fp.*._flags) & _IO_ERR_SEEN) != 0) {346 \\pub fn __ferror_unlocked_body(_fp: anytype) callconv(.Inline) @TypeOf((_fp.*._flags & _IO_ERR_SEEN) != 0) {
152 \\ return ((_fp.*._flags) & _IO_ERR_SEEN) != 0;347 \\ return (_fp.*._flags & _IO_ERR_SEEN) != 0;
153 \\}348 \\}
154 });349 });
155350
...@@ -193,9 +388,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -193,9 +388,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
193 \\pub export fn foo() void {388 \\pub export fn foo() void {
194 \\ while (false) while (false) {};389 \\ while (false) while (false) {};
195 \\ while (true) while (false) {};390 \\ while (true) while (false) {};
196 \\ while (true) while (true) {391 \\ while (true) {}
197 \\ if (!false) break;
198 \\ };
199 \\}392 \\}
200 });393 });
201394
...@@ -245,15 +438,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -245,15 +438,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
245 \\ volatile _Atomic int abufused[12];438 \\ volatile _Atomic int abufused[12];
246 \\};439 \\};
247 , &[_][]const u8{440 , &[_][]const u8{
248 \\pub const struct_arcan_shmif_page = //441 \\source.h:4:8: warning: struct demoted to opaque type - unable to translate type of field abufused
249 ,442 \\pub const struct_arcan_shmif_page = opaque {};
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`
255 \\pub const struct_arcan_shmif_cont = extern struct {443 \\pub const struct_arcan_shmif_cont = extern struct {
256 \\ addr: [*c]struct_arcan_shmif_page,444 \\ addr: ?*struct_arcan_shmif_page,
257 \\};445 \\};
258 });446 });
259447
...@@ -293,22 +481,22 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -293,22 +481,22 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
293 , &[_][]const u8{481 , &[_][]const u8{
294 \\pub const uuid_t = [16]u8;482 \\pub const uuid_t = [16]u8;
295 \\pub const UUID_NULL: uuid_t = [16]u8{483 \\pub const UUID_NULL: uuid_t = [16]u8{
296 \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))),484 \\ 0,
297 \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))),485 \\ 0,
298 \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))),486 \\ 0,
299 \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))),487 \\ 0,
300 \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))),488 \\ 0,
301 \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))),489 \\ 0,
302 \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))),490 \\ 0,
303 \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))),491 \\ 0,
304 \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))),492 \\ 0,
305 \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))),493 \\ 0,
306 \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))),494 \\ 0,
307 \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))),495 \\ 0,
308 \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))),496 \\ 0,
309 \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))),497 \\ 0,
310 \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))),498 \\ 0,
311 \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))),499 \\ 0,
312 \\};500 \\};
313 });501 });
314502
...@@ -362,10 +550,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -362,10 +550,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
362 \\};550 \\};
363 \\pub export var ub: union_unnamed_1 = union_unnamed_1{551 \\pub export var ub: union_unnamed_1 = union_unnamed_1{
364 \\ .c = [4]u8{552 \\ .c = [4]u8{
365 \\ @bitCast(u8, @truncate(i8, @as(c_int, 'a'))),553 \\ 'a',
366 \\ @bitCast(u8, @truncate(i8, @as(c_int, 'b'))),554 \\ 'b',
367 \\ @bitCast(u8, @truncate(i8, @as(c_int, 'b'))),555 \\ 'b',
368 \\ @bitCast(u8, @truncate(i8, @as(c_int, 'a'))),556 \\ 'a',
369 \\ },557 \\ },
370 \\};558 \\};
371 });559 });
...@@ -492,7 +680,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -492,7 +680,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
492 , &[_][]const u8{680 , &[_][]const u8{
493 \\pub export fn foo() void {681 \\pub export fn foo() void {
494 \\ var a: c_int = undefined;682 \\ var a: c_int = undefined;
495 \\ var b: u8 = @bitCast(u8, @truncate(i8, @as(c_int, 123)));683 \\ var b: u8 = 123;
496 \\ const c: c_int = undefined;684 \\ const c: c_int = undefined;
497 \\ const d: c_uint = @bitCast(c_uint, @as(c_int, 440));685 \\ const d: c_uint = @bitCast(c_uint, @as(c_int, 440));
498 \\ var e: c_int = 10;686 \\ var e: c_int = 10;
...@@ -514,8 +702,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -514,8 +702,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
514 \\ var a: c_int = undefined;702 \\ var a: c_int = undefined;
515 \\ _ = @as(c_int, 1);703 \\ _ = @as(c_int, 1);
516 \\ _ = "hey";704 \\ _ = "hey";
517 \\ _ = (@as(c_int, 1) + @as(c_int, 1));705 \\ _ = @as(c_int, 1) + @as(c_int, 1);
518 \\ _ = (@as(c_int, 1) - @as(c_int, 1));706 \\ _ = @as(c_int, 1) - @as(c_int, 1);
519 \\ a = 1;707 \\ a = 1;
520 \\}708 \\}
521 });709 });
...@@ -559,9 +747,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -559,9 +747,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
559 \\ '2',747 \\ '2',
560 \\ 0,748 \\ 0,
561 \\};749 \\};
562 \\pub export fn foo() void {750 \\pub export fn foo() void {}
563 \\ _ = v2;
564 \\}
565 });751 });
566752
567 cases.add("simple function definition",753 cases.add("simple function definition",
...@@ -634,9 +820,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -634,9 +820,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
634 \\ var a: c_int = undefined;820 \\ var a: c_int = undefined;
635 \\ var b: c_int = undefined;821 \\ var b: c_int = undefined;
636 \\ var c: c_int = undefined;822 \\ var c: c_int = undefined;
637 \\ c = (a + b);823 \\ c = a + b;
638 \\ c = (a - b);824 \\ c = a - b;
639 \\ c = (a * b);825 \\ c = a * b;
640 \\ c = @divTrunc(a, b);826 \\ c = @divTrunc(a, b);
641 \\ c = @rem(a, b);827 \\ c = @rem(a, b);
642 \\ return 0;828 \\ return 0;
...@@ -645,11 +831,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -645,11 +831,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
645 \\ var a: c_uint = undefined;831 \\ var a: c_uint = undefined;
646 \\ var b: c_uint = undefined;832 \\ var b: c_uint = undefined;
647 \\ var c: c_uint = undefined;833 \\ var c: c_uint = undefined;
648 \\ c = (a +% b);834 \\ c = a +% b;
649 \\ c = (a -% b);835 \\ c = a -% b;
650 \\ c = (a *% b);836 \\ c = a *% b;
651 \\ c = (a / b);837 \\ c = a / b;
652 \\ c = (a % b);838 \\ c = a % b;
653 \\ return 0;839 \\ return 0;
654 \\}840 \\}
655 });841 });
...@@ -914,13 +1100,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -914,13 +1100,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
914 \\ ;;;;;1100 \\ ;;;;;
915 \\}1101 \\}
916 , &[_][]const u8{1102 , &[_][]const u8{
917 \\pub export fn foo() void {1103 \\pub export fn foo() void {}
918 \\ {}
919 \\ {}
920 \\ {}
921 \\ {}
922 \\ {}
923 \\}
924 });1104 });
9251105
926 if (std.Target.current.os.tag != .windows) {1106 if (std.Target.current.os.tag != .windows) {
...@@ -1335,11 +1515,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1335,11 +1515,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1335 \\extern enum enum_ty my_enum;1515 \\extern enum enum_ty my_enum;
1336 \\enum enum_ty { FOO };1516 \\enum enum_ty { FOO };
1337 , &[_][]const u8{1517 , &[_][]const u8{
1338 \\pub const FOO = @enumToInt(enum_enum_ty.FOO);
1339 \\pub const enum_enum_ty = extern enum(c_int) {1518 \\pub const enum_enum_ty = extern enum(c_int) {
1340 \\ FOO,1519 \\ FOO,
1341 \\ _,1520 \\ _,
1342 \\};1521 \\};
1522 \\pub const FOO = @enumToInt(enum_enum_ty.FOO);
1343 \\pub extern var my_enum: enum_enum_ty;1523 \\pub extern var my_enum: enum_enum_ty;
1344 });1524 });
13451525
...@@ -1448,7 +1628,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1448,7 +1628,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1448 , &[_][]const u8{1628 , &[_][]const u8{
1449 \\pub fn foo() callconv(.C) void {1629 \\pub fn foo() callconv(.C) void {
1450 \\ var arr: [10]u8 = [1]u8{1630 \\ var arr: [10]u8 = [1]u8{
1451 \\ @bitCast(u8, @truncate(i8, @as(c_int, 1))),1631 \\ 1,
1452 \\ } ++ [1]u8{0} ** 9;1632 \\ } ++ [1]u8{0} ** 9;
1453 \\ var arr1: [10][*c]u8 = [1][*c]u8{1633 \\ var arr1: [10][*c]u8 = [1][*c]u8{
1454 \\ null,1634 \\ null,
...@@ -1481,48 +1661,48 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1481,48 +1661,48 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1481 \\ p,1661 \\ p,
1482 \\};1662 \\};
1483 , &[_][]const u8{1663 , &[_][]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);
1487 \\const enum_unnamed_1 = extern enum(c_int) {1664 \\const enum_unnamed_1 = extern enum(c_int) {
1488 \\ a,1665 \\ a,
1489 \\ b,1666 \\ b,
1490 \\ c,1667 \\ c,
1491 \\ _,1668 \\ _,
1492 \\};1669 \\};
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);
1493 \\pub const d = enum_unnamed_1;1673 \\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);
1497 \\const enum_unnamed_2 = extern enum(c_int) {1674 \\const enum_unnamed_2 = extern enum(c_int) {
1498 \\ e = 0,1675 \\ e = 0,
1499 \\ f = 4,1676 \\ f = 4,
1500 \\ g = 5,1677 \\ g = 5,
1501 \\ _,1678 \\ _,
1502 \\};1679 \\};
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);
1503 \\pub export var h: enum_unnamed_2 = @intToEnum(enum_unnamed_2, e);1683 \\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);
1507 \\const enum_unnamed_3 = extern enum(c_int) {1684 \\const enum_unnamed_3 = extern enum(c_int) {
1508 \\ i,1685 \\ i,
1509 \\ j,1686 \\ j,
1510 \\ k,1687 \\ k,
1511 \\ _,1688 \\ _,
1512 \\};1689 \\};
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);
1513 \\pub const struct_Baz = extern struct {1693 \\pub const struct_Baz = extern struct {
1514 \\ l: enum_unnamed_3,1694 \\ l: enum_unnamed_3,
1515 \\ m: d,1695 \\ m: d,
1516 \\};1696 \\};
1517 \\pub const n = @enumToInt(enum_i.n);
1518 \\pub const o = @enumToInt(enum_i.o);
1519 \\pub const p = @enumToInt(enum_i.p);
1520 \\pub const enum_i = extern enum(c_int) {1697 \\pub const enum_i = extern enum(c_int) {
1521 \\ n,1698 \\ n,
1522 \\ o,1699 \\ o,
1523 \\ p,1700 \\ p,
1524 \\ _,1701 \\ _,
1525 \\};1702 \\};
1703 \\pub const n = @enumToInt(enum_i.n);
1704 \\pub const o = @enumToInt(enum_i.o);
1705 \\pub const p = @enumToInt(enum_i.p);
1526 ,1706 ,
1527 \\pub const Baz = struct_Baz;1707 \\pub const Baz = struct_Baz;
1528 });1708 });
...@@ -1639,7 +1819,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1639,7 +1819,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1639 cases.add("macro pointer cast",1819 cases.add("macro pointer cast",
1640 \\#define NRF_GPIO ((NRF_GPIO_Type *) NRF_GPIO_BASE)1820 \\#define NRF_GPIO ((NRF_GPIO_Type *) NRF_GPIO_BASE)
1641 , &[_][]const u8{1821 , &[_][]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);
1643 });1823 });
16441824
1645 cases.add("basic macro function",1825 cases.add("basic macro function",
...@@ -1701,13 +1881,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1701,13 +1881,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1701 \\ unsigned d = 440;1881 \\ unsigned d = 440;
1702 \\}1882 \\}
1703 , &[_][]const u8{1883 , &[_][]const u8{
1704 \\pub var a: c_long = @bitCast(c_long, @as(c_long, @as(c_int, 2)));1884 \\pub var a: c_long = 2;
1705 \\pub var b: c_long = @bitCast(c_long, @as(c_long, @as(c_int, 2)));1885 \\pub var b: c_long = 2;
1706 \\pub var c: c_int = 4;1886 \\pub var c: c_int = 4;
1707 \\pub export fn foo(arg_c_1: u8) void {1887 \\pub export fn foo(arg_c_1: u8) void {
1708 \\ var c_1 = arg_c_1;1888 \\ var c_1 = arg_c_1;
1709 \\ var a_2: c_int = undefined;1889 \\ 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;
1711 \\ b_3 = @bitCast(u8, @truncate(i8, a_2));1891 \\ b_3 = @bitCast(u8, @truncate(i8, a_2));
1712 \\ {1892 \\ {
1713 \\ var d: c_int = 5;1893 \\ var d: c_int = 5;
...@@ -1723,17 +1903,17 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1723,17 +1903,17 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1723 \\}1903 \\}
1724 , &[_][]const u8{1904 , &[_][]const u8{
1725 \\pub export fn foo() c_int {1905 \\pub export fn foo() c_int {
1726 \\ _ = (blk: {1906 \\ _ = blk: {
1727 \\ _ = @as(c_int, 2);1907 \\ _ = @as(c_int, 2);
1728 \\ break :blk @as(c_int, 4);1908 \\ break :blk @as(c_int, 4);
1729 \\ });1909 \\ };
1730 \\ return (blk: {1910 \\ return blk: {
1731 \\ _ = (blk_1: {1911 \\ _ = blk_1: {
1732 \\ _ = @as(c_int, 2);1912 \\ _ = @as(c_int, 2);
1733 \\ break :blk_1 @as(c_int, 4);1913 \\ break :blk_1 @as(c_int, 4);
1734 \\ });1914 \\ };
1735 \\ break :blk @as(c_int, 6);1915 \\ break :blk @as(c_int, 6);
1736 \\ });1916 \\ };
1737 \\}1917 \\}
1738 });1918 });
17391919
...@@ -1780,20 +1960,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1780,20 +1960,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1780 \\ while (true) {1960 \\ while (true) {
1781 \\ var a_1: c_int = 4;1961 \\ var a_1: c_int = 4;
1782 \\ a_1 = 9;1962 \\ a_1 = 9;
1783 \\ return (blk: {1963 \\ return blk: {
1784 \\ _ = @as(c_int, 6);1964 \\ _ = @as(c_int, 6);
1785 \\ break :blk a_1;1965 \\ break :blk a_1;
1786 \\ });1966 \\ };
1787 \\ }1967 \\ }
1788 \\ while (true) {1968 \\ while (true) {
1789 \\ var a_1: c_int = 2;1969 \\ var a_1: c_int = 2;
1790 \\ a_1 = 12;1970 \\ a_1 = 12;
1791 \\ if (!true) break;
1792 \\ }
1793 \\ while (true) {
1794 \\ a = 7;
1795 \\ if (!true) break;
1796 \\ }1971 \\ }
1972 \\ while (true) a = 7;
1797 \\ return 0;1973 \\ return 0;
1798 \\}1974 \\}
1799 });1975 });
...@@ -1813,16 +1989,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1813,16 +1989,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1813 \\ var b: c_int = 4;1989 \\ var b: c_int = 4;
1814 \\ while ((i + @as(c_int, 2)) != 0) : (i = 2) {1990 \\ while ((i + @as(c_int, 2)) != 0) : (i = 2) {
1815 \\ var a: c_int = 2;1991 \\ var a: c_int = 2;
1816 \\ _ = (blk: {1992 \\ _ = blk: {
1817 \\ _ = (blk_1: {1993 \\ _ = blk_1: {
1818 \\ a = 6;1994 \\ a = 6;
1819 \\ break :blk_1 @as(c_int, 5);1995 \\ break :blk_1 @as(c_int, 5);
1820 \\ });1996 \\ };
1821 \\ break :blk @as(c_int, 7);1997 \\ break :blk @as(c_int, 7);
1822 \\ });1998 \\ };
1823 \\ }1999 \\ }
1824 \\ }2000 \\ }
1825 \\ var i: u8 = @bitCast(u8, @truncate(i8, @as(c_int, 2)));2001 \\ var i: u8 = 2;
1826 \\}2002 \\}
1827 });2003 });
18282004
...@@ -1830,7 +2006,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1830,7 +2006,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1830 \\unsigned anyerror = 2;2006 \\unsigned anyerror = 2;
1831 \\#define noreturn _Noreturn2007 \\#define noreturn _Noreturn
1832 , &[_][]const u8{2008 , &[_][]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;
1834 ,2010 ,
1835 \\pub const noreturn_2 = @compileError("unable to translate C expr: unexpected token .Keyword_noreturn");2011 \\pub const noreturn_2 = @compileError("unable to translate C expr: unexpected token .Keyword_noreturn");
1836 });2012 });
...@@ -1844,7 +2020,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1844,7 +2020,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1844 \\pub export var a: f32 = @floatCast(f32, 3.1415);2020 \\pub export var a: f32 = @floatCast(f32, 3.1415);
1845 \\pub export var b: f64 = 3.1415;2021 \\pub export var b: f64 = 3.1415;
1846 \\pub export var c: c_int = @floatToInt(c_int, 3.1415);2022 \\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;
1848 });2024 });
18492025
1850 cases.add("conditional operator",2026 cases.add("conditional operator",
...@@ -1854,7 +2030,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1854,7 +2030,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1854 \\}2030 \\}
1855 , &[_][]const u8{2031 , &[_][]const u8{
1856 \\pub export fn bar() c_int {2032 \\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);
1858 \\ return if (true) @as(c_int, 5) else if (true) @as(c_int, 4) else @as(c_int, 6);2034 \\ return if (true) @as(c_int, 5) else if (true) @as(c_int, 4) else @as(c_int, 6);
1859 \\}2035 \\}
1860 });2036 });
...@@ -1870,34 +2046,64 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1870,34 +2046,64 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1870 \\ default:2046 \\ default:
1871 \\ res = 3 * i;2047 \\ res = 3 * i;
1872 \\ break;2048 \\ break;
2049 \\ break;
2050 \\ case 7: {
2051 \\ res = 7;
2052 \\ break;
2053 \\ }
1873 \\ case 4:2054 \\ case 4:
2055 \\ case 5:
2056 \\ res = 69;
2057 \\ {
1874 \\ res = 5;2058 \\ res = 5;
2059 \\ return;
2060 \\ }
2061 \\ case 6:
2062 \\ switch (res) {
2063 \\ case 9: break;
2064 \\ }
2065 \\ res = 1;
2066 \\ return;
1875 \\ }2067 \\ }
1876 \\}2068 \\}
1877 , &[_][]const u8{2069 , &[_][]const u8{
1878 \\pub export fn switch_fn(arg_i: c_int) void {2070 \\pub export fn switch_fn(arg_i: c_int) void {
1879 \\ var i = arg_i;2071 \\ var i = arg_i;
1880 \\ var res: c_int = 0;2072 \\ var res: c_int = 0;
1881 \\ @"switch": {2073 \\ switch (i) {
1882 \\ case_2: {2074 \\ @as(c_int, 0) => {
1883 \\ default: {2075 \\ res = 1;
1884 \\ case_1: {2076 \\ res = 2;
1885 \\ case: {2077 \\ res = @as(c_int, 3) * i;
1886 \\ switch (i) {2078 \\ },
1887 \\ @as(c_int, 0) => break :case,2079 \\ @as(c_int, 1)...@as(c_int, 3) => {
1888 \\ @as(c_int, 1)...@as(c_int, 3) => break :case_1,2080 \\ res = 2;
1889 \\ else => break :default,2081 \\ res = @as(c_int, 3) * i;
1890 \\ @as(c_int, 4) => break :case_2,2082 \\ },
1891 \\ }2083 \\ else => {
1892 \\ }2084 \\ res = @as(c_int, 3) * i;
1893 \\ res = 1;2085 \\ },
1894 \\ }2086 \\ @as(c_int, 7) => {
1895 \\ res = 2;2087 \\ {
2088 \\ res = 7;
2089 \\ break;
1896 \\ }2090 \\ }
1897 \\ res = (@as(c_int, 3) * i);2091 \\ },
1898 \\ break :@"switch";2092 \\ @as(c_int, 4), @as(c_int, 5) => {
1899 \\ }2093 \\ res = 69;
1900 \\ res = 5;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 \\ },
1901 \\ }2107 \\ }
1902 \\}2108 \\}
1903 });2109 });
...@@ -1973,13 +2179,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1973,13 +2179,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1973 \\ Two,2179 \\ Two,
1974 \\};2180 \\};
1975 , &[_][]const u8{2181 , &[_][]const u8{
1976 \\pub const One = @enumToInt(enum_unnamed_1.One);
1977 \\pub const Two = @enumToInt(enum_unnamed_1.Two);
1978 \\const enum_unnamed_1 = extern enum(c_int) {2182 \\const enum_unnamed_1 = extern enum(c_int) {
1979 \\ One,2183 \\ One,
1980 \\ Two,2184 \\ Two,
1981 \\ _,2185 \\ _,
1982 \\};2186 \\};
2187 \\pub const One = @enumToInt(enum_unnamed_1.One);
2188 \\pub const Two = @enumToInt(enum_unnamed_1.Two);
1983 });2189 });
19842190
1985 cases.add("c style cast",2191 cases.add("c style cast",
...@@ -1993,7 +2199,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1993,7 +2199,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1993 \\}2199 \\}
1994 });2200 });
19952201
1996 // TODO translate-c should in theory be able to figure out to drop all these casts
1997 cases.add("escape sequences",2202 cases.add("escape sequences",
1998 \\const char *escapes() {2203 \\const char *escapes() {
1999 \\char a = '\'',2204 \\char a = '\'',
...@@ -2012,17 +2217,17 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2012,17 +2217,17 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2012 \\2217 \\
2013 , &[_][]const u8{2218 , &[_][]const u8{
2014 \\pub export fn escapes() [*c]const u8 {2219 \\pub export fn escapes() [*c]const u8 {
2015 \\ var a: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\'')));2220 \\ var a: u8 = '\'';
2016 \\ var b: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\\')));2221 \\ var b: u8 = '\\';
2017 \\ var c: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\x07')));2222 \\ var c: u8 = '\x07';
2018 \\ var d: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\x08')));2223 \\ var d: u8 = '\x08';
2019 \\ var e: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\x0c')));2224 \\ var e: u8 = '\x0c';
2020 \\ var f: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\n')));2225 \\ var f: u8 = '\n';
2021 \\ var g: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\r')));2226 \\ var g: u8 = '\r';
2022 \\ var h: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\t')));2227 \\ var h: u8 = '\t';
2023 \\ var i: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\x0b')));2228 \\ var i: u8 = '\x0b';
2024 \\ var j: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\x00')));2229 \\ var j: u8 = '\x00';
2025 \\ var k: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\"')));2230 \\ var k: u8 = '\"';
2026 \\ return "\'\\\x07\x08\x0c\n\r\t\x0b\x00\"";2231 \\ return "\'\\\x07\x08\x0c\n\r\t\x0b\x00\"";
2027 \\}2232 \\}
2028 });2233 });
...@@ -2043,12 +2248,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2043,12 +2248,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2043 \\pub export fn foo() void {2248 \\pub export fn foo() void {
2044 \\ var a: c_int = 2;2249 \\ var a: c_int = 2;
2045 \\ while (true) {2250 \\ while (true) {
2046 \\ a = (a - @as(c_int, 1));2251 \\ a = a - @as(c_int, 1);
2047 \\ if (!(a != 0)) break;2252 \\ if (!(a != 0)) break;
2048 \\ }2253 \\ }
2049 \\ var b: c_int = 2;2254 \\ var b: c_int = 2;
2050 \\ while (true) {2255 \\ while (true) {
2051 \\ b = (b - @as(c_int, 1));2256 \\ b = b - @as(c_int, 1);
2052 \\ if (!(b != 0)) break;2257 \\ if (!(b != 0)) break;
2053 \\ }2258 \\ }
2054 \\}2259 \\}
...@@ -2084,25 +2289,28 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2084,25 +2289,28 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2084 \\ C,2289 \\ C,
2085 \\ _,2290 \\ _,
2086 \\};2291 \\};
2292 \\pub const FooA = @enumToInt(enum_Foo.A);
2293 \\pub const FooB = @enumToInt(enum_Foo.B);
2294 \\pub const FooC = @enumToInt(enum_Foo.C);
2087 \\pub const SomeTypedef = c_int;2295 \\pub const SomeTypedef = c_int;
2088 \\pub export fn and_or_non_bool(arg_a: c_int, arg_b: f32, arg_c: ?*c_void) c_int {2296 \\pub export fn and_or_non_bool(arg_a: c_int, arg_b: f32, arg_c: ?*c_void) c_int {
2089 \\ var a = arg_a;2297 \\ var a = arg_a;
2090 \\ var b = arg_b;2298 \\ var b = arg_b;
2091 \\ var c = arg_c;2299 \\ var c = arg_c;
2092 \\ var d: enum_Foo = @intToEnum(enum_Foo, FooA);2300 \\ var d: enum_Foo = @intToEnum(enum_Foo, FooA);
2093 \\ var e: c_int = @boolToInt(((a != 0) and (b != 0)));2301 \\ var e: c_int = @boolToInt((a != 0) and (b != 0));
2094 \\ var f: c_int = @boolToInt(((b != 0) and (c != null)));2302 \\ var f: c_int = @boolToInt((b != 0) and (c != null));
2095 \\ var g: c_int = @boolToInt(((a != 0) and (c != null)));2303 \\ var g: c_int = @boolToInt((a != 0) and (c != null));
2096 \\ var h: c_int = @boolToInt(((a != 0) or (b != 0)));2304 \\ var h: c_int = @boolToInt((a != 0) or (b != 0));
2097 \\ var i: c_int = @boolToInt(((b != 0) or (c != null)));2305 \\ var i: c_int = @boolToInt((b != 0) or (c != null));
2098 \\ var j: c_int = @boolToInt(((a != 0) or (c != null)));2306 \\ 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)));2307 \\ 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)));2308 \\ 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)));2309 \\ var m: c_int = @boolToInt((c != null) or (@bitCast(c_uint, @enumToInt(d)) != 0));
2102 \\ var td: SomeTypedef = 44;2310 \\ var td: SomeTypedef = 44;
2103 \\ var o: c_int = @boolToInt(((td != 0) or (b != 0)));2311 \\ var o: c_int = @boolToInt((td != 0) or (b != 0));
2104 \\ var p: c_int = @boolToInt(((c != null) and (td != 0)));2312 \\ var p: c_int = @boolToInt((c != null) and (td != 0));
2105 \\ return ((((((((((e + f) + g) + h) + i) + j) + k) + l) + m) + o) + p);2313 \\ return (((((((((e + f) + g) + h) + i) + j) + k) + l) + m) + o) + p;
2106 \\}2314 \\}
2107 ,2315 ,
2108 \\pub const Foo = enum_Foo;2316 \\pub const Foo = enum_Foo;
...@@ -2129,6 +2337,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2129,6 +2337,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2129 \\ B,2337 \\ B,
2130 \\ _,2338 \\ _,
2131 \\};2339 \\};
2340 \\pub const BarA = @enumToInt(enum_Bar.A);
2341 \\pub const BarB = @enumToInt(enum_Bar.B);
2132 \\pub extern fn func(a: [*c]struct_Foo, b: [*c][*c]enum_Bar) void;2342 \\pub extern fn func(a: [*c]struct_Foo, b: [*c][*c]enum_Bar) void;
2133 ,2343 ,
2134 \\pub const Foo = struct_Foo;2344 \\pub const Foo = struct_Foo;
...@@ -2143,7 +2353,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2143,7 +2353,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2143 \\pub export fn max(arg_a: c_int, arg_b: c_int) c_int {2353 \\pub export fn max(arg_a: c_int, arg_b: c_int) c_int {
2144 \\ var a = arg_a;2354 \\ var a = arg_a;
2145 \\ var b = arg_b;2355 \\ var b = arg_b;
2146 \\ return ((a & b) ^ (a | b));2356 \\ return (a & b) ^ (a | b);
2147 \\}2357 \\}
2148 });2358 });
21492359
...@@ -2162,13 +2372,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2162,13 +2372,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2162 \\pub export fn test_comparisons(arg_a: c_int, arg_b: c_int) c_int {2372 \\pub export fn test_comparisons(arg_a: c_int, arg_b: c_int) c_int {
2163 \\ var a = arg_a;2373 \\ var a = arg_a;
2164 \\ var b = arg_b;2374 \\ var b = arg_b;
2165 \\ var c: c_int = @boolToInt((a < b));2375 \\ var c: c_int = @boolToInt(a < b);
2166 \\ var d: c_int = @boolToInt((a > b));2376 \\ var d: c_int = @boolToInt(a > b);
2167 \\ var e: c_int = @boolToInt((a <= b));2377 \\ var e: c_int = @boolToInt(a <= b);
2168 \\ var f: c_int = @boolToInt((a >= b));2378 \\ var f: c_int = @boolToInt(a >= b);
2169 \\ var g: c_int = @boolToInt((c < d));2379 \\ var g: c_int = @boolToInt(c < d);
2170 \\ var h: c_int = @boolToInt((e < f));2380 \\ var h: c_int = @boolToInt(e < f);
2171 \\ var i: c_int = @boolToInt((g < h));2381 \\ var i: c_int = @boolToInt(g < h);
2172 \\ return i;2382 \\ return i;
2173 \\}2383 \\}
2174 });2384 });
...@@ -2215,11 +2425,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2215,11 +2425,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2215 \\}2425 \\}
2216 , &[_][]const u8{2426 , &[_][]const u8{
2217 \\pub export fn foo() c_int {2427 \\pub export fn foo() c_int {
2218 \\ return (blk: {2428 \\ return blk: {
2219 \\ var a: c_int = 1;2429 \\ var a: c_int = 1;
2220 \\ _ = a;2430 \\ _ = a;
2221 \\ break :blk a;2431 \\ break :blk a;
2222 \\ });2432 \\ };
2223 \\}2433 \\}
2224 });2434 });
22252435
...@@ -2289,8 +2499,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2289,8 +2499,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2289 , &[_][]const u8{2499 , &[_][]const u8{
2290 \\pub export fn foo() void {2500 \\pub export fn foo() void {
2291 \\ var a: [10]c_longlong = undefined;2501 \\ var a: [10]c_longlong = undefined;
2292 \\ var i: c_longlong = @bitCast(c_longlong, @as(c_longlong, @as(c_int, 0)));2502 \\ var i: c_longlong = 0;
2293 \\ a[@intCast(usize, i)] = @bitCast(c_longlong, @as(c_longlong, @as(c_int, 0)));2503 \\ a[@intCast(usize, i)] = 0;
2294 \\}2504 \\}
2295 });2505 });
22962506
...@@ -2302,8 +2512,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2302,8 +2512,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2302 , &[_][]const u8{2512 , &[_][]const u8{
2303 \\pub export fn foo() void {2513 \\pub export fn foo() void {
2304 \\ var a: [10]c_uint = undefined;2514 \\ var a: [10]c_uint = undefined;
2305 \\ var i: c_uint = @bitCast(c_uint, @as(c_int, 0));2515 \\ var i: c_uint = 0;
2306 \\ a[i] = @bitCast(c_uint, @as(c_int, 0));2516 \\ a[i] = 0;
2307 \\}2517 \\}
2308 });2518 });
23092519
...@@ -2395,6 +2605,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2395,6 +2605,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2395 \\ C,2605 \\ C,
2396 \\ _,2606 \\ _,
2397 \\};2607 \\};
2608 \\pub const A = @enumToInt(enum_SomeEnum.A);
2609 \\pub const B = @enumToInt(enum_SomeEnum.B);
2610 \\pub const C = @enumToInt(enum_SomeEnum.C);
2398 \\pub export fn if_none_bool(arg_a: c_int, arg_b: f32, arg_c: ?*c_void, arg_d: enum_SomeEnum) c_int {2611 \\pub export fn if_none_bool(arg_a: c_int, arg_b: f32, arg_c: ?*c_void, arg_d: enum_SomeEnum) c_int {
2399 \\ var a = arg_a;2612 \\ var a = arg_a;
2400 \\ var b = arg_b;2613 \\ var b = arg_b;
...@@ -2484,10 +2697,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2484,10 +2697,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2484 \\ var f: ?fn () callconv(.C) void = foo;2697 \\ var f: ?fn () callconv(.C) void = foo;
2485 \\ var b: ?fn () callconv(.C) c_int = baz;2698 \\ var b: ?fn () callconv(.C) c_int = baz;
2486 \\ f.?();2699 \\ f.?();
2487 \\ (f).?();2700 \\ f.?();
2488 \\ foo();2701 \\ foo();
2489 \\ _ = b.?();2702 \\ _ = b.?();
2490 \\ _ = (b).?();2703 \\ _ = b.?();
2491 \\ _ = baz();2704 \\ _ = baz();
2492 \\}2705 \\}
2493 });2706 });
...@@ -2508,31 +2721,31 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2508,31 +2721,31 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2508 , &[_][]const u8{2721 , &[_][]const u8{
2509 \\pub export fn foo() void {2722 \\pub export fn foo() void {
2510 \\ var i: c_int = 0;2723 \\ var i: c_int = 0;
2511 \\ var u: c_uint = @bitCast(c_uint, @as(c_int, 0));2724 \\ var u: c_uint = 0;
2512 \\ i += 1;2725 \\ i += 1;
2513 \\ i -= 1;2726 \\ i -= 1;
2514 \\ u +%= 1;2727 \\ u +%= 1;
2515 \\ u -%= 1;2728 \\ u -%= 1;
2516 \\ i = (blk: {2729 \\ i = blk: {
2517 \\ const ref = &i;2730 \\ const ref = &i;
2518 \\ ref.* += 1;2731 \\ ref.* += 1;
2519 \\ break :blk ref.*;2732 \\ break :blk ref.*;
2520 \\ });2733 \\ };
2521 \\ i = (blk: {2734 \\ i = blk: {
2522 \\ const ref = &i;2735 \\ const ref = &i;
2523 \\ ref.* -= 1;2736 \\ ref.* -= 1;
2524 \\ break :blk ref.*;2737 \\ break :blk ref.*;
2525 \\ });2738 \\ };
2526 \\ u = (blk: {2739 \\ u = blk: {
2527 \\ const ref = &u;2740 \\ const ref = &u;
2528 \\ ref.* +%= 1;2741 \\ ref.* +%= 1;
2529 \\ break :blk ref.*;2742 \\ break :blk ref.*;
2530 \\ });2743 \\ };
2531 \\ u = (blk: {2744 \\ u = blk: {
2532 \\ const ref = &u;2745 \\ const ref = &u;
2533 \\ ref.* -%= 1;2746 \\ ref.* -%= 1;
2534 \\ break :blk ref.*;2747 \\ break :blk ref.*;
2535 \\ });2748 \\ };
2536 \\}2749 \\}
2537 });2750 });
25382751
...@@ -2595,67 +2808,67 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2595,67 +2808,67 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2595 , &[_][]const u8{2808 , &[_][]const u8{
2596 \\pub export fn foo() void {2809 \\pub export fn foo() void {
2597 \\ var a: c_int = 0;2810 \\ var a: c_int = 0;
2598 \\ var b: c_uint = @bitCast(c_uint, @as(c_int, 0));2811 \\ var b: c_uint = 0;
2599 \\ a += (blk: {2812 \\ a += blk: {
2600 \\ const ref = &a;2813 \\ const ref = &a;
2601 \\ ref.* = ref.* + @as(c_int, 1);2814 \\ ref.* += @as(c_int, 1);
2602 \\ break :blk ref.*;2815 \\ break :blk ref.*;
2603 \\ });2816 \\ };
2604 \\ a -= (blk: {2817 \\ a -= blk: {
2605 \\ const ref = &a;2818 \\ const ref = &a;
2606 \\ ref.* = ref.* - @as(c_int, 1);2819 \\ ref.* -= @as(c_int, 1);
2607 \\ break :blk ref.*;2820 \\ break :blk ref.*;
2608 \\ });2821 \\ };
2609 \\ a *= (blk: {2822 \\ a *= blk: {
2610 \\ const ref = &a;2823 \\ const ref = &a;
2611 \\ ref.* = ref.* * @as(c_int, 1);2824 \\ ref.* *= @as(c_int, 1);
2612 \\ break :blk ref.*;2825 \\ break :blk ref.*;
2613 \\ });2826 \\ };
2614 \\ a &= (blk: {2827 \\ a &= blk: {
2615 \\ const ref = &a;2828 \\ const ref = &a;
2616 \\ ref.* = ref.* & @as(c_int, 1);2829 \\ ref.* &= @as(c_int, 1);
2617 \\ break :blk ref.*;2830 \\ break :blk ref.*;
2618 \\ });2831 \\ };
2619 \\ a |= (blk: {2832 \\ a |= blk: {
2620 \\ const ref = &a;2833 \\ const ref = &a;
2621 \\ ref.* = ref.* | @as(c_int, 1);2834 \\ ref.* |= @as(c_int, 1);
2622 \\ break :blk ref.*;2835 \\ break :blk ref.*;
2623 \\ });2836 \\ };
2624 \\ a ^= (blk: {2837 \\ a ^= blk: {
2625 \\ const ref = &a;2838 \\ const ref = &a;
2626 \\ ref.* = ref.* ^ @as(c_int, 1);2839 \\ ref.* ^= @as(c_int, 1);
2627 \\ break :blk ref.*;2840 \\ break :blk ref.*;
2628 \\ });2841 \\ };
2629 \\ a >>= @intCast(@import("std").math.Log2Int(c_int), (blk: {2842 \\ a >>= @intCast(@import("std").math.Log2Int(c_int), blk: {
2630 \\ const ref = &a;2843 \\ 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));
2632 \\ break :blk ref.*;2845 \\ break :blk ref.*;
2633 \\ }));2846 \\ });
2634 \\ a <<= @intCast(@import("std").math.Log2Int(c_int), (blk: {2847 \\ a <<= @intCast(@import("std").math.Log2Int(c_int), blk: {
2635 \\ const ref = &a;2848 \\ 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));
2637 \\ break :blk ref.*;2850 \\ break :blk ref.*;
2638 \\ }));2851 \\ });
2639 \\ a = @divTrunc(a, (blk: {2852 \\ a = @divTrunc(a, blk: {
2640 \\ const ref = &a;2853 \\ const ref = &a;
2641 \\ ref.* = @divTrunc(ref.*, @as(c_int, 1));2854 \\ ref.* = @divTrunc(ref.*, @as(c_int, 1));
2642 \\ break :blk ref.*;2855 \\ break :blk ref.*;
2643 \\ }));2856 \\ });
2644 \\ a = @rem(a, (blk: {2857 \\ a = @rem(a, blk: {
2645 \\ const ref = &a;2858 \\ const ref = &a;
2646 \\ ref.* = @rem(ref.*, @as(c_int, 1));2859 \\ ref.* = @rem(ref.*, @as(c_int, 1));
2647 \\ break :blk ref.*;2860 \\ break :blk ref.*;
2648 \\ }));2861 \\ });
2649 \\ b /= (blk: {2862 \\ b /= blk: {
2650 \\ const ref = &b;2863 \\ const ref = &b;
2651 \\ ref.* = ref.* / @bitCast(c_uint, @as(c_int, 1));2864 \\ ref.* /= @bitCast(c_uint, @as(c_int, 1));
2652 \\ break :blk ref.*;2865 \\ break :blk ref.*;
2653 \\ });2866 \\ };
2654 \\ b %= (blk: {2867 \\ b %= blk: {
2655 \\ const ref = &b;2868 \\ const ref = &b;
2656 \\ ref.* = ref.* % @bitCast(c_uint, @as(c_int, 1));2869 \\ ref.* %= @bitCast(c_uint, @as(c_int, 1));
2657 \\ break :blk ref.*;2870 \\ break :blk ref.*;
2658 \\ });2871 \\ };
2659 \\}2872 \\}
2660 });2873 });
26612874
...@@ -2673,47 +2886,47 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2673,47 +2886,47 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2673 \\}2886 \\}
2674 , &[_][]const u8{2887 , &[_][]const u8{
2675 \\pub export fn foo() void {2888 \\pub export fn foo() void {
2676 \\ var a: c_uint = @bitCast(c_uint, @as(c_int, 0));2889 \\ var a: c_uint = 0;
2677 \\ a +%= (blk: {2890 \\ a +%= blk: {
2678 \\ const ref = &a;2891 \\ const ref = &a;
2679 \\ ref.* = ref.* +% @bitCast(c_uint, @as(c_int, 1));2892 \\ ref.* +%= @bitCast(c_uint, @as(c_int, 1));
2680 \\ break :blk ref.*;2893 \\ break :blk ref.*;
2681 \\ });2894 \\ };
2682 \\ a -%= (blk: {2895 \\ a -%= blk: {
2683 \\ const ref = &a;2896 \\ const ref = &a;
2684 \\ ref.* = ref.* -% @bitCast(c_uint, @as(c_int, 1));2897 \\ ref.* -%= @bitCast(c_uint, @as(c_int, 1));
2685 \\ break :blk ref.*;2898 \\ break :blk ref.*;
2686 \\ });2899 \\ };
2687 \\ a *%= (blk: {2900 \\ a *%= blk: {
2688 \\ const ref = &a;2901 \\ const ref = &a;
2689 \\ ref.* = ref.* *% @bitCast(c_uint, @as(c_int, 1));2902 \\ ref.* *%= @bitCast(c_uint, @as(c_int, 1));
2690 \\ break :blk ref.*;2903 \\ break :blk ref.*;
2691 \\ });2904 \\ };
2692 \\ a &= (blk: {2905 \\ a &= blk: {
2693 \\ const ref = &a;2906 \\ const ref = &a;
2694 \\ ref.* = ref.* & @bitCast(c_uint, @as(c_int, 1));2907 \\ ref.* &= @bitCast(c_uint, @as(c_int, 1));
2695 \\ break :blk ref.*;2908 \\ break :blk ref.*;
2696 \\ });2909 \\ };
2697 \\ a |= (blk: {2910 \\ a |= blk: {
2698 \\ const ref = &a;2911 \\ const ref = &a;
2699 \\ ref.* = ref.* | @bitCast(c_uint, @as(c_int, 1));2912 \\ ref.* |= @bitCast(c_uint, @as(c_int, 1));
2700 \\ break :blk ref.*;2913 \\ break :blk ref.*;
2701 \\ });2914 \\ };
2702 \\ a ^= (blk: {2915 \\ a ^= blk: {
2703 \\ const ref = &a;2916 \\ const ref = &a;
2704 \\ ref.* = ref.* ^ @bitCast(c_uint, @as(c_int, 1));2917 \\ ref.* ^= @bitCast(c_uint, @as(c_int, 1));
2705 \\ break :blk ref.*;2918 \\ break :blk ref.*;
2706 \\ });2919 \\ };
2707 \\ a >>= @intCast(@import("std").math.Log2Int(c_uint), (blk: {2920 \\ a >>= @intCast(@import("std").math.Log2Int(c_uint), blk: {
2708 \\ const ref = &a;2921 \\ 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));
2710 \\ break :blk ref.*;2923 \\ break :blk ref.*;
2711 \\ }));2924 \\ });
2712 \\ a <<= @intCast(@import("std").math.Log2Int(c_uint), (blk: {2925 \\ a <<= @intCast(@import("std").math.Log2Int(c_uint), blk: {
2713 \\ const ref = &a;2926 \\ 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));
2715 \\ break :blk ref.*;2928 \\ break :blk ref.*;
2716 \\ }));2929 \\ });
2717 \\}2930 \\}
2718 });2931 });
27192932
...@@ -2733,35 +2946,35 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2733,35 +2946,35 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2733 , &[_][]const u8{2946 , &[_][]const u8{
2734 \\pub export fn foo() void {2947 \\pub export fn foo() void {
2735 \\ var i: c_int = 0;2948 \\ var i: c_int = 0;
2736 \\ var u: c_uint = @bitCast(c_uint, @as(c_int, 0));2949 \\ var u: c_uint = 0;
2737 \\ i += 1;2950 \\ i += 1;
2738 \\ i -= 1;2951 \\ i -= 1;
2739 \\ u +%= 1;2952 \\ u +%= 1;
2740 \\ u -%= 1;2953 \\ u -%= 1;
2741 \\ i = (blk: {2954 \\ i = blk: {
2742 \\ const ref = &i;2955 \\ const ref = &i;
2743 \\ const tmp = ref.*;2956 \\ const tmp = ref.*;
2744 \\ ref.* += 1;2957 \\ ref.* += 1;
2745 \\ break :blk tmp;2958 \\ break :blk tmp;
2746 \\ });2959 \\ };
2747 \\ i = (blk: {2960 \\ i = blk: {
2748 \\ const ref = &i;2961 \\ const ref = &i;
2749 \\ const tmp = ref.*;2962 \\ const tmp = ref.*;
2750 \\ ref.* -= 1;2963 \\ ref.* -= 1;
2751 \\ break :blk tmp;2964 \\ break :blk tmp;
2752 \\ });2965 \\ };
2753 \\ u = (blk: {2966 \\ u = blk: {
2754 \\ const ref = &u;2967 \\ const ref = &u;
2755 \\ const tmp = ref.*;2968 \\ const tmp = ref.*;
2756 \\ ref.* +%= 1;2969 \\ ref.* +%= 1;
2757 \\ break :blk tmp;2970 \\ break :blk tmp;
2758 \\ });2971 \\ };
2759 \\ u = (blk: {2972 \\ u = blk: {
2760 \\ const ref = &u;2973 \\ const ref = &u;
2761 \\ const tmp = ref.*;2974 \\ const tmp = ref.*;
2762 \\ ref.* -%= 1;2975 \\ ref.* -%= 1;
2763 \\ break :blk tmp;2976 \\ break :blk tmp;
2764 \\ });2977 \\ };
2765 \\}2978 \\}
2766 });2979 });
27672980
...@@ -2854,15 +3067,15 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2854,15 +3067,15 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2854 \\ Foo1,3067 \\ Foo1,
2855 \\};3068 \\};
2856 , &[_][]const u8{3069 , &[_][]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");
2860 \\pub const enum_Foo = extern enum(c_int) {3070 \\pub const enum_Foo = extern enum(c_int) {
2861 \\ A = 2,3071 \\ A = 2,
2862 \\ B = 5,3072 \\ B = 5,
2863 \\ @"1" = 6,3073 \\ @"1" = 6,
2864 \\ _,3074 \\ _,
2865 \\};3075 \\};
3076 \\pub const FooA = @enumToInt(enum_Foo.A);
3077 \\pub const FooB = @enumToInt(enum_Foo.B);
3078 \\pub const Foo1 = @enumToInt(enum_Foo.@"1");
2866 ,3079 ,
2867 \\pub const Foo = enum_Foo;3080 \\pub const Foo = enum_Foo;
2868 });3081 });
...@@ -2872,13 +3085,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2872,13 +3085,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2872 \\#define BAR (void*) a3085 \\#define BAR (void*) a
2873 \\#define BAZ (uint32_t)(2)3086 \\#define BAZ (uint32_t)(2)
2874 , &[_][]const u8{3087 , &[_][]const u8{
2875 \\pub fn FOO(bar: anytype) callconv(.Inline) @TypeOf(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))) {
2876 \\ return baz((@import("std").meta.cast(?*c_void, baz)));3089 \\ return baz(@import("std").meta.cast(?*c_void, baz));
2877 \\}3090 \\}
2878 ,3091 ,
2879 \\pub const BAR = (@import("std").meta.cast(?*c_void, a));3092 \\pub const BAR = @import("std").meta.cast(?*c_void, a);
2880 ,3093 ,
2881 \\pub const BAZ = (@import("std").meta.cast(u32, 2));3094 \\pub const BAZ = @import("std").meta.cast(u32, 2);
2882 });3095 });
28833096
2884 cases.add("macro with cast to unsigned short, long, and long long",3097 cases.add("macro with cast to unsigned short, long, and long long",
...@@ -2886,9 +3099,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2886,9 +3099,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2886 \\#define CURLAUTH_BASIC ((unsigned long) 1)3099 \\#define CURLAUTH_BASIC ((unsigned long) 1)
2887 \\#define CURLAUTH_BASIC_BUT_ULONGLONG ((unsigned long long) 1)3100 \\#define CURLAUTH_BASIC_BUT_ULONGLONG ((unsigned long long) 1)
2888 , &[_][]const u8{3101 , &[_][]const u8{
2889 \\pub const CURLAUTH_BASIC_BUT_USHORT = (@import("std").meta.cast(c_ushort, 1));3102 \\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));3103 \\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));3104 \\pub const CURLAUTH_BASIC_BUT_ULONGLONG = @import("std").meta.cast(c_ulonglong, 1);
2892 });3105 });
28933106
2894 cases.add("macro conditional operator",3107 cases.add("macro conditional operator",
...@@ -2904,9 +3117,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2904,9 +3117,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2904 \\}3117 \\}
2905 , &[_][]const u8{3118 , &[_][]const u8{
2906 \\pub fn foo() callconv(.C) void {3119 \\pub fn foo() callconv(.C) void {
2907 \\ if (true) while (true) {3120 \\ if (true) {}
2908 \\ if (!false) break;
2909 \\ };
2910 \\}3121 \\}
2911 });3122 });
29123123
...@@ -2923,7 +3134,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2923,7 +3134,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2923 \\}3134 \\}
2924 });3135 });
29253136
2926 // TODO: detect to use different block labels here
2927 cases.add("nested assignment",3137 cases.add("nested assignment",
2928 \\int foo(int *p, int x) {3138 \\int foo(int *p, int x) {
2929 \\ return *p++ = x;3139 \\ return *p++ = x;
...@@ -3033,10 +3243,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -3033,10 +3243,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
3033 , &[_][]const u8{3243 , &[_][]const u8{
3034 \\pub export fn foo(arg_x: bool) bool {3244 \\pub export fn foo(arg_x: bool) bool {
3035 \\ var x = arg_x;3245 \\ var x = arg_x;
3036 \\ var a: bool = (@as(c_int, @boolToInt(x)) != @as(c_int, 1));3246 \\ 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));3247 \\ var b: bool = @as(c_int, @boolToInt(a)) != @as(c_int, 0);
3038 \\ var c: bool = @ptrToInt(foo) != 0;3248 \\ 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)));
3040 \\}3250 \\}
3041 });3251 });
30423252
...@@ -3106,8 +3316,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -3106,8 +3316,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
3106 \\#define DefaultScreen(dpy) (((_XPrivDisplay)(dpy))->default_screen)3316 \\#define DefaultScreen(dpy) (((_XPrivDisplay)(dpy))->default_screen)
3107 \\3317 \\
3108 , &[_][]const u8{3318 , &[_][]const u8{
3109 \\pub fn DefaultScreen(dpy: anytype) callconv(.Inline) @TypeOf((@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) {
3110 \\ return (@import("std").meta.cast(_XPrivDisplay, dpy)).*.default_screen;3320 \\ return @import("std").meta.cast(_XPrivDisplay, dpy).*.default_screen;
3111 \\}3321 \\}
3112 });3322 });
31133323
...@@ -3115,9 +3325,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -3115,9 +3325,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
3115 \\#define NULL ((void*)0)3325 \\#define NULL ((void*)0)
3116 \\#define FOO ((int)0x8000)3326 \\#define FOO ((int)0x8000)
3117 , &[_][]const u8{3327 , &[_][]const u8{
3118 \\pub const NULL = (@import("std").meta.cast(?*c_void, 0));3328 \\pub const NULL = @import("std").meta.cast(?*c_void, 0);
3119 ,3329 ,
3120 \\pub const FOO = (@import("std").meta.cast(c_int, 0x8000));3330 \\pub const FOO = @import("std").meta.cast(c_int, 0x8000);
3121 });3331 });
31223332
3123 if (std.Target.current.abi == .msvc) {3333 if (std.Target.current.abi == .msvc) {